While libstdc++ does not, libc++ does followthe standard which states that passingios_base::failbit tobasic_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 ofLWG2349 is that it would causebasic_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 tothe 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::badbitis turned on in*this’s error state.(Exceptions thrown fromIfbasic_ios<>::clear()are not caught or rethrown.)(exceptions()&badbit) != 0then the exception is rethrown.
I understand thatbasic_istream::clear is what throws in reaction to bad formatted input so am I misreading LWG2349 or is it in fact going to stopbasic_istream from throwing any errors?
1 Answer1
The point of the language excluding exceptions "thrown fromclear()" is to ensure thatifclear() throws, because an input function has calledclear(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 failclang outputs:
eof failAfter LWG2349, the correct behavior is to set badbit and rethrow the exception:
my_get::do_geteof bad15 Comments
clear() will continue to throw in that case, it just will not setbadbit." And that throw would stillnot be rethrown, if theexceptions() & ios_base::badbit == 0, right? So this won't do anything to align the question's example behaviors of libc++ and libstdc++?clear() is not caught (or, if it is caught, it is invisibly rethrown without settingbadbit); the catch and rethrow only applies to user-thrown exceptions. So in the question's example, the correct output would be "ouch".sbumpc andsgetc) behave similarly.clear exception "all the way" is not actually possible - a conformant implementation would have to wrap user operations in a try block, so would have to specifically catchios_base::failure and rethrow it withthrow;. But that's an implementation detail - it should indeed appear to the user as if theios_base::failure thrown byclear is thrown all the way.Explore related questions
See similar questions with these tags.
