putting an object as condition in while statement

Pages: 12
I am trying to understand how filestream objects work. What does the file passed as a condition in the while loop return wheen running its loop and breaking off the loop? Is it the same as writing "while(file == true)"?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main()
{
    string filename;
    string temp;

    cin >> filename;
    ifstream file(filename);

    while(file){
        getline(file, temp);
        cout << temp;
    }
    cout << "ended";

    return 0;
}


If its true, how do I make something like this possible:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct A{
    string hi = "hello";
    
};



int main()
{
    A a;
    
    
    if(a){
        
    }
    cout << "ended";

    return 0;
}
Last edited on
I'm not fully sure what condition "file" waits for to return false, but it ends up repeating the last line of the file. It's much better to use file.eof() :

1
2
3
4
5
while (!file.eof())
{
	getline(file, temp);
	cout << temp << '\n';
}


But no, the last piece of code wont work. File streams have a bunch of behind the scenes stuff so that something like "while(file)" actually works. But you can't just put a struct like that as a condition. At what point would you actually expect the loop to end if it even starts at all? You can do this however:

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
using namespace std;

struct A 
{
	string hi = "hello";
	bool set = true;
};



int main()
{
	A a;


	while (a.set) 
	{

		if (...) //Whatever Condition To End The Loop
		{
			a.set = false;
		}

	}
	cout << "ended";

	return 0;
}
@ndcn541,

C & C++ "if" statements evaluate a boolean. Whatever expression is inside the parenthesis must resolve to a boolean for "if" to work, notwithstanding the multiple tests possible, like if ( a < b || b > d ).

So, if you want to be able to evaluate an instance with

1
2
3
A a

if ( a )....


You would add a conversion operator representing a boolean.

1
2
3
4
5
6
7
8
class A
{
 private:
 int v { 0 };

 public:
 operator bool () const { if ( v > 0 ) return true; return false; }
};


That trivial example will permit an instance of class A to be evaluated as a bool, using a conversion operator. Of course, there still must be some kind of evaluation in the conversion operator which makes sense, which follows what @zapshe is saying where the "set" member provides a true or false value, but that could be wrapped up into a conversion operator to bool.

There is a problem, though. Some conversions happen when you don't expect it. Wherever a function overload calls for a bool and can take an "A", there can be conflicts which cause the compiler to complain.

You can precede the conversion operator with the "explicit" declaration, which can help.

From a design standpoint it isn't advisable to do this just for convenience. It should be a design concern that evaluating the instance as a bool makes sense due to what the object is and does.

For example, there is a conversion operator available so you can evaluate some smart pointers as bools, to test if they're "nullptr", or contain some valid instance. This mimics historical use of raw pointers in the same test, which makes sense as a design.

On the other hand, if you design a class representing a 2d vector (as in graphics, not containers), and you must test to ensure the vector is a unit vector (length of 1), using a conversion operator so this vector could be tested as a bool doesn't make sense, because there are no other situations in which vectors are tested this way in math. To evaluate if a vector is a unit vector, it should be checked for it's length == 1, so one should make that explicit by using the length() member function of a vector. There could be so many different interpretations for a conversion to bool on a vector (like, a zero vector), that it just doesn't "fit" well to use the one and only conversion operator to a bool for some arbitrary choice.


Last edited on
> Is it the same as writing "while(file == true)"?

Yes. A standard stream is contextually converted to bool.

explicit operator bool() const ;

Returns true if the stream has no errors and is ready for I/O operations. Specifically, returns !fail().

This operator makes it possible to use streams and functions that return references to streams as loop conditions, resulting in the idiomatic C++ input loops such as
while(stream >> value) {...}
or while(getline(stream, string)){...}.
Such loops execute the loop's body only if the input operation succeeded.

https://en.cppreference.com/w/cpp/io/basic_ios/operator_bool



A contextual conversion example:

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
29
30
31
32
33
34
35
#include <iostream>
#include <string>

struct A {

    std::string hi = "hello" ;

    // user-defined conversion from A to bool (toy)
    // explicit: this conversion function can't be used for implicit conversions
    // https://en.cppreference.com/w/cpp/language/cast_operator
    explicit operator bool() const { return hi.size() > 8 ; }

    // basic decency: provide an overloaded operator!() for symmetry
    // returns the negation of the result of operator bool()
    bool operator!() const { return !operator bool() ; }
};

int main() {

    const A a { "string containing more than eight characters" } ;

    // https://en.cppreference.com/w/cpp/language/implicit_conversion#Contextual_conversions
    if(a) { // a is contextually converted to bool via a.operator bool()

        std::cout << "object a when contextually converted to bool yields true\n" ;
    }

    // a is contextually converted to bool (when used in the expression a && 23)
    std::cout << std::boolalpha << ( a && 23 ) << '\n' ; // true

    // const bool f = a ; // *** error: no implicit conversion from a to bool

    const bool f2 = bool(a) ; // fine: explicit conversion to bool
    std::cout << f2 << '\n' ; // true
}
zapshe wrote:

