Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal to outlaw std::endl #357

Closed
Fiona-J-W opened this issue Oct 24, 2015 · 47 comments
Closed

Proposal to outlaw std::endl #357

Fiona-J-W opened this issue Oct 24, 2015 · 47 comments
Assignees
Labels

Comments

@Fiona-J-W
Copy link

Writing a std::endl to a stream is exactly equivalent to writing a '\n', followed by a std::flush to a stream. In my experience very few people are aware of the later part and for some reason assume that it is instead a portable way to print a newline (which it technically is, but so is '\n'). Many of them are surprised, sometimes shocked, when they learn the truth, because they excessively use it in their codebase whenever they need a newline, thereby slowing down everything for no gain at all. (This is premature pessimization!)

Even for people who are aware of the truth this can be a disadvantage because they have to find out whether the flush is really necessary in the particular place, or whether they can replace multiple writes with one.

The following is similar to what I've seen in the wild:

std::cout << "foo" << std::endl
          << "bar" << std::endl << std::endl
          << "baz" << std::endl;

This can be replaced with

std::cout << "foo\n"
             "bar\n\n"
             "baz\n";

Which is just one write-call and additionally improves readability by reducing line-noise (this is even more true in editors that highlight escape-sequences).

For the few cases where flushing is really desired, std::flush works perfectly well and actually states the intent:

std::cout << "Some log-statement\n" << std::flush;
@RicoAntonioFelix
Copy link
Contributor

Or better yet, why not introduce a new manipulator instead of cluttering up the string

#include <iostream>

namespace std
{
    std::ostream&
    newl(std::ostream& cout)
    {
        return cout << "\n";
    }
}

int main(void)
{
    std::cout << "foo" << std::newl
              << "bar" << std::newl << std::newl
              << "baz" << std::newl;

    std::cout << "Some log-statement" << std::endl; //< Since we want to flush in this case
}

@Fiona-J-W
Copy link
Author

I strictly oppose that: There really is nothing wrong with a normal newline character and there is exactly no reason not to use that. I don't know what you consider cluttering about it, but the one thing that is cluttered (apart from being still slower than just the one write I argue for) is everything where manipulators are used for something perfectly normal like a newline.

@dosvidos
Copy link

I completely agree with this proposal. std::endl should be deprecated from now on.

@RicoAntonioFelix
Copy link
Contributor

The reasoning behind the manipulators is to separate formatting from content...

Joining formatting with content in a string is cluttering...

@Fiona-J-W
Copy link
Author

And since newlines are part of the content…

@villasv
Copy link
Contributor

villasv commented Oct 24, 2015

