3

I am using the following loop to read numbers from two files until both are exhausted:

int a, b;
while (file1 >> a, file2 >> b, file1 || file2) {
    if (file1 && file2) ... // use of both a and b
    if (file1) ... // use of a
    if (file2) ... // use of b
}

My program works. But is it guaranteed to work by the standard? That is, am I permitted to continue reading from a failed stream or an implementation can choose to throw an exception?

AlwaysLearning
  • 7,257
  • 4
  • 33
  • 68
  • first you have to [clear](https://en.cppreference.com/w/cpp/io/basic_ios/clear) stream error flags. – Marek R Dec 17 '19 at 10:43
  • Also your loops stops running in case of errors. – Marek R Dec 17 '19 at 10:45
  • 1
    If you're asking whether you can continuously perform failed reads on a istream reliably once the fail state is set without repercussion (besides the obvious: the failed reads perpetually after the initial failure), then yes, you can. If you were shooting for cryptic, btw, congratulations; you found it. – WhozCraig Dec 17 '19 at 10:46
  • @MarekR It does not say there that, if I don't clear the error flags, I could get into trouble with my example. A source (preferably a quote from the standard) would be much appreciated. – AlwaysLearning Dec 17 '19 at 10:47
  • @WhozCraig I would appreciate if you could reply with a source. Do you mean that my code is cryptic? Why so? – AlwaysLearning Dec 17 '19 at 10:49
  • What kind of source did you have in mind? You're looking for a citation from a standard stating failures always fail ad infinitum? I suspect you could probably find it no slower than we could (unless you've already looked and that's why you're here?). – WhozCraig Dec 17 '19 at 10:51
  • @WhozCraig Yes, without throwing an exception. I am here because I have looked in places like cppreference.com and could not find it stated explicitly (or explicitly enough for me to see it). The standard is too cryptic for me as of yet. – AlwaysLearning Dec 17 '19 at 10:55
  • Ahh. Well, an exception will only be tossed if you've enable stream exceptions on you source, but I understand now regardless. – WhozCraig Dec 17 '19 at 10:56
  • This question got me wondering about multiple comma operators in one line, and their evaluation order, and what eventually gets returned. And now my head hurts. – acraig5075 Dec 17 '19 at 11:33
  • @PicaudVincent Both replies so far suggest that clearing the flags is unnecessary. If they are wrong IYO, I will appreciate your reply with a source. – AlwaysLearning Dec 17 '19 at 11:37
  • @AlwaysLearning sorry for the noise, I miss read your question. I will delete my comment which is not relevant – Picaud Vincent Dec 17 '19 at 11:57

2 Answers2

4

Short answer: Yes, you can attempt to input something from a stream as many times as you want. Even after an attempt to input something from that stream failed. All that will happen is that all attempts to input something after a failed attempt to input something will also fail.

Long answer: operator >> behaves as a formatted input function [istream.formatted.arithmetic]. Formatted input functions construct a local sentry object and only perform input if converting that sentry object to bool results in the value true [istream.formatted.reqmts]. By default (if you did not override this behavior by supplying custom character traits to the stream), an object of type std::sentry will be used. An std::sentry that has been constructed for a stream will evaluate to true only if good() was true [istream::sentry]. good() only returns true if neither failbit nor badbit nor eofbit are set [iostate.flags]/7. If operator >>(int&) attempts to input (due to successful construction and check of the sentry) but fails to do so, it will set the failbit [istream.formatted.arithmetic]/3. If the corresponding flag in the exception mask of the stream is set (it is not set by default), setting the failbit will result in an exception being thrown [iostate.flags]/6 [iostate.flags]/5. Otherwise, the stream will simply be in a failed state and construction of a sentry object will result in false the next time around until you call clear()

I would consider rewriting this code, for example

do
{
    if (int a; file1 >> a)
        ; // use a
    if (int b; file2 >> b)
        ; // use b
} while (file1 || file2);
Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
  • I did not know you could declare a variable inside the condition of `if` like that, thanks! But what do you with my very first `if`, where I want to use both `a` and `b`? – AlwaysLearning Dec 17 '19 at 11:33
0

Yes, you can do this. Once a stream is in a failed state (failbit), further reads will also fail, but this is what you wanted to happen. These reads will not throw an exception - the failbit will only generate an exception if this is explicitly enabled via the stream's exceptions() method. According to this source, the default is that these exceptions are not enabled:

All streams have goodbit by default (they do not throw exceptions due to error state flags being set).

Nadav Har'El
  • 11,785
  • 1
  • 24
  • 45