3

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?

Superlokkus
  • 4,731
  • 1
  • 25
  • 57
  • 2
    @πάνταῥεῖ Sure they use libc++? Reproduced it with Apple LLVM version 7.0.2 (clang-700.1.81) and libc++ 1101 . – Superlokkus Jan 26 '16 at 17:32
  • 1
    It reproduces with libc++. Comparing std::lib issues using gcc/libstdc++ vs clang/libstdc++ is almost always pointless, but people still insist on doing it :-\ – Jonathan Wakely Jan 26 '16 at 17:34
  • 1
    Possible duplicate of [libc++ std::istringstream doesn't thrown exceptions. Bug?](http://stackoverflow.com/questions/16468189/libc-stdistringstream-doesnt-thrown-exceptions-bug) – Jonathan Mee Jan 26 '16 at 18:38
  • This is [LWG issue 2349](http://wg21.link/LWG2349). – T.C. Jan 27 '16 at 16:35

2 Answers2

7

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);
Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 1
    Try using `-stdlib=libc++` otherwise you're just using the same std::lib as GCC uses, which doesn't confirm anything about clang's library. – Jonathan Wakely Jan 26 '16 at 17:35
  • @JonathanWakely Thanks, that was a very helpful comment. I mistakenly though that Clang compiled with libc++ by default. – Jonathan Mee Jan 26 '16 at 17:49
  • 1
    `fail()` returns true if either `failbit` or `badbit` is set. To test `failbit` only, one needs to write `is.rdstate() | std::ios_base::failbit`. – cpplearner Jan 27 '16 at 06:36
  • @cpplearner Thanks for the tip. I've updated my answer. It appears `fail` is a bit of a misnomer for this function :S To whom it may concern, your bitwise or should have been a bitwise and: `is.rdstate() & istream::failbit != 0` – Jonathan Mee Jan 27 '16 at 13:58
  • If you want to make a statement about "any compliant compiler" (or really, library implementation), I'd like to see a quote from the standard, not cppreference, especially when you are contradicting a committee member who did cite the standard. – T.C. Jan 27 '16 at 14:11
  • @T.C. I'd love to, but I don't have a copy of the standard T.T I have however provided the bug report documenting this issue with libc++. Also, while not the standard there is a bit more direct quote at: http://www.cplusplus.com/reference/ios/ios/exceptions/ "The exception mask is an internal value kept by all stream objects specifying for which state flags an exception of member type `failure` (or some derived type) is thrown when set." – Jonathan Mee Jan 27 '16 at 14:49
  • 1
    "I have however provided the bug report documenting this issue with libc++"...whose (then-)maintainer does not think is a bug, based on his reading of the standard text. Perhaps Howard Hinnant was wrong, but it takes more than isolated quotes from nonauthoritative sources like cppreference or cplusplus.com to prove it. The latest working draft ([N4567](http://wg21.link/n4567)) is freely available online. – T.C. Jan 27 '16 at 16:33
  • @T.C. Thanks for the heads up. You are right, it is not libstdc++ that's compliant it's libc++ :( I've updated my answer. Thanks for keeping me honest. – Jonathan Mee Jan 28 '16 at 03:24
  • 27.7.2.2.3 [istream::extractors]/15 (please separate paragraph numbers and section numbers; it can be ambiguous otherwise) is about the streambuf extractor, not all unformatted input; the common requirements for unformatted input is in 27.7.2.3 [istream.unformatted]/1. 27.7.2.2.1 [istream.formatted.reqmts] is about formatted input, not unformatted input. Finally, the answer should discuss [LWG2349](http://wg21.link/LWG2349). – T.C. Jan 28 '16 at 03:33
  • @T.C. I don't believe [LWG2349](http://wg21.link/LWG2349) has anything to do with this. In my understanding that's a bug against class provided extraction operators which throw errors, like `bitset`'s extraction operator. If I've misunderstood that and you could correct me I'd love to include it in this answer, although I'll probably need to add a table of contents as well too ;) – Jonathan Mee Jan 28 '16 at 04:29
  • LWG2349 has *everything* to do with this. It deals with the question "are formatted/unformatted input functions supposed to swallow exceptions thrown from `basic_ios::clear` (which is the function that's actually responsible for throwing `ios_base::failure` based on the mask)?" `bitset`'s `>>` is just an example of a formatted input function. – T.C. Jan 28 '16 at 04:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/101900/discussion-between-jonathan-mee-and-t-c). – Jonathan Mee Jan 28 '16 at 14:04
  • @T.C. I've asked [here](http://stackoverflow.com/questions/35088800/what-effect-would-lwg2349-have) to get more clarification on [LWG2349](http://cplusplus.github.io/LWG/lwg-active.html#2349) if you care to chime in. – Jonathan Mee Jan 29 '16 at 16:02
  • @cpplearner Thank you for that correction. Clearly I didn't test that properly. I can't believe that I cited your answer in mine and didn't upvote you. But since it has come to my attention, here, have an upvote! – Jonathan Mee Jan 30 '18 at 21:34
2

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.

cpplearner
  • 13,776
  • 2
  • 47
  • 72
  • This sounds sound finally. – πάντα ῥεῖ Jan 26 '16 at 18:30
  • It's interesting, [I linked as a duplicate](http://stackoverflow.com/questions/35019687/stdistream-operator-exception-reset-not-thrown/35020134#comment57769093_35019687), and this is the exact passage that [Howard Hinnant uses to defend this bug as invalid](http://stackoverflow.com/a/16473878/2642059). But I don't believe that the bug is invalid. I believe that the solution in [my answer](http://stackoverflow.com/a/35020134/2642059): `if(is.fail()) throw ios_base::failure("basic_ios::clear");` may be the only way to rectify libc++'s misbehavior with libstdc++'s. – Jonathan Mee Jan 26 '16 at 18:57