Lightness Races in Orbit Lightness Races in Orbit - 1 month ago 16
C++ Question

Why does std::ios_base::ignore() set the EOF bit?

When I read all data from a stream, but make no attempt to read past its end, the stream's EOF is not set. That's how C++ streams work, right? It's the reason this works:

#include <sstream>
#include <cassert>

char buf[255];

int main()
{
std::stringstream ss("abcdef");
ss.read(buf, 6);

assert(!ss.eof());
assert(ss.tellg() == 6);
}


However, if instead of
read()
ing data I
ignore()
it, EOF is set:

#include <sstream>
#include <cassert>

int main()
{
std::stringstream ss("abcdef");
ss.ignore(6);

assert(!ss.eof()); // <-- FAILS
assert(ss.tellg() == 6); // <-- FAILS
}


This is on GCC 4.8 and GCC trunk (Coliru).

It also has the unfortunate side-effect of making
tellg()
return
-1
(because that's what
tellg()
does), which is annoying for what I'm doing.

Is this standard-mandated? If so, which passage and why? Why would
ignore()
attempt to read more than I told it to?

I can't find any reason for this behaviour on cppreference's
ignore()
page
. I can probably
.seekg(6, std::ios::cur)
instead, right? But I'd still like to know what's going on.

Answer

I think this is a libstdc++ bug (42875, h/t NathanOliver). The requirements on ignore() in [istream.unformatted] are:

Characters are extracted until any of the following occurs:
n != numeric_limits<streamsize>::max() (18.3.2) and n characters have been extracted so far
— end-of-file occurs on the input sequence (in which case the function calls setstate(eofbit), which may throw ios_base::failure (27.5.5.4));
traits::eq_int_type(traits::to_int_type(c), delim) for the next available input character c (in which case c is extracted).
Remarks: The last condition will never occur if traits::eq_int_type(delim, traits::eof()).

So we have two conditions (the last is ignored) - we either read n characters, or at some point we hit end-of-file in which case we set the eofbit. But, we are able to read n characters from the stream in this case (there are in fact 6 characters in your stream), so we will not hit end-of-file on the input sequence.

In libc++, eof() is not set and tellg() does return 6.