5 ms·
Genuinely surprised and confused by “print” and “println”
by jonathankoren 3y ago
Genuinely surprised and confused by “print” and “println”
- CoastalCoder 3y ago> Genuinely surprised and confused by “print” and “println” I was too. Looking at [0], I'm guessing it's meant to bring these benefits over old-school "printf": - Leverages the type system to avoid mismatches between the format string and the arguments. - Adds Python- / Rust-style support for position-based substitutions, e.g. "{1}". - Like Rust (I think; I'm still learning Rust) you can specify per-type formatters. But I'm still confused about: - How this relates to the standard library's iostream system. It seems pretty redundant. - Why "println" exists. [0] https://en.cppreference.com/w/cpp/io/print https://en.cppreference.com/w/cpp/io/print
- dxuh 3y agoiostreams are horrible to use, especially if you want to format your output and they blow up compilation to a mythical degree. People go to great lengths to avoid them and I agree with them. print/println are based on the fmt library (https://github.com/fmtlib/fmt https://github.com/fmtlib/fmt), which had it's first release in 2015, so it's roughly as old as Rust afaik. It's mainly inspired by Python formatting. Having per-type formatters is just a logical conclusion of having type-safe formatting. iostreams are for all kinds of io (which includes stdin/stdout), while fmt is entirely dedicated to formatting strings. Those things are related, but not the same. cout and cerr and such will probably be entirely superseeded by print/println (I hope), but it doesn't make iostreams generally redundant. println adds a newline and you want to be able to choose, so there is print and println.
- Karliss 3y agoMost of those benefits apply to std::format which was already introduced in c++20. But formatting a string you will often want to output it somewhere. You could do `std::cerr << std::format(....)`, but that just invites weird mixes of std::format and iostream based formatting. I look at print/println as partially just convenience function combing the existing std::format functionality with outputing to something. Not sure if standard permits it but print could also allow skipping some temporary heap allocated std::string which the std::format returns by directly writing to the output stream buffer. In C++20 if you wanted to print using std::format style formating (or it's variant) your options where: ``` std::cout << std::format("{}{}", arg1, arg2);// not the best syntax but less bad than everything else, slightly inefficient due to temp string std::string tmp = std::format("{} {}", arg1, arg2); fwrite(stdout, tmp.c_str(), tmp.length()); // more ugly, and still uses temporary string std::format_to(std::ostream_iterator<char>(std::cout), "{} {}", arg1, arg2); // avoids temp string, but what kind of monstrosity is this ``` But doesn't the std::format style formatting make the formatting part of ostream redundant -> it somewhat does. I guess that's why there are 3 types of print overloads: * accepting ostream as first argument * accepting c style "FILE*" as first output argument * no output stream argument, implicitly outputting to stdout One potential consideration to use ostream based versions instead of FILE* ones even though the formatting part is somewhat redundant, is RAII based resource management. If you want to use FILE* based versions, it means that you have to either remember manually close the File* handle which just like manual new/delete or malloc/free calls is something modern C++ tries to move away, or you have to create your own RAII wrappers around c style FILE* handles. An alternative would have been introducing new kind of output stream API which only concerns with outputing buffered stream of bytes and skips the formatting part of ostream, but that would have introduced different kind of redundancy -> having 3 kinds of API for opening and writing to file. Allowing to pass ostream to print, while discouraging use of << operator parts of it seems like a simpler solution. One more concern is how using the version of print which outputs to stdout without taking output File or ostream handle interacts with any buffering done by previous printf,and std::cout APIs and also synchronization with input streams. The same problem already existed before for interactions between printf and std::cout. For the most part if you don't mix them it doesn't matter too much, but if you care the standard defines how exactly they are synchronized. The new print probably either adds third case to this or is defined as being equivalent to one of the previous two. After looking reading docs a bit more, seems like std::basic_streambuf/std::file_buf did exist. The new print API, might have used that as abstraction around output streams without some of the ostream formatting baggage. I have only seen them mentioned as implementation details of ostream stuff, never seen anyone use them directly. There was also std::format_to in c++20 which avoid the temporary string std::format returns. I guess they could have extended that with additional overloads so that it can function more similar to std::print. But if they need to define new functions might as well call them something more familar to people coming from other languages like print, instead of having format_to(stdout, "formatstring", arg1, arg2);. Currently std::format_to outputs to output iterator. So to sumarize why print exists: - combine std::format introduced by C++20 with outputting somewhere with cleaner syntax compared to doing it manually. - sane looking hello world for beginner and people coming from other programming languages, this is probably also why println exists - (maybe) avoid some heap allocations that temporary string returned by std::format would cause. - avoid confusion of mixing two formatting approaches that `std::cout<<std::format()` implies - avoid C style manual resource management that would be required for doing `File\* f=fopen("");auto tmp=std::format();write(f, tmp.c_str())`
- dxuh 3y agoWhat's surprising to you about this? I think they are generally regarded as very welcome additions.
- 1024core 3y ago> Genuinely surprised and confused by “print” and “println” Just like Pascal/Modula-2, except 30 years later :-D
- bluedino 3y agoWasn't Pascal WriteLn and Java was Println?
- aodonnell2536 3y agoJava has the beloved and hilariously verbose System.out.println
- 1024core 3y agoYou could be correct, I have but a very vague recollection of Pascal/Modula2... :-) Now I still miss my favorite language, FP...
- mikewarot 3y ago>Just like Pascal/Modula-2, except 30 years later :-D Now if they could fix the CaSe SeNSiTivity bug, get rid of macros, get some better strings, and maybe use @ to get an address, and ^ to point to things.... they'd be on to something. Especially getting rid of macros, I hate them. Most C code looks like line noise, maybe they could then use something like Begin/End to denote blocks instead of abusing comment markers {} ;-)
- ksherlock 3y agoTake your pick: print("{:#04x}\n", 0xbeef); - or - cout << std::setw(4) << std::setfill('0') << std::hex << 0xbeef << std::endl;
- jordigh 3y agoYeah, they make sense, just surprising that C++ is finally tacitly admitting that iostreams were a mistake. I kind of wonder why anyone thought iostreams would be a good idea to begin with. I don't think anyone but C++ ever created a comparable interface.
- q845712 3y agoI could be wrong as I was young and not yet in the field, but my impression has always been that sometime in the 80s/90s as the whole "networking, world wide web, wowie!" moment happened, there was this idea that "maybe on a local computer everything is files, but on the network everything is streams. Hey, maybe everything is streams!?" and C++ just happened to be creating itself in that zeitgeist, trying to look modern and future-thinking, so somebody decided to see what would happen if all the i/o was "stream-native". IDK, it'll probably make more sense in another 15 years as we clear away the cruft of all the things that tried to bring "cloud native" paradigms into a space where they didn't really fit...
- GuB-42 3y agoI think it is more simple and technical that that. The big thing is that they wanted type safe IO. Not like printf where you can print an integer with %s and the compiler won't have a problem with that, and it will crash. Reusing bit shift operators for IO is quite clever actually. If you have operator overloading, you have type safe IO for free. Remember C++ came out in the 80s as a superset of C, these technical considerations mattered. std::println doesn't look like much, but it actually involves quite significant metaprogramming magic to work as intended, which is why it took so long to appear.
- tialaramex 3y ago