https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88034

            Bug ID: 88034
           Summary: std::ws doesn't set failbit when the stream is already
                    at EOF
           Product: gcc
           Version: 8.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: vz-gcc at zeitlins dot org
  Target Milestone: ---

Consider the following test program:

% cat -n stream_ws.cpp
     1  #include <iostream>
     2  #include <sstream>
     3
     4  int main()
     5  {
     6      std::istringstream is("123");
     7
     8      std::cout << "eof/fail/bad bit values: "
     9          << is.eofbit << ", "
    10          << is.failbit << ", "
    11          << is.badbit << "\n";
    12
    13      std::cout << "Initial state: " << is.rdstate() << "\n";
    14
    15      int n = -1;
    16      is >> n;
    17      std::cout << "After reading \"" << n << "\": " << is.rdstate() <<
"\n";
    18
    19      is >> std::ws;
    20      std::cout << "After ws: " << is.rdstate() << "\n";
    21  }

With g++ 8.2.0 (Debian 8.2.0-9) the output is:

    eof/fail/bad bit values: 2, 4, 1
    Initial state: 0
    After reading "123": 2
    After ws: 2

I.e. calling ws() for the stream which is already at EOF does _not_ set the
failbit at line 19. When the same program is compiled with MSVS 15.9, the
output is:

    eof/fail/bad bit values: 1, 2, 4
    Initial state: 0
    After reading "123": 1
    After ws: 3

i.e. it does turn failbit on.

I had originally thought that MSVS behaviour was wrong, but Microsoft engineer
Billy Robert O'Neal III has convincingly explained that it is correct in this
reply to

   
https://developercommunity.visualstudio.com/content/problem/382896/regression-stdws-incorrectly-sets-failbit-when-the.html

In case this URL is not accessible without Microsoft account, let me quote his
reply here:

--- start quoted reply ---
The current std::ws behavior is correct, and the old behavior was incorrect. If
you look at N4762 (the current C++ working paper) [istream.manip]/2 it says
that ws:


1. Behaves as an unformatted input function.

2. If it stops because it encounters the end, sets eofbit but not failbit.


In particular, the unformatted input function processing happens before
anything about this specific unformatted input operation is applied. The
unformatted input rules are described in [istream.unformatted]/1, starting
with:


> Each unformatted input function begins execution by constructing an object of 
> class sentry with the default argument noskipws (second) argument true


The behavior of constructing a sentry is described in [istream::sentry]/2,
which begins with:


> Effects: If is.good() is false, calls is.setstate(failbit)


This ordering is more directly supported by ws' specific wording in
[istream.manip]/2 which says (emphasis mine):


> *After constructing a sentry object* extracts characters as long as the next 
> available character c is whitespace or until there are no more characters in 
> the sequence.

--- end quoted reply ---

So it looks like ws() is indeed supposed to set failbit here and it's a bug in
libstdc++ that it doesn't. Note that if the stream were not already at EOF
(e.g. if there were a trailing space after "123" in the line 6 of the example
above), then failbit should not, and is not, correctly, being set by both
libstdc++ and MSVS.

Reply via email to