2

I've just encountered a curious situation in C++. I was doing something like:

istream in;
// ...
in.get();      // get a char (which turns out to be the last)
               // curiously ios::eof bit doesn't get set just yet

c = in.peek(); // attempt to peek, return EOF and now set ios::eof bit

if(c == EOF) {
    // realize shouldn't have gotten the last char for some reason
    in.unget(): // attempt to unget, *fails* (because ios:eof bit was set)
}

Now I'm curious why peek sets the eof bit; I find this highly unintuitive. It's supposed to just peek not actually consume anything and shouldn't change the stream state. Also, why unget subsequently doesn't work? Does the standard mandates all operations to be nop when good() is false or something?

user229044
  • 232,980
  • 40
  • 330
  • 338
Giovanni Funchal
  • 8,934
  • 13
  • 61
  • 110

1 Answers1

4
in.get();      // get a char (which turns out to be the last)
               // curiously ios::eof bit doesn't get set just yet

This is not "curious". The stream's EOF bit gets set when a read fails due to having reached eof; it does not mean "the last read took us to eof".

c = in.peek(); // attempt to peek, return EOF and now set ios::eof bit

Like now.

Also, why unget subsequently doesn't work? Does the standard mandates all operations to be nop when good() is false or something?

... which is what's happening. You failed to otherwise define "doesn't work".

You'll have to clear the stream's state yourself when EOF was reached, if you want to unget that character you retrieved on line 3.

istream in;
// ...
in.get();      // assume this succeeds (*)

c = in.peek(); // assume this fails and sets EOF bit

if (!in) {
   in.clear(); // clear stream error state for use
   in.unget(); // "put back" that character (*)
}
else {
   // next char from .get() will be equal to `c`
}
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Your answer is kind of convoluted and unclear to me. Looks like I (wrongly) assumed that the eof bit was an indication that the stream's current position was *at the end*. Your code will not clear only the eof bit. Also, on which basis you say that `unget` makes no sense after a `peek` that returned `EOF`? – Giovanni Funchal Jan 06 '12 at 20:11
  • @GiovanniFunchal: I'm sorry you feel that way. If your `peek` failed to yield a character from the stream (because there were no more to retrieve), then how does it make sense to perform an `unget`? There's nothing to unget; you failed to yield a character from the stream. And as for your comment about stream state clearing... I have no idea why that would be a problem for you. – Lightness Races in Orbit Jan 06 '12 at 20:16
  • 3
    The eof() flag indicates that the the stream tried to read past the last character. Before trying to read past the last character the stream doesn't know that it was the last. This flag is only good to detect why a read failed: tupically you don't want to report an error due to having consumed the whole file. – Dietmar Kühl Jan 06 '12 at 20:17
  • (Oh, I see that you may be attempting to `unget` what was gotten on the third line. That makes sense. You still need to clear the stream state first, though.) – Lightness Races in Orbit Jan 06 '12 at 20:18
  • FYI, C++11 asks for unget to first clear the eofbit, so I think the OP code would work with a C++11 implementation. – AProgrammer Jan 06 '12 at 20:28
  • 1
    @AProgrammer: Hmm, so it does! (27.7.2.3/36) Very nice. – Lightness Races in Orbit Jan 06 '12 at 20:31
  • @LightnessRacesinOrbit, and it is new, it wasn't in C++03. – AProgrammer Jan 06 '12 at 20:51
  • Oh god, I have wasted an entire day fighting this issue. I'll name my first born after you. – Urban Mar 08 '16 at 23:36