Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
332 views
in Technique[技术] by (71.8m points)

c++ - std::istream operator exception reset / not thrown

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...