While libstdc++ does not, libc++ does follow the standard which states that passing ios_base::failbit
to basic_istream::exceptions
has no effect on formatted input. For example this code:
istringstream is{"ASD"};
double foo;
is.exceptions(istream::failbit);
try {
is >> foo;
cout << foo << endl;
} catch(ios_base::failure& fail) {
cout << "ouch\n";
}
Would result in:
My reading of LWG2349 is that it would cause basic_istream
to not throw on any formatted input.
For example LWG2349 proposes a change to the standard's 27.7.2.3 [istream]/1 which was cited with reference to the invalidation of a bug that would have made libc++ behave like libstdc++. The change is in bold and strike through below:
If an exception , other than the ones thrown from
clear()
, if any, is thrown during input thenios::badbit
is turned on in*this
’s error state.(Exceptions thrown fromIfbasic_ios<>::clear()
are not caught or rethrown.)(exceptions()&badbit) != 0
then the exception is rethrown.
I understand that basic_istream::clear
is what throws in reaction to bad formatted input so am I misreading LWG2349 or is it in fact going to stop basic_istream
from throwing any errors?
The point of the language excluding exceptions "thrown from clear()
" is to ensure that if clear()
throws, because an input function has called clear(failbit)
and (exceptions() & failbit) != 0
, then badbit is not set as a result. clear()
will continue to throw in that case, it just will not set badbit.
As described in the commentary to LWG2349, the intention is that badbit is set when an exception is thrown from user code:
PJ and Matt both agree that the intention (of badbit + rethrow) is "to signify an exception arising in user code, not the iostreams package".
Now, when can an exception be thrown by "user code" but within the iostreams machinery? One example would be by the locale getters:
struct my_get : std::num_get<char> {
using iter_type = std::istreambuf_iterator<char>;
iter_type do_get(iter_type, iter_type, std::ios_base&, std::ios_base::iostate&, bool&) const override {
throw std::logic_error{"my_get::do_get"};
}
};
int main() {
std::istringstream iss;
iss.imbue({std::locale{}, new my_get});
iss.exceptions(std::ios_base::failbit | std::ios_base::badbit);
try {
bool b;
iss >> b;
} catch (std::exception& ex) {
std::cout << ex.what() << '\n';
}
std::cout
<< ((iss.rdstate() & std::ios_base::eofbit) ? "eof " : "")
<< ((iss.rdstate() & std::ios_base::failbit) ? "fail " : "")
<< ((iss.rdstate() & std::ios_base::badbit) ? "bad " : "")
<< '\n';
}
At present, gcc outputs:
eof fail
clang outputs:
eof fail
After LWG2349, the correct behavior is to set badbit and rethrow the exception:
my_get::do_get
eof bad
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With