putting an object as condition in while statement

Pages: 12
zapshe wrote:
it's useful in that it tells you when you've reached the end of the file

No that's not what it does, as noted earlier in this thread.

Let's update lastchance's example to show what failing to check the success of getline leads to:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;

int main()
{
// ifstream in( "data.txt" );
   stringstream in( "zap\n"
                    "she\n" );

   string line;
   while( !in.eof() )
   {
      std::getline(in, line);
      cout << line << "of size " << line.size() << '\n';
   }
   
   cout << '\n';
   
   stringstream in2( "last\n"
                     "chance\n" );
   while(std::getline(in2, line))
   {   
      cout << line << "of size " << line.size() << '\n';
   }
}

zapof size 3
sheof size 3
of size 0

lastof size 4
chanceof size 6


Your rare corner case where it made the appearance of "working" was likely because the last line was incomplete (no delimiter), and so getline reported the failure by setting eof while also recording the (incomplete, from getline's point of view) line, which happened to be exactly what you wanted from it. That failure of the previous iteration's getline is what you checked for by writing "while(!file.eof())".
Last edited on
that there's nothing wrong with using eof() when you know what you're doing

(Emphasis mine.)
That's true! The problem is that many people often don't know what they're doing, and most people who browse this forum fall into that category (and often aren't exactly thorough).

That said (and this is important), expecting plain text input to not be terminated by a newline is inherently fragile behavior. It's pretty easy to end up with trailing whitespaces in a text file just from careless editing, and convention (at least on POSIX systems, where the standard allows programs to expect certain things of their text files) is to end such files with a newline. Some VCSes (Git) will issue warnings about text files not ended with newlines.

The issue here isn't so much that you're calling eof(). It's useful if you need to check if an operation hit the end of file while reading, which there are definitely valid use cases for (like preventing your program from trying a different approach if failbit gets set). The issue is that you are using eof() instead of a check for read failure. It is not a suitable replacement, not even if all you're using is std::getline.

-Albatross
Last edited on
That's true!

*Gets Teary Eyed* Thank you lord and savior!

expecting plain text input to not be terminated by a newline is inherently fragile behavior.

I've used it many times with files ending in newlines, it's pretty expected. It's only an issue if the loop isn't planned out and ends up redoing everything with the old data.

The issue is that you are using eof() instead of a check for read failure.

Something to be done separately.

No that's not what it does, as noted earlier in this thread... Let's update lastchance's example to show what failing to check the success of getline leads to:

Your example didn't prove anything, except maybe that you're lazy to add one extra line to make it work:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using namespace std;

int main()
{
	stringstream in("zap\nshe\n");
       //Heck, you could even use this one:
       //stringstream in("zap\n\n\nshe\n\n\n\n");

	string line;
	while (!in.eof())
	{
		std::getline(in, line);
		cout << line << " of size " << line.size() << '\n';
		in >> std::ws;
	}

	cout << '\n';

	stringstream in2("last\n" "chance\n");
	while (in2 >> line)
	{
		cout << line << " of size " << line.size() << '\n';
	}
}


zap of size 3
she of size 3

last of size 4
chance of size 6
Last edited on
And YET you still do not check for failure. Moreover, the addition of line 14 is not only a hack to get eofbit set, but it means that lines beginning with whitespace get that whitespace discarded. Your updated example behaves differently from the original example in a fundamental way. In fact, it demonstrates behavior that would be undesirable for a program that reads lines and returns those lines' contents faithfully.

I don't even get why you've chosen this particular hill to metaphorically die on, and have opted to respond with sarcasm and veiled insults to people who take issue with you propagating bad advice defending a hazardous practice with few (if any) measurable benefits.

-Albatross
Last edited on
And YET you still do not check for failure.

My bad, I'll be sure to write a complete program, a .txt for us to look at, and then a 10 page essay explaining my incompetence. I mean, for God's sake, you could literally just put an if statement in front of the getline and an else afterwards to handle the error. In fact, this is probably preferable if you know the kind of error you might encounter and DON'T want your loop to end because of a tiny hiccup.

Moreover, the addition of line 14 is not only a hack to get eofbit set

You were supposed to be the savior. I liked that though, a hack. *Put on Thug Life Music and Glasses*.

but it means that lines beginning with whitespace get that whitespace discarded

That was gonna happen anyway when you use cin or getline. Every program will be different. There's no reason to bash on this when it can be implemented a number of ways depending on the situation.

In fact, it demonstrates undesirable behavior for a program that reads lines and returns those lines contents' faithfully.

If that's what we wanted, we wouldn't use getline in the condition statement at all:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using namespace std;

