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
Florianjw opened this Issue Oct 24, 2015 · 44 comments

Comments

Projects
None yet
@Florianjw
Copy link

Florianjw commented Oct 24, 2015

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

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 24, 2015

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
}
@Florianjw

This comment has been minimized.

Copy link
Author

Florianjw commented Oct 24, 2015

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

This comment has been minimized.

Copy link

dosvidos commented Oct 24, 2015

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

@RicoAntonioFelix

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 24, 2015

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

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

@Florianjw

This comment has been minimized.

Copy link
Author

Florianjw commented Oct 24, 2015

And since newlines are part of the content…

@villasv

This comment has been minimized.

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

This comment has been minimized.

Copy link
Member

cubbimew commented Oct 24, 2015

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

@RicoAntonioFelix

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 24, 2015

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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Contributor

khatharr commented Oct 24, 2015

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

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 24, 2015

@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...

@Florianjw

This comment has been minimized.

Copy link
Author

Florianjw commented Oct 25, 2015

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

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 25, 2015

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

This comment has been minimized.

Copy link
Contributor

khatharr commented Oct 25, 2015

Huh? Where's that coming from?

@RicoAntonioFelix

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 25, 2015

@khatharr What is what coming from?

@khatharr

This comment has been minimized.

Copy link
Contributor

khatharr commented Oct 25, 2015

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

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 25, 2015

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

This comment has been minimized.

Copy link
Contributor

khatharr commented Oct 25, 2015

I'll check it out. ty

@khatharr

This comment has been minimized.

Copy link
Contributor

khatharr commented Oct 25, 2015

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

This comment has been minimized.

Copy link

MichaelCook commented Oct 25, 2015

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

This comment has been minimized.

Copy link
Contributor

khatharr commented Oct 25, 2015

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

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 26, 2015

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

This comment has been minimized.

Copy link
Contributor

khatharr commented Oct 26, 2015

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

@RicoAntonioFelix

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 26, 2015

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

@khatharr

This comment has been minimized.

@RicoAntonioFelix

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 26, 2015

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

This comment has been minimized.

Copy link
Contributor

khatharr commented Oct 26, 2015

Oh my god, Rico... -.-

@RicoAntonioFelix

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 26, 2015

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

This comment has been minimized.

Copy link
Contributor

khatharr commented Oct 26, 2015

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

@villasv

This comment has been minimized.

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).

@thecoshman

This comment has been minimized.

Copy link

thecoshman commented Oct 29, 2015

std::flush will flush the stream, std::endl will write something that represent an end of line and flush. I think the fact that std::endl is concise in what it does is good enough grounds to consider it's use bad. It is not obvious what it is doing, and it is not hard to use alternative forms.

If you consider this argument in reverse, let's say there is no std::endl and someone is proposing this behaviour, I bet the first thing most people would say is "Why is it also doing flush?"

@RicoAntonioFelix

This comment has been minimized.

Copy link
Contributor

RicoAntonioFelix commented Oct 29, 2015

Seeing that the majority of participants in this conversation share a similar view in contrast to my opposing view, I suppose the majority of the community will also side with the greater force...

Now the final decision its left up to the council....

@BjarneStroustrup

This comment has been minimized.

Copy link
Contributor

BjarneStroustrup commented Nov 3, 2015

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

bradbishop added a commit to bradbishop/phosphor-hwmon that referenced this issue Jan 12, 2017

Remove use of std::endl
isocpp/CppCoreGuidelines#357

Change-Id: Iebfb13e4e03859e66811b6a6c9a3fe9d1b8f85a5
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>

williamspatrick pushed a commit to openbmc/phosphor-hwmon that referenced this issue Jan 18, 2017

Remove use of std::endl
isocpp/CppCoreGuidelines#357

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

This comment has been minimized.

Copy link

alf-p-steinbach commented Aug 4, 2017

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Copy link

phillipjohnston commented Aug 4, 2017

@phillipjohnston

This comment has been minimized.

Copy link

phillipjohnston commented Aug 4, 2017

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

@jwakely

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Copy link

BrianSipple commented Sep 13, 2017

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

This comment has been minimized.

Copy link

thecoshman commented Oct 7, 2017

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

This comment has been minimized.

Copy link

MikeGitb commented Oct 7, 2017

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

acgetchell added a commit to acgetchell/CDT-plusplus that referenced this issue Nov 17, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment