Stop Using Std::endl [video]
articles.emptycrate.com
articles.emptycrate.com
If a long-running program crashes, a missing flush can be the difference between seeing or not seeing an important detail that preceded the crash. Instead of going straight to the cause, you might be wasting time exploring something that happened a bit earlier, or you may be forced to rerun the long program again to see the same issue with better logging.
Then there’s the inconsistency aspect; if you become used to "\n", and see it “work” all the time for streams that happen to go to the terminal, you will be confused when “the same” thing does not have the same behavior for a file stream. Use the abstraction ("endl") until you know you need it to be different.
Systems programming languages aren't about easy troubleshooting. C/C++ APIs should be performant by default because when they are not we end up working around them, often badly. If I want the stream flushed I'll configure it to behave as such or I'll call flush explicitly. Any other mindset belongs in Python, Java or some other higher level language.
[1] https://github.com/isocpp/CppCoreGuidelines/issues/357#issue...
If the main purpose of a program is to generate output then it ought to know what it’s doing anyway and know how to avoid poor performance. For most programs, I’d say that Unix rules of thumb apply: if you are already trying to be “silent on success”, your program will either print nothing or maybe an error or two and not have to worry about print performance. If your program might only print one error and that doesn’t even show up because a flush didn’t happen before a crash, there is a big problem.
This is a bit unfair to HLLs, IMO. Flushing to disk can add unacceptable overhead even for, say, Python. (Particularly on spinning rust, but...).
The short and long of it is that "endl" should probably be deprecated in the standard and a good replacement created.
("std::newline"? Maybe we could just specify it as "\n" and get rid of the whole text/binary distinction that another poster on this topic was rightly complaining about.)
My addition (which might have been in the video, didn't watch it all): Don't worry about \r\n vs \n, if you're on windows outputting \n will convert to \r\n, as long as you opened the file in text mode.
The sad part is it's not even just Notepad. It's the underlying "EDIT" HWND class itself. So anyone coding in raw Win32 APIs has to basically capture paste events from the clipboard to transform text into the "\r\n" state, and upon reading text out of it, potentially convert it back to "\n".
Probably a five-line patch to that class would eliminate so much extra work for developers. And we could drop the whole ridiculous "text mode" for working with files while we were at it.
Plus I'm sure there must have been thousands upon thousands of external requests to Microsoft to be able to use Notepad with all their non-Notepad text/configuration files over the years.
It's hard to believe this is anything but very deliberate at this point. Add to this, the mess Microsoft perpetuates with not supporting UTF-8 (std::wstring, wchar_t, _wfopen, non-standard iostream extensions for Unicode filenames, etc), and one really begins to have a deep seething hatred for Microsoft.
That's it. Notepad is only a wrapper over a text edit. Nothing more.
Whenever it comes to something programming-related that might improve code portability between different OSes (C99, LF, UTF-8, etc), there's a mountain of after-the-fact justifications for why it would break backward compatibility and other such tripe.
But honestly, what would making "EDIT" accept "\n" alone do to break backward-compatibility? "\r\n" would still work, of course. Is there a group of people inserting stray "\n"s alone into text files, and expecting Notepad to silently ignore them and dump all of that text onto the same line? If so, those people are absolutely horrible :P
But okay, fine. Require a program .Manifest flag to opt-in, then another special ES_ACCEPT_LF flag with a caveat that it's Windows 10+.
Even all the people that have jumped through hoops with things like hooking paste operations to transform "\n" into "\r\n" for "EDIT" ... that would still work just fine, it would just be unnecessary.
The only cost would be that there's a short time where people are trying to use these "\n" only text files on older versions of Windows. But we've been dealing with the fallout of that anyway for the past 21+ years now.
You can't possibly tell me that supporting "\n" alone is an immense burden for a company with as much money and developers as Microsoft. No, I'm sorry, but this, C99, UTF-8, things like _wfopen, WSAPoll over poll, etc are all very much intentional: they're designed to maximize vendor lock in. Microsoft doesn't want your code easily moving between Windows and Linux. Even though they're a convicted monopoly with a mountain of evidence of their abusive actions, they still have so many white knights ready to defend their actions as benign =(
CMD.EXE is -sadly- a bucket of suck.
> ...WSAPoll over poll...
Don't fucking get me started about the WinSock API. >:(
(I don't buy the "decades" bit, but I'll accept it just for argument's sake.)
fprintf(fd, "Number: %.3f\n", number)
Is a whole lot easier than
std::streamsize ss = std::cout.precision(); std::cout << "Number: " << std::setprecision(3) << number << std::setprecision(ss) << std::endl;
Since setting precision is modal, you have to set it back if you don't want to screw up future output.
Streams suck!
Also take a look at cppformat for some of the best of both worlds.
When you're doing internationalization, a common thing you might do is something along these lines:
fprintf(fd, translate_string(context, "Result %d of %d"), num1, num2);
The translation may be "%d of %d results" in another language. It's really difficult to map that into streams.
Yes, I'm over-simplifying, since you can't have arbitrary placeholders due to order differences between languages, but it's _much_ harder to write simple code with streams than libc, and I stand by that!
As for user-defined data, I've added .toString() functions on objects which I can call whenever needed, either from the ostream operator or pass the result to libc.
Logging (std::clog) on the other hand is optimized for throughput since you expect to use some of that even in the happy path and don't want to bog everything down. I believe that since they're both associated with stderr, the error handling routine will also flush the last of the log.
Of course if you're debugging you want to flush ASAP.
However, sometimes endl is indeed what you want. There is nothing to "fix" here, except maybe your attitude.
This violated the principal of least surprise enough that someone made a 10 minute long video on it :) It's really hard to blame the user this time.
It was weird to ever merge two unrelated functions into something, and then name it something as cryptic as "endl". Especially when the most common case novices would encounter this would be std::cout, where terminals will automatically flush text to the screen upon receiving "\n" anyway.
// ef -> std::eol_flush
template class <typename X, typename Y> basic_ostream<X, Y> &ef(basic_ostream <X, Y> &os)
{
return std::eol_flush(&os);
}
A four-letter name could have been found with better mnemonic value covering the full semantics. We can look toward assembly languages for inspiration: they pack a lot into one, two, three or four letters. For instance std::nlfl (add NewLine and FLush).It's been years since I used C++ iostreams. I totally forgot that endl also flushes. When I saw this submission, my reaction was: what is this fuss about. Then "oh, right! That endl manipulator flushes, even on a fully buffered stream!". The name didn't remind me of this at all; but "nlfl" would almost certainly have served that purpose.
And why do you think its name (std::endl) doesn't match its effect (ends the line)?
You always want to follow the Principle of Least Astonishment. Suppose a hypothetical programming language has two DSLs for interfacing with SQL databases. Which of these is immediately understandable without having to consult the docs? Which one are you more likely to "learn" in a way that litters your codebase with subtle bugs?
// Returns the SQL query as a string
DB.table("users").where("id").in($args).toSQL()
// Returns the results of the query
DB.table("users").where("id").in($args)
// Locks the DB connection and returns the results of the query
DB.conn.lock() { DB.table("users").where("id").in($args) }
vs. // Returns the SQL query as a string
DB.query.find("users", ["id", sqlSafe($args)])
// Returns the results of the query
DB.query.find("users", ["id", sqlSafe($args)]).run()
// Locks the DB connection and returns the results of the query
DB.query.find("users", ["id", sqlSafe($args)]).exec()If it was called std::newline I would agree with you about it being misnamed. But it isn't called that.
Could the name be better? Maybe. Bike shedding.
Besides the other suggestions, I'd say something like std::eof_nl would make that interpretation a lot more obvious. Although like others I'd question the need for it at all as opposed to just an explicit "\n" << std::flush .
>Bike shedding.
I don't think anything is "bike shedding" when it comes to defining the standard library of a programming language (especially one for which all updates to the standard must maintain 100% backwards compatibility).
(Not) choosing descriptive identifiers might not have the glamour of defining semantics or optimizing performance, but it can have huge real-world implications.
The (std::endl == "\n") misconception is enormously widespread even amongst experienced C++ people, and is one of the most common sources of performance issues with the language.
> [std::endl] is one of the most common sources of performance issues with the language.
Citation needed.
The main thrust of that article [1] was suggesting a new way to use the existing class and pointing out common mistakes he'd seen. Yeah, the docs were slightly outdated and needed a little TLC. They didn't mention the alternatives that had recently been made possible, but that's hardly an earth-shattering mistake.