int main()
{
	stringstream in("zap\n\n\nshe\n\n\n\n");

	string line;
	while (!in.eof())
	{
		std::getline(in, line);
		cout << line;
	}

	cout << '\n';

	stringstream in2("last\n\n\nchance\n\n\n\n");
	while (getline(in2, line))
	{
		cout << line;
	}
}


zapshe
lastchance


Either way, you're getting rid of the white space, so i don't know what the point you're making is.


EDIT: An even better "hack" would be replacing the "in >> std::ws" with "in.peek()". Not that the first solution was doing anything unwanted, but if you're really sad about the whitespace *shrug*.
Last edited on
and have opted to respond with sarcasm and veiled insults to people who take issue with you propagating bad advice defending a hazardous practice with few (if any) measurable benefits.

You yourself agreed it's not bad if you know what you're doing. It's not hazardous. I'm not telling people to walk into a Chernobyl, I'm saying eof() isn't some evil devil to avoid at all costs. I usually use getline or ">>" as the condition, but eof() has been a convenient tool as well.

My sarcasm comes from the fact that I was already pissed off when I came into the conversation.
Apologies, I didn't mean to contribute to your being pissed off.

Just because something is not bad if you know what you're doing doesn't make it not hazardous. As an example, a chainsaw is an insanely useful tool and not inherently bad, but it's also hazardous in that you can gravely injure yourself. As for code examples, here are a few:
* goto can be used to create very intricate and comparatively efficient flow control (also remains the cleanest way to break out of deeply-nested loops), but misuse can also make code a ball of spaghetti as cooked by a chef in Gordium.
* Putting single statements on a new line after an if(condition) reduces line length compared to putting it on a new line and line count compared to wrapping them in traditional braces, but also makes your code prone to problems like this: https://nakedsecurity.sophos.com/2014/02/24/anatomy-of-a-goto-fail-apples-ssl-bug-explained-plus-an-unofficial-patch/
* Using input without checking whether said input is valid is convenient (and in some cases much more performant), but in the worst case can lead to security issues (like SQL injection).

As it turns out, that "if you know what you're doing" bit is a massive caveat, even for experienced programmers. People make mistakes, wherein they occasionally forget where they're doing. A more defensive approach taken from the get-go can potentially save them from embarrassment (or worse). Consequently, people who teach/mentor programmers generally (should) have an interest in promoting ways of doing things that aren't hazardous, especially to a beginner. Relatedly, that also means disrecommending solutions that are more hazardous/fragile unless they have some property that the person they're being recommended to absolutely needs (and even that will have disclaimers).

I complain about this particular way of using eof() not only because it's part of a known beginner anti-pattern, but because you posted several examples that demonstrate exactly why it's a risky idea. You provided input for them that works obviously, but on slightly different input, they demonstrate non-obvious and potentially-unwanted behavior. It's fine if you intended it, but a) you didn't document it, and b) this is a beginner thread (despite the subforum it's posted in), where people using posted code as an example to refer to is a comment occurrence.

Sorry, but while I agree eof() isn't some devil to avoid at all costs, this simply isn't a good use of it.

-Albatross

P.S. - std::getline, unlike most stream extraction operators, doesn't strip leading whitespace. It does discard the delimiter, which is a newline by default, but that's about it.
Last edited on
Apologies, I didn't mean to contribute to your being pissed off.

Can't tell if you were being sarcastic, but you have nothing to apologize for.

As an example, a chainsaw is an insanely useful tool and not inherently bad, but it's also hazardous in that you can gravely injure yourself.

That's inherently true with everything. A rock, a spoon, your teeth, etc.. But if we stay away from these things because they have the potential of hurting us, we wouldn't get anywhere. Obviously the only difference would be the magnitude. You could hurt yourself far more with a chainsaw that the other things, which is why you should be more careful with it.

Unlike with our physical actions (where we can be careful and still screw up), with coding, you can test against a few possible use cases to make sure that the issue never presents itself.


As it turns out, that "if you know what you're doing" bit is a massive caveat, even for experienced programmers. People make mistakes, wherein they occasionally forget where they're doing.

Again, not that I disagree, but this isn't reason to avoid eof(). You break your program 1000% more easily with arrays and vectors. The point is to use them correctly. If you don't, something bad will happen. It's why debugging and testing the code is an important step.


You provided input for them that works obviously, but on slightly different input, they demonstrate non-obvious and potentially-unwanted behavior. It's fine if you intended it, but a) you didn't document it, and b) this is a beginner thread (despite the subforum it's posted in), where people using posted code as an example to refer to is a comment occurrence.

Which is why it's good to have the conversation. I've gone several posts asking for an example before one was provided - so it sounded more like an old wives tale than an actual issue. And in the end, it wasn't really an issue but a byproduct of what eof() does. If the loop requires eof(), it probably is dependent on input and won't cause an issue if it goes an extra time. If it does, then there's debugging and figuring out that putting peek() is a pretty good "hack" as you've said.


Sorry, but while I agree eof() isn't some devil to avoid at all costs, this simply isn't a good use of it.


By "this" I'm not sure what you're referring to. If the OP's original code, it's not the most efficient but it's lifetimes away from breaking the program. If you mean the way it's been used in these posts, those were just examples. I've given scenarios a few posts ago where eof() might be preferred.


P.S. - std::getline, unlike most stream extraction operators, doesn't strip leading whitespace. It does discard the delimiter, which is a newline by default, but that's about it.

Yes, you're right. You'd have to code it to realize when the input was empty so that it'll output a newline if you were just trying to copy the text faithfully. I don't think this is a very important/common use case, but if it ever matters, the peek() solution works just as well.
This'll probably be my last reply to this topic.

Can't tell if you were being sarcastic, but you have nothing to apologize for.

Sarcasm definitely not intended. :)

