std::istream operator exception reset / not thrown

巧了我就是萌 提交于 2019-11-28 13:58:03
Jonathan Mee

First some background information (each of these is explained below under it's respective title if you would like further elaboration):

  • The standard requires istreams to rethrow only when ios_base::badbit is set in basic_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 desired ios_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_iterators 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 istreams 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 and failbit is on in exceptions() (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 of num_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:


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. If badbit is on in exceptions(), 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 the noskipws (second) argument false. If the sentry object returns true, when converted to a value of type bool, the function endeavors to obtain the requested input. If an exception is thrown during input then ios::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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!