+1 for "premature pessimization". The reasoning is just, the benefits are there. Still, this sounds too radical to me (maybe I'm being resistant to change...)

Where that issue is indeed relevant to me (nowhere except competitive programming & small tools) I use a #define endl '\n' and do my flush whenever needed. About cluttering... I think that there are times when the newline is part of the content but there are times that it is not. It really depends on what you mean by content.

I'd propose a guideline to avoid using endl it if you don't need interaction. Outlawing it may be a little too much. The problem is the seemingly innocent name, not the use itself.

@cubbimew
Copy link
Member

Interaction doesn't usually require flushing, either, thanks to the cin/cout and cerr/cout ties.

@RicoAntonioFelix
Copy link
Contributor

In my reasoning I think that endl conveys its proper meaning...

It says that I am finished with this line of content and I will like to send it off to the output stream
(end line)...

It is totally different from saying I want to insert a new line at this point...

@villasv
Copy link
Contributor

villasv commented Oct 24, 2015

@cubbimew that's true, forgot about it because I usually disable it too. What were the reasons that endl was designed to flush? Maybe understanding why endl does more than initially expected to will eventually induce a resolution for this proposal.

@RicoAntonioFelix In my reasoning ending a line has nothing to do with my intentions of flushing or not. At least is not intuitive why there would be a relationship. I use endl everywhere, including with stringstream where flushing would have no meaning.

@khatharr
Copy link
Contributor

While I agree that endl isn't clearly named, I don't really want to lose it either because I do use it intentionally. (That is, I use '\n' for newline and endl for newline with flush.)

Maybe a guideline that detects endl within a stream instead of at the end, or looks for patterns indicating excessive endl use?

Addressing the core issue, I would suggest renaming endl, except that it seems fairly entrenched at this point. Maybe introducing a synonym would be an appropriate first step?

@RicoAntonioFelix
Copy link
Contributor

@villasv I have captured this reasoning by relating it to other concepts...

For instance, if you were typing and e-mail or an instant message or a comment as this one, at the end of all your typing, the only next step is to send it off to its recipient by hitting the send button (endmail | endim) or the comment button (endcom) which streams it off to its destination simply because you are at the end...

From this reasoning it can be related to endl where you are at the end of the line so the next step is to end the line and stream it off to its destination...

@Fiona-J-W
Copy link
Author

hat were the reasons that endl was designed to flush?

I am positive that the reasoning was like this:

  • We need to be able to flush the stream, so let's introduce std::flush
  • In almost all cases where we want to flush, we want to print a newline before that.
  • Let's introduce a helper that does both

And then everyone started using a flush-operation that also prints a newline whenever a newline was needed.

@RicoAntonioFelix
Copy link
Contributor

One must remember that the concept of a stream is a sequence of characters divided into lines where each line consist of zero or more characters followed by the newline character \n...

Hence when we think about endl we can capture the meaning of saying I am at the end of a line in the stream, send this line to its destination and expect more to follow (We are streaming data from a source to a destination)...

@khatharr
Copy link
Contributor

Huh? Where's that coming from?

@RicoAntonioFelix
Copy link
Contributor

@khatharr What is what coming from?

@khatharr
Copy link
Contributor

One must remember that the concept of a stream is a sequence of characters divided into lines where each line consist of zero or more characters followed by the newline character \n...

I've never heard it expressed that way before.

@RicoAntonioFelix
Copy link
Contributor

That definition was provided in Samuel P. Harbison. et al. 2002. C: A Reference Manual, 5th ed. NJ: Prentice-Hall on the first page in chapter fifteen...

Since C++ is a superset of C I presume the concept has remained the same but the interface has been improved...

@khatharr
Copy link
Contributor

I'll check it out. ty

@khatharr
Copy link
Contributor

The section you're quoting is discussing the difference between text and binary streams rather than attempting to define a paradigm for streams to observe. The significant point about the newline character in the passage is that text streams employ it to break lines. There's no discussion of flushing buffers along with this.

There are two general forms of streams: text and binary. A text stream consists of a sequence of characters divided into lines; each line consisting of zero or more characters followed by (and including) a newline character, '\n'.

It's also specifically discussing C, as indicated by discussion about CRT implementation requirements which immediately follows that. C stdio is not the basis of C++ streams, though they overlap conceptually in places. It's worth noting, for example, that cstdio does not have a std::endl analog.

In the case of std::ostream (where std::endl is relevant), it's very wasteful to use std::endl at the end of every line, which is exactly why Florianjw raised this issue. You used the example of an email earlier, but what you're suggesting is not analogous to sending the full email. It's analogous to sending one message via several emails, with each mail containing one line of text.

Actually, mulling this over a bit more, I'm coming to agree with Florianjw. It's really more consistent to just use "\n" << std::flush rather than omitting the last "\n" and using std::endl, and it prevents the problem of overuse.

@MichaelCook
Copy link

It used to be in C++ that the type of 'x' was int, and so cout<<'\n' would
print 10. I suspect that influenced the introduction of endl...

On Sun, Oct 25, 2015 at 12:37 AM, Khatharr notifications@github.com wrote:

The section you're quoting is discussing the difference between text and
binary streams rather than attempting to define a paradigm for streams to
observe. The significant point about the newline character in the passage
is that text streams employ it to break lines. There's no discussion of
flushing buffers along with this.

There are two general forms of streams: text and binary. A text stream
consists of a sequence of characters divided into lines; each line consist
of zero or more characters followed by (and including) a newline character,
'\n'.

It's also specifically discussing C, as indicated by discussion about CRT
implementation requirements which immediately follows that. C stdio is not
the basis of C++ streams, though they overlap conceptually in places. In
the case of std::ostream (where std::endl is relevant), it's very wasteful
to use std::endl at the end of every line, which is exactly why Florianjw
raised this issue.

You used the example of an email earlier, but what you're suggesting is
not analogous to sending the full email. It's analogous to sending one
message via several emails, with each mail containing one line of text.


Reply to this email directly or view it on GitHub
#357 (comment)
.

@khatharr
Copy link
Contributor

That may explain the infuriating behavior of streams when dealing with chars, but I don't think it would really explain endl, since the correct thing to do in that case would be to learn the difference between single and double quotes, and also because endl flushes the stream.

@RicoAntonioFelix
Copy link
Contributor

Maybe this link might add some merit to this discussion...

Pointing out something that's relevant, it states:

In many implementations, standard output is line-buffered, and writing '\n' causes a flush anyway, unless std::cout.sync_with_stdio(false) was executed. In those situations, unnecessary endl only degrades the performance of file output, not standard output.

This captures the essence of what I was trying to point out from the quote in the book...

If such is the case, then @Florianjw's code example deems irrelevant to the point she is trying to make...

@khatharr
Copy link
Contributor

endl relates to ostream, not to cout. C++ is not responsible for implementation behavior..

@RicoAntonioFelix
Copy link
Contributor

std::cout is an object of the specialization std::ostream therefore std::endl relates to both...

@khatharr
Copy link
Contributor

@RicoAntonioFelix
Copy link
Contributor

Even if you are trying to base your argument on this principle, everything still has context and the context of dealing with a storage device (files) is different from the context of dealing with a character output device...

In my view one should understand the differences in context at any particular point and use the appropriate construct...

@khatharr
Copy link
Contributor

Oh my god, Rico... -.-

@RicoAntonioFelix
Copy link
Contributor

I'm just being deliberately critical and thoroughly analysing this proposal...

At the end of all this, whatever the community decides will always override one person's view...

@khatharr
Copy link
Contributor

My fault. I'm frustrated because I'm not communicating clearly. Sorry.

@villasv
Copy link
Contributor

villasv commented Oct 27, 2015

Still, although it makes sense for ostream, flushing at endl is not an inherent concept of streams. As I said before, I use endl with stringstreams and they are not associated with devices.

You can see it either ways. Stringstream has a "dumbed down" behavior because it's a "fake" output stream... or device output has extra functionality because it normally has expected behavior of flush when feeding newlines.

I can agree with both lines of reasoning. What I find important in this proposal is highlight the fact that endl might be a misleading innocent name and would be interesting to detect overuse (not sure about outlawing).

@BjarneStroustrup
Copy link
Contributor

I added SL.50: Avoid endl. I have been following that rule myself for about a decade.

Mironenko referenced this issue in AnastasiaVern/LR2-Classes- Mar 2, 2016
williamspatrick pushed a commit to openbmc/phosphor-hwmon that referenced this issue Jan 18, 2017
isocpp/CppCoreGuidelines#357

Change-Id: Iebfb13e4e03859e66811b6a6c9a3fe9d1b8f85a5
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
@alf-p-steinbach
Copy link

One doesn't use iostreams for efficiency.

Using endl has two advantages:

  • It flushes. That means that if e.g. the program crashes, one has output to look at.

  • It's visually distinctive.

Avoiding endl has no advantage that I know of, except silly terseness. Which is an anti-pattern. I do know that it's popular to be against endl and for ultimate terseness, but that's a crowd view, much like religious views, or e.g. whatever ideas get a majority of voters to vote for the least reasonable choice.

@galik
Copy link
Contributor

galik commented Aug 4, 2017

@alf-p-steinbach One doesn't use iostreams for efficiency.

Why not? In my tests iostreams are slightly faster than scanf/printf and, what else are you going to use? Flushing the buffer every single line can considerably degrade performance in some situations.

@alf-p-steinbach
Copy link

alf-p-steinbach commented Aug 4, 2017

@galic: It took some googling to find some test results. As far as I remember the committee didn't include numbers about this in their performance report, and anyway that's very old. (https://cristianadam.eu/20160410/c-plus-plus-i-slash-o-benchmark/) is from 2016, scroll to bottom for conclusion.

You're right about considerably degrade, but the some situations where that matters are rare. Just say no to premature optimization. It comes at high cost.

@galik
Copy link
Contributor

galik commented Aug 4, 2017

@alf-p-steinbach When I run the test you linked I still get iostreams coming out a shade faster.The linked results seem to be more of a Visual C++ problem rather than iostream. I am not able to test Visual C++ here. The strange thing about the test results published there is that the test is for copying binary files, not formatted i/o. I would have thought those operations would be largely governed by the disk performance. How the library writers managed to get such bad results is a mystery to me. It is almost like every buffer is being copied (several times) before being flushed? Maybe by now the MS library is fixed.

Regarding premature optimization I agree. But I don't think that means we should write gratuitously inefficient code either. After all, buffering is an optimization. Why work to defeat the optimizations that are already in place? It costs nothing to state only what you want to do (which is to issue a new line). I am pretty sure it is rare for code to need to flush at the same time.

@phillipjohnston
Copy link

phillipjohnston commented Aug 4, 2017 via email

@phillipjohnston
Copy link

But perhaps ye ol' printf is just what embedded folks are meant to be stuck with :)

@jwakely
Copy link
Contributor

jwakely commented Aug 4, 2017

I can't understand how this can generate so much discussion. If you want a newline, insert a newline. If you want a newline and a flush, insert endl. What matters is understanding that they're not the same thing, but forbidding or insisting on one or the other seems wrong to me. Understand what endl does, and use appropriately.

@Cleroth
Copy link
Contributor

Cleroth commented Aug 6, 2017

"Understand what endl does and use it appropriately."
But this is the kind of thing that makes learning C++ take even longer. The function does not really hint at anything that it flushes. Most C++ amateurs simply don't know much about IO buffering.

@BrianSipple
Copy link

I'm coming to this issue after several months of heavily studying and learning C++ and using endl just about everywhere because I genuinely thought it was a simple replacement for \n.

So there's one data point 😃.

As a developer, I totally understand the mentality of "understand what endl does and use it appropriately" (which is why I'm here now 😂). But from the perspective of language implementation, I would think it wise to be wary of devices that set newcomers up for failure. As a function whose name offers no hint of the flush that it ultimately performs, endl, at least for me, has been exactly that.

@thecoshman
Copy link

I think @BrianSipple puts a very good reason for why std::endl should be discouraged. It's not doing what people think it is doing. I don't see why people are not encouraged to just think about when they need to use std::flush.

@MikeGitb
Copy link

MikeGitb commented Oct 7, 2017

The guideline already exists for two years now - what is your point?

@xparq
Copy link

xparq commented Sep 30, 2023

Just FTR: if only it had been called sendl... That could've mitigated the confusion.

@Cleroth
Copy link
Contributor

Cleroth commented Oct 1, 2023

Did you really just necro a 6-year-old thread to post a hypothetical scenario that is never going to happen with a subpar solution

@xparq
Copy link

xparq commented Oct 2, 2023

@Cleroth Did you really just get triggered by that? :-o

This is not a gaming forum. This is not a social media thread for organizing family events. There are no weird rules to forbid that either.
People google things, and find things, and may have thoughts that they may even find worth adding. Does it really hurt that much to you? Sorry about assuming a more welcoming attitude by default.

FTR, these issue conversations are knowledge repositories, i.e. a timeless public space for exchanging and recording ideas.

Too bad you spammed it with your irritated policing comment, and kinda forced me to follow it up, too, resulting in two more notifications.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests