Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should std::ws raise failbit at end of file?

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;
}
like image 264
OldPeculier Avatar asked Nov 16 '12 19:11

OldPeculier


2 Answers

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).

like image 109
Marshall Clow Avatar answered Nov 01 '22 13:11

Marshall Clow


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().

like image 31
Jerry Coffin Avatar answered Nov 01 '22 14:11

Jerry Coffin