Consider the following simple streambuf implementation:
#include <iostream>
#include <sstream>
class mystreambuf : public std::streambuf
{
int_type underflow() override
{
// [27.6.3.4.5/6] ... 'or throws an exception if the function fails'
throw std::runtime_error("here");
}
};
int main()
{
mystreambuf rdbuf;
std::ostringstream os;
std::cout << "State: " << os.exceptions() << std::endl;
try
{
os << &rdbuf;
std::cout << "No except: " << os.good() << std::endl;
}
catch (std::runtime_error const&)
{
std::cout << "With except: " << os.good() << std::endl;
}
return 0;
}
The above implementation seems to be following my understanding of other posts:
- how to set the badbit of a stream by a customized streambuf
- Can C++ streambuf methods throw exceptions?
However I am seeing different behaviors. With gcc/linux I get:
State: 0
No except: 0
On Visual Studio 2019 I get:
State: 0
With except: 0
Which behavior is the correct one ? Both ?
For reference:
What you're really supposed to throw in a case like this is an object fo the class
std::ios_base::failure, or some other class derived from it.When/if you do that:
...g++ has the same behavior as MS VC++:
It seems pretty clear to me that the real intent is that the exception you throw be an instance of
std::ios_base::failureor a derived class, but it's much less clear whether you're entitled to throw other exceptions and still expect the same behavior. In fact, the only mention I can find outunderflow()exiting with an error is in a non-normative footnote that reads:This doesn't provide much indication in either direction. It doesn't specifically restrict the exception to
failure, but it doesn't specifically say it can be something else. And it's a non-normative footnote, so even if it did say that would only reveal the author's intent, not an actual requirement on the implementation.Most of the rest of the text deals with what happens if an exception is thrown during execution of an input or output function (in which case, a
sentryobject is created before starting the operation, and there's clear requirements for re-throwing the exception ifbadbitis set).But your code throws before it ever gets as far as trying to read input from the stream. In that circumstance, it's not clear whether the exception should be re-thrown or not.
Summary
It seems pretty clear to me that both VC++ and g++ have sort of an intent that you should normally expect the behavior you're getting from VC++. But I don't see any requirement that you'll actually get that behavior under these circumstances. So I'd say they're both correct, but purely as a quality of implementation issue, VC++'s behavior is probably preferable.