I'm not sure about how to use std::istream::exception
according to the standard, to let std::istream::operator>>
throw an exception if it can't read the input into a variable, e.g. double.
The following code has different behavior with clang/libc++ and gcc/libstdc++:
#include <iostream>
#include <cassert>
int main () {
double foo,bar;
std::istream& is = std::cin;
is.exceptions(std::istream::failbit);
is >> foo; //throws exception as expected with gcc/libstdc++ with input "ASD"
std::cout << foo;
is >> bar;
std::cout << bar;
assert(is); //failed with clang/libc++ after input "ASD"
std::cout << foo << " " << bar << std::endl;
}
Is is.exceptions(std::istream::failbit);
right for the purpose to let operator>>
throw, according to the C++ standard?
First some background information (each of these is explained below under it's respective title if you would like further elaboration):
- The standard requires
istream
s to rethrow only whenios_base::badbit
is set inbasic_istream::exceptions
- libstdc++ does not comply with this requirement but libc++ does
- libc++ invalidates bugs requesting it's mirroring of libstdc++ behavior
- libc++ proffers
ios_base::badbit
bit-wise ored with the desiredios_base::iostate
as a workaround
Unfortunately this workaround has the side effect of also rethrowing whenever ios_base::badbit
is set independent of ios_base::failbit
: http://en.cppreference.com/w/cpp/io/ios_base/iostate#The_badbit
If you're looking for a throw to happen only when ios_base::failbit
is set and you need this to have the same behavior on libc++ and libstdc++ you'll have to check the ios_base::badbit
after each input operation occurring on the istream
. That'd need to look something like this:
if((is.rdstate() & ios_base::failbit) != 0) throw ios_base::failure("basic_ios::clear");
As noted by cpplearner you can't even use basic_istream::fail
, you have to do a bit-wise test of the istream
's rdstate
return. But honestly that only adds a bit of complexity.
What could make this a monumental task is the extent to which the istream
is used. Wide usage of the istream
could be combated by helper functions, but use of istream_iterator
s or compound overloads of the extraction operator quickly make the manual inspection of this an unreasonable task.
If you find yourself there I would seriously consider the possibility of the is.exceptions(ios_base::failbit | ios_base::badbit)
workaround.
The standard requires istream
s to rethrow only when ios_base::badbit
is set in basic_istream::exceptions
Calling basic_istream::exceptions(istream::failbit)
will set a mask which can be retrieved by calling basic_istream::exceptions()
which according to 27.5.5.4 [iosstate.flags]/11 of the standard is:
A mask that determines what elements set in
rdstate()
cause exceptions to be thrown.
This is supported in 27.7.2.2.3 [istream::extractors]/15 for unformated insertion methods:
If it inserted no characters because it caught an exception thrown while extracting characters from
*this
andfailbit
is on inexceptions()
(27.5.5.4), then the caught exception is rethrown.
However for formatted input this is retrograded in 27.7.2.2.1 [istream.formatted.reqmts]/1; requiring a throw to occur only when a bit-wise and of the mask and ios_base::badbit
is non-zero:
If an exception is thrown during input then
ios::badbit
is turned on in*this
’s error state. If(exceptions()&badbit) != 0
then the exception is rethrown.
libstdc++ does not comply with this requirement but libc++ does
The ios_base::failbit
should be set on it's respective istream
on events such as:
The numeric, pointer, and boolean input overloads of
basic_istream::operator>>
(technically, the overloads ofnum_get::get
they call), if the input cannot be parsed as a valid value or if the value parsed does not fit in the destination type.
[Source]
If only the ios_base::failbit
is set on a basic_istream::exceptions
' mask and an event occurs, causing the ios_base::failbit
to be set, such as extracting an invalid number as described above:
- Under libstdc++ an exception is still rethrown, which does not conform to the standard
- Under libc++ no such exception is thrown, meaning the standard is upheld
libc++ invalidates bugs requesting it's mirroring of libstdc++ behavior
There is a now invalidated bug against libc++ for this very issue. Citing 27.7.2.1 [istream]/4
If one of these called functions throws an exception, then unless explicitly noted otherwise, the input function sets
badbit
in error state. Ifbadbit
is on inexceptions()
, the input function rethrows the exception without completing its actions, otherwise it does not throw anything and proceeds as if the called function had returned a failure indication.
libc++ proffers ios_base::badbit
bit-wise ored with the desired ios_base::iostate
as a workaround
Our own Howard Hinnant (who also happens to be libc++'s representative who invalidated the linked libc++ bug) suggests in answer to a duplicate of this question (as well as in the libc++ bug) that you use the workaround:
is.exceptions(ios_base::failbit | ios_base::badbit);
It seems that libc++ follows this requirement:
Each formatted input function begins execution by constructing an object of class
sentry
with thenoskipws
(second) argumentfalse
. If the sentry object returnstrue
, when converted to a value of typebool
, the function endeavors to obtain the requested input. If an exception is thrown during input thenios::badbit
is turned on312 in*this
’s error state. If(exceptions()&badbit) != 0
then the exception is rethrown. In any case, the formatted input function destroys the sentry object. If no exception has been thrown, it returns*this
.312) This is done without causing an
ios::failure
to be thrown.
(quoted from N4567 § 27.7.2.2.1 [istream.formatted.reqmts] but the C++ IS contains identical wording. Emphasis mine)
I don't know whether this really implies "if (exceptions()&badbit) == 0
then the input function shall not throw any exception", though.
来源:https://stackoverflow.com/questions/35019687/stdistream-operator-exception-reset-not-thrown