Matthew Busche
Matthew Busche

Reputation: 674

What is the behavior of the C++ method ostream::exceptions(iostate state) supposed to be?

I'm in the process of writing my own output streaming library and I'm trying to mimic std::ostream behaviors where it doesn't conflict with the new behaviors I'm trying to achieve. Currently I'm trying to mimic this interface inherited from ios:

std::ostream::exceptions(ios::iostate state)

According to cplusplus.com:

"this method sets a new exception mask for the stream and clears the stream's error state flags (as if member clear() was called)."

It wasn't clear to me whether this meant all flags would be cleared or only the ones being set in the exceptions mask, so I wrote a test program but got quite unexpected results. Here's the program:

#include <sstream>
#include <iostream>

using namespace std;
int main(int argc, char** argv)
{
    ostringstream oss;
    try
    {
        cout << "            badbit           = " << ios::badbit        << "\n";
        cout << "            eofbit           = " << ios::eofbit        << "\n";
        cout << "            failbit          = " << ios::failbit       << "\n\n";

        cout << "            oss.rdstate()    = " << oss.rdstate()      << "\n";
        cout << "            oss.exceptions() = " << oss.exceptions()   << "\n\n";

        cout << "executing:  oss.setstate(ios::badbit | ios::failbit);" << "\n";
        oss.setstate(ios::badbit | ios::failbit);
        cout << "            oss.rdstate()    = " << oss.rdstate()      << "\n";
        cout << "            oss.exceptions() = " << oss.exceptions()   << "\n\n";

        cout << "executing:  oss.exceptions(ios::failbit);"             << "\n";
        oss.exceptions(ios::failbit);
        cout << "            oss.rdstate()    = " << oss.rdstate()      << "\n";
        cout << "            oss.exceptions() = " << oss.exceptions()   << "\n";
    }
    catch(const exception& x)
    {
        cout << endl;
        cout << "**** EXCEPTION THROWN ****"                            << "\n";
        cout << argv[0] << ":  " << x.what() << endl;
        cout << "            oss.rdstate()    = " << oss.rdstate()      << "\n";
        cout << "            oss.exceptions() = " << oss.exceptions()   << "\n";
        return 1;
    }
    catch(...)
    {
        cerr << argv[0] << ":  unknown exception." << endl;
        return 1;
    }
    return 0;
}

And here's the output:

matt@dworkin:~/dev/ostream/libs/ostream$ g++ --version
g++ (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

matt@dworkin:~/dev/ostream/libs/ostream$ g++ --std=c++17 foo.cpp
matt@dworkin:~/dev/ostream/libs/ostream$ ./a.out

            badbit           = 1
            eofbit           = 2
            failbit          = 4

            oss.rdstate()    = 0
            oss.exceptions() = 0

executing:  oss.setstate(ios::badbit | ios::failbit);
            oss.rdstate()    = 5
            oss.exceptions() = 0

executing:  oss.exceptions(ios::failbit);

**** EXCEPTION THROWN ****
./a.out:  basic_ios::clear: iostream error
            oss.rdstate()    = 5
            oss.exceptions() = 4

So based on the cplusplus.com docs, I wasn't expecting an exception to be thrown at all. And as can be seen from the output generated from within the exception handler, no state flags were ever cleared. So is this a compiler bug, a documentation bug, or am I missing something?

As an aside, I prefer the behavior exhibited over the behavior documented. Seems sorta strange to me that a request to throw on errors would have the side-affect of erasing existing errors. I actually first implemented it the same way g++ apparently does (assuming this was the way it would surely work), and only then read the docs for this method.

Upvotes: 0

Views: 294

Answers (1)

user4581301
user4581301

Reputation: 33952

cplusplus.com is wrong. They are correct that clear is called by exceptions, but it is not called to clear the state and the state is not cleared.

In spite of the name, clear is used in the back end by iostreams, including setstate itself, to do the grunt work for a great deal of setting, not just clearing, of the stream's state. As such, it is the logical place to house the raising of exceptions and other flag-related behaviours.

In exceptions it appears clear is being used primarily test for existing flags to raise an exception should any of the desired flags already be set. To actually clear the flags after the exception, a call to clear is still required to remove the error flags.

Upvotes: 1

Related Questions