It's much better to use file.eof() :

That's wrong.

Do not loop on (! stream.eof()) or (stream.good()). This does not work the way you expect. The eof bit is set true only after you make a read attempt on the file. This means after you read the last record of the file, eof is still false. Your attempt to read past the last record sets eof, but you're not checking it there. You proceed as if you had read a good record. This will result in reading an extra (bad) record. The correct way to deal with this is to put the >> (or getline) operation as the condition in the while statement.
1
2
3
  while (stream >> var) // or while (getline(stream,var))
  {  //  Good operation
  }
The eof bit is set true only after you make a read attempt on the file.


I'm not sure if that's still how that works, and it wouldn't be a deal breaker if it was. If this was true, it would go through the entire loop an extra time, but it doesn't:

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

int main()
{
	string filename;
	string temp;

	ifstream file("sortedNames.txt");

	int i = 0;
	while(!file.eof())
	{
		i++;
		getline(file, temp);
		cout << i << ": " << temp << '\n';
	}
	cout << "ended: " << i;

	return 0;
}


"i" will be the amount of times the loop runs, and it's the exact same number of lines I had. So by the time it finishes reading the last line, it's already set to the true.

I don't know if that's just a Visual Studio thing, but I've never encountered that extra read issue. However, using just "file" instead of "file.eof()" results in that, reading one extra time.
Last edited on
Does sortedNames.txt end with a newline? It should, but if it doesn't, that would make getline set eofbit but not failbit (which, by the way, good() isn't the same as !fail()).

Seriously, though, please check for the success of your read operations before using the variables you've read into. AbstractionAnon is right to recommend looping on the read operation here.

-Albatross
Last edited on
It doesn't end with a newline - so I put a newline and tried again:


1: Taylor, Marie Denise
2: Thomas, James Wilis
3: Brown, Stone Rock
4: Lea, High Lee
5: Reynolds, Stephens Jabcobs
6: Russell, Mack Hussell
7: Lewis, Michelle Tee
8: Marshall, John Mark
9: Moto, Ah Sey
10: Vey, O E
11: Knocktosee, Twosee Ore
12: Finitee, Two N
13: Ghigher, Eve N
14: Jammer, T Tow
15: One, Uh Nuther
16: Lotts, Lue Knew
17: Moto, Ah C
18: Phinshed, Just About
19: Won, Thee Last
20:
ended: 20


Without the newline it ended at 19.

I've never had issues with eof()

I usually use the ">>" operator in the loop condition, but there have been times where that simply wasn't possible. eof() has never done anything I've seen said about it. Again, I don't know whether that's just Visual Studio, the situation isn't right, or if the way it works changed, but I've never encountered an issue with it.
I've never had issues with eof()

"This works, because it gives the correct answer!"

Wait, that was not a valid argument, was it?
"This works, because it gives the correct answer!"

Wait, that was not a valid argument, was it?


IDK, ask physicists. "This equation works because it's reflective of reality!" - What a bunch of morons, huh?
If you loop on .eof(), you're not testing for conversion failure nor I/O errors. Hopefully you have set the stream's exception mask instead, but if not, any malformed input or I/O problem will break your program.

Here is a program that exhibits the issue.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <sstream>

constexpr char data[] = "a b\nc d\n";

int main()
{
  for (std::istringstream s{data}; !s.eof(); ) // wrong
  {
    std::string a, b;
    s >> a >> b;
    std::cout << "a: " << a << "; b: " << b << ";\n";
  }
}
Last edited on
That argument doesn't even begin to approach relevance, zapshe. The behavior of std::getline (and iostreams in general) are human-designed and well documented. The fundamental behavior of reality is not documented by whatever (if anything) designed it, and physicists are given the job of writing models that approximate that (undocumented) behavior. Those are two very different situations. Comparing them is a fallacy.