if we stay away from these things because they have the potential of hurting us, we wouldn't get anywhere. Obviously the only difference would be the magnitude

Whoops. At some point while drafting my previous post, I know I acknowledged that. I must have deleted it while enumerating the code examples.

Unlike with our physical actions (where we can be careful and still screw up), with coding, you can test against a few possible use cases to make sure that the issue never presents itself.

Eh, I wouldn't rely on a test-suite being well-written or complete, especially for code that's just being written. And for beginners, I wouldn't rely on the existence of a test suite (other than whatever an instructor uses to grade stuff, at which point it's too late).

And in the end, it wasn't really an issue but a byproduct of what eof() does.

But... if that byproduct was not specifically desired, that's a bug, no? I don't think anyone here said there was a fundamental issue with eof() (unlike something like gets).

If the loop requires eof(), it probably is dependent on input and won't cause an issue if it goes an extra time.

That's putting an awful lot of trust in whoever wrote that loop, IMO, considering that misuse of eof() as a loop condition is a recurring issue. It could just as easily be something like:
1
2
3
4
while(! input.eof()) {
    input >> integer_variable;
    //SNIP: Do something with integer_variable w/o checking input.fail()
}

...being run on input that wasn't validated earlier.

By "this" I'm not sure what you're referring to.

I meant the use of !eof() as the sole condition of an input loop. Actually, in those circumstances, I'd tend to recommend good() over !eof() by default, the reasoning being (among other things) that if a read failure occurs, most of the time you're not going to want to run the loop again.

-Albatross
I suppose my final reply to wrap things up.

Eh, I wouldn't rely on a test-suite being well-written or complete, especially for code that's just being written.

I personally enjoy seeing if my code is full-proof or not, but I suppose you're right. Again though, this would be true for many things.

But... if that byproduct was not specifically desired, that's a bug, no?

Many things do something not desired. For example, when getline is skipped over because you cin beforehand and there's a '\n' left in the buffer. That's unwanted behavior, but no one says to forego getline like they do eof(). I know you said there's no benefit, but it's benefited me many times, a math-heavy program I was coding for example. Every iteration relied on several ">>" with heavy parsing. It was simply too problematic to take in from the condition statement into a temp and then try to sort it into the correct variables later.

I don't think anyone here said there was a fundamental issue with eof()
Cubbi seemed like he wanted to stab me for even thinking it was a viable option.

That's putting an awful lot of trust in whoever wrote that loop, IMO, considering that misuse of eof() as a loop condition is a recurring issue.

Yes, but it's something they have to do. Even a regular cin >> can break the stream. Checking should be second nature at this point unless the program is coded in a way where the stream is impossible to break.


Actually, in those circumstances, I'd tend to recommend good() over !eof() by default, the reasoning being (among other things) that if a read failure occurs, most of the time you're not going to want to run the loop again.

That's ironic, because I've never had a loop break because of a stream that I didn't want up and running again. Of course, I can imagine the many scenarios you'd want the loop to break, but my scenarios usually involve fixing the stream and moving on. Though yes, if you'd want it to end if it breaks, good() would be the better solution to keep from going into an infinite loop.


"I will stop doing this thing that I love and makes me feel happy and fulfilled to follow you to the next stop on our pointless, depressing journey." ~ Shitbot

"Where would we be without the pussy riding that proud cock?" ~ Kian Alvane
Or at least he said something along those lines.
Topic archived. No new replies allowed.
Pages: 12