Should std::ws raise failbit at end of file?
Asked Answered
J

2

10

Should extracting from a stream using the std::ws manipulator ever raise the fail bit? In the following code, a Clang-compiled (within Xcode 4.5.1) program fails the final assertion. Evidently s >> std::ws at EOF causes a fail. Yet GCC 4.7.2 passes the assertion. Which is correct?

#include <iostream>
#include <sstream>
#include <cassert>

int main(int argc, const char * argv[])
{
    {
        // Read string with trailing ws.
        std::istringstream s( "test   " );
        std::string test;

        s >> std::ws;
        assert( !s.fail() );    // No ws to skip, but no failure.

        s >> test;
        assert( test == "test" );
        assert( !s.fail() );

        s >> std::ws;
        assert( !s.fail() );    // No prob skipping trailing ws.
    }
    {
        // Retry with no trailing ws.
        std::istringstream s( "test" );
        std::string test;

        s >> std::ws;
        assert( !s.fail() );    // No ws to skip, but no failure.

        s >> test;
        assert( test == "test" );
        assert( !s.fail() );

        s >> std::ws;
        assert( !s.fail() );    // CLANG: Skipping absent ws at eof raises failbit.
    }

    return 0;
}
Josefina answered 16/11, 2012 at 19:56 Comment(1)
For me gcc 4.6, gcc 4.7 and clang 3.1 all pass every assertYestreen
E
3

C++11, §27.7.2.4/1:

If ws stops extracting characters because there are no more available it sets eofbit, but not failbit.

So, the ws manipulator doesn't set failbit directly. However, as Marshall Clow points out in his answer, it doesn't have to--it is required to create a sentry object, and the sentry object is required to set the failbit if !stream.good().

Excellence answered 16/11, 2012 at 20:18 Comment(3)
So is this a bug in libc++?Gerta
This answer is incorrect according to the C++ specifications since C++11 (I didn't check the C++03 spec).Counterweight
@HowardHinnant: Yup--Marshall should really get the "accepted" flag, but only the OP can un-accept the answer, and until/unless he does, I can't delete it.Excellence
T
4

I believe that libc++ is implementing the standard correctly.

[ I'm quoting from N3290, which is the draft C++11 standard. C++14 does not change this. ]

ws is described in [istream.manip], which states:

Effects: Behaves as an unformatted input function (as described in 27.7.2.3, paragraph 1), except that it does not count the number of characters extracted and does not affect the value returned by subsequent calls to is.gcount(). 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. Whitespace characters are distinguished with the same criterion as used by sentry::sentry (27.7.2.1.3). If ws stops extracting characters because there are no more available it sets eofbit, but not fail bit.

The key phrase here for determining the correct behavior is "after constructing a sentry object".

Sentry objects are described in [istream::sentry], and the text there begins...

1 The class sentry defines a class that is responsible for doing exception safe prefix and suffix operations.

explicit sentry(basic_istream& is, bool noskipws = false);

2 Effects: If is.good() is false, calls is.setstate(failbit). Otherwise, prepares for formatted or > unformatted input. ...and so on...

That's the only sentry constructor that is available, so that's the one that libc++ uses.

So the fail bit gets set before extracts any characters, so the text at the end of the paragraph does not apply. if the stream contained " " (i.e, a single space at the end), then calling std::ws does not set the fail bit, just eof (which is what the OP expected to happen).

Thorathoracic answered 21/4, 2014 at 16:24 Comment(5)
That seems like a fair reading of the standard to me, but since it leads to a less useful version std::ws, I think we should consider GCC's interpretation as the right one. The problem with Clang's version is that it cannot be used to eat optional white space at end of input.Tiphanie
If we've got divergence between implementations, then I think that the C++ standards committee should decide what behavior they meant to specify, and re-word that section of the standard so that it is clear :-)Thorathoracic
Directions for creating an LWG issue: cplusplus.github.io/LWG/lwg-active.html#submit_issueCounterweight
@HowardHinnant: Does Marshall really need the link to know how to create an LWG issue? ;-)Excellence
No, I do not :-) but other people might.Thorathoracic
E
3

C++11, §27.7.2.4/1:

If ws stops extracting characters because there are no more available it sets eofbit, but not failbit.

So, the ws manipulator doesn't set failbit directly. However, as Marshall Clow points out in his answer, it doesn't have to--it is required to create a sentry object, and the sentry object is required to set the failbit if !stream.good().

Excellence answered 16/11, 2012 at 20:18 Comment(3)
So is this a bug in libc++?Gerta
This answer is incorrect according to the C++ specifications since C++11 (I didn't check the C++03 spec).Counterweight
@HowardHinnant: Yup--Marshall should really get the "accepted" flag, but only the OP can un-accept the answer, and until/unless he does, I can't delete it.Excellence

© 2022 - 2024 — McMap. All rights reserved.