Keeping with the physics comparison, what you did earlier is like giving advice for some ball-throwing sport (let's say golf) that completely disregards the existence of wind. You arrived at that advice based on your own experience playing that sport, which you play exclusively on windless days. Then, someone came and pointed out that advice is insufficient and can get people in trouble on windy days (a common occurrence), at which point you replied with "I've never had issues with the way I'm doing it".

Also, if you can't check your istream's state in a loop's condition, you can always stick it in the condition of an if statement. Just a note.

-Albatross
That argument doesn't even begin to approach relevance, zapshe... You arrived at that advice based on your own experience

I clearly said the issue didn't happen with me and stated 3 possibilities why. That's why I was sassy with Keskiverto. I didn't say you guys were wrong, I was saying it never happened with me and speculated on why. Provide me code where it happens so I can learn, or don't and find someone else to criticize. He basically gave a straw man argument because I never made that claim he shot down.


The behavior of std::getline (and iostreams in general) are human-designed and well documented. The fundamental behavior of reality is not documented by whatever (if anything) designed it, and physicists are given the job of writing models that approximate that (undocumented) behavior. Those are two very different situations. Comparing them is a fallacy.

That was kind of the point.


Also, if you can't check your istream's state in a loop's condition, you can always stick it in the condition of an if statement.

Which is what I end up doing when having something like "while(inFile >> temp)" isn't an option. I clearly also stated it simply wasn't feasible in some situations.
I clearly said the issue didn't happen with me and stated 3 possibilities why.

Beginners roam these Fora. They don't analyze possibilities.
They see: "eof() works" in conversation. They will probably miss that it is not true.

What is wrong in (apart result not being 100% same as for OP):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <fstream>
#include <string>

int main()
{
  std::string filename;
  if ( std::cin >> filename ) {
    std::ifstream file(filename);
    std::string temp;
    while ( std::getline( file, temp ) ) {
        std::cout << temp;
    }
    std::cout << "ended";
  }
}


However, file access was not the topic of this thread. Object in condition was.

Yes, if ( obj ) has same meaning as if ( obj == true ).

In the first the obj is converted to bool value, if possible. As shown, a class can define such conversion operator for itself.

In the second there either is an operator== that accepts those operand types (a class can provide it) or the obj converts to bool before the ==.

Condition if ( cin >> filename ) evaluates if ( cin )
Condition if ( getline(file,temp) ) evaluates if ( file )
What happens to filename and temp in those is a side-effect.
Last edited on
Beginners roam these Fora. They don't analyze possibilities.

Well I'm glad you were able to find some kind of logic you could use.

What is wrong in

I recall two separate coding assignments where putting getline or ">>" as the condition wasn't as logical. Ex: The variable you'd want to input would change.

If the program you provided was supposed to show the faultiness of using .eof(), it didn't. The only difference was when I added many blank lines. Using getline still counted them except for the last one while eof() outputted the last line too. When I left no blank new lines at the end, the output was exactly the same. I tested this on cpp.sh with the same results.


It was said:
The eof bit is set true only after you make a read attempt on the file. This means after you read the last record of the file, eof is still false.

Meaning using eof() as the condition would make it loop an extra time even though there's nothing left in the file to read. It's never done that. I don't know of a situation where this does happen. If this is an actual issue, some code showing where it happens would be helpful. Though at this point, I'm doubting the claim.


The only valid statement I've seen is that the stream might break, which doesn't explain why not to use good() or even why eof() is bad since using it means you were likely parsing code and checking the condition of the stream anyway.
I am still wondering who started this rumor that eofbit checks are somehow meaningful as loop conditions. It appears to have happened early in C history: while(!feof(fp)) is just as wrong and just as popular with bad tutorials. Every input loop in K&R correctly performs input inside loop condition and checks the result, never feof, it must have been some other 80s book author.
zapshe wrote:
Meaning using eof() as the condition would make it loop an extra time even though there's nothing left in the file to read. It's never done that. I don't know of a situation where this does happen. If this is an actual issue, some code showing where it happens would be helpful.


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() )
   {
      in >> line;
      cout << line;
   }
   
   cout << '\n';
   
   stringstream in2( "last\n"
                     "chance\n" );
   while( in2 >> line )
   {   
      cout << line;
   }
}


zapsheshe
lastchance

Last edited on
Thanks Lastchance, but I think that actually proves that there's nothing wrong with using eof() when you know what you're doing.

The only reason it outputs "she" twice is because you ended the text with a new line and were using ">>" instead of getline. So there was a ('\n') left in the file and it skipped it - imitating an extra loop but it actually did exactly what you'd expect it to.

It very much DID set eof() when it reached the last line and DIDN'T loop an extra time. There was still an extra line to be had.


I'm gonna go kill myself.
But @zapshe, I handle numerical data, not strings, most of the time.

I don't want to have to read everything into a string with getline() and subsequently have to process the data into numerical variables. Moreover, reading it into a string won't tell me if it is invalid numerical data: I have to wait for the subsequent processing to do that.

No, I'd prefer to use the stream extraction operator >> when appropriate, and for that the test (or even multiple tests) in the if() or while() statements is infinitely preferable to "having to be very careful what I'm doing" with eof().
Every method has it's place, and I'm sure using ">>" in the loop condition is useful for a million reasons (I've used it myself, but some situations make it impractical). eof() has it's own use cases, it's useful in that it tells you when you've reached the end of the file and so you're free to take in either whole lines or words/numbers as needed with conditions. I just think eof() is being unjustly criticized.
Pages: 12