6

After configuring a std::istringstream to throw exceptions when failbit is set I get no exceptions happening with libc++ (this is under linux with libc++ compiled with support from libcxxrt). I suppose this is a bug in libc++ or libcxxrt:

#include <iostream>
#include <sstream>

template<typename T> std::istream &getvalue(std::istream &is, T &value, const T &default_value = T())
{
    std::stringstream ss;
    std::string s;
    std::getline(is, s, ',');
    ss << s;
    if((ss >> value).fail())
        value = default_value;
    return is;
}

int main()
{
    std::string s = "123,456,789";
    std::istringstream is(s);
    unsigned n;

    try
    {
        is.exceptions(std::ios::failbit | std::ios::eofbit);

        getvalue(is, n);
        std::cout << n << std::endl;

        getvalue(is, n);
        std::cout << n << std::endl;

        // Disable EOF exception on last bit
        is.exceptions(std::ios::failbit);

        getvalue(is, n);
        std::cout << n << std::endl;

        // Force Fail reading after EOF
        getvalue(is, n);
        std::cout << n << std::endl;
    }
    catch(std::ios::failure &fail)
    {
        std::cout << "Fail" << std::endl;
    }
}

output for libstdc++:

123
456
789
Fail

libc++/libcxxrt output:

123
456
789
0

EDIT

Also tested on OS X.

Bug submitted: http://llvm.org/bugs/show_bug.cgi?id=15949

oblitum
  • 11,380
  • 6
  • 54
  • 120

1 Answers1

4

libc++ is responding to 27.7.2.1 [istream]/p4 which describes the basic_istream parse operator>> for unsigned:

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.

If:

is.exceptions(std::ios::failbit | std::ios::badbit);

then the desired behavior is obtained.

123
456
789
Fail

Update

chico rightly pointed out in the comments below that he expected getline(is, s, ',') to throw, not the unsigned extractor.

Looking at 21.4.8.9 [string.io]/p7 which describes this getline:

Effects: Behaves as an unformatted input function (27.7.2.3), except that it does not affect the value returned by subsequent calls to basic_istream<>::gcount(). After constructing a sentry object, if the sentry converts to true, calls str.erase() and then extracts characters from is and appends them to str as if by calling str.append(1, c) until any of the following occurs: ...

So the question becomes:

How does an unformatted input function behave?

27.7.2.3 [istream.unformatted]/p1 says:

Each unformatted input function begins execution by constructing an object of class sentry with the default argument noskipws (second) argument true. If the sentry object returns true, when converted to a value of type bool, the function endeavors to obtain the requested input. Otherwise, if the sentry constructor exits by throwing an exception or if the sentry object returns false, when converted to a value of type bool, the function returns without attempting to obtain any input. In either case the number of extracted characters is set to 0; unformatted input functions taking a character array of non-zero size as an argument shall also store a null character (using charT()) in the first location of the array. If an exception is thrown during input then ios::badbit is turned on315 in *this’s error state. (Exceptions thrown from basic_ios<>::clear() are not caught or rethrown.) If (exceptions()&badbit) != 0 then the exception is rethrown. It also counts the number of characters extracted. If no exception has been thrown it ends by storing the count in a member object and returning the value specified. In any event the sentry object is destroyed before leaving the unformatted input function.

315) This is done without causing an ios::failure to be thrown.

(emphasis added by me for readability purposes)

So this again appears to indicate that if an exception is desired from this parse operation, badbit has to be set in exceptions.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Does "`unsigned`" matter? `std::getline(is, s, ',');` is the line that should throw, it doesn't involve reading into `unsigned`. – oblitum May 10 '13 at 01:50
  • In the end I got the impression, "Why can't it be just that simple? If I wan't to set exceptions for some given flags, just set it for such. But no, I must insert `badbit` out of nowhere..." – oblitum May 10 '13 at 11:43
  • 2
    [This libc++ bug was closed as invalid](https://llvm.org/bugs/show_bug.cgi?id=15949), but I don't believe it is. The reasons that `failbit` is set: http://en.cppreference.com/w/cpp/io/ios_base/iostate#The_failbit differ from the reasons that `badbit` is set: http://en.cppreference.com/w/cpp/io/ios_base/iostate#The_badbit It's completely realistic that I may want to *only* throw if a `failbit` was set and *not* if a `badbit` was set. Because of this but libc++ now requires a painstaking workaround, particularly for compound extraction operators: http://stackoverflow.com/a/35020134/2642059 – Jonathan Mee Jan 26 '16 at 18:42
  • I have linked a question on [LWG2349](http://cplusplus.github.io/LWG/lwg-active.html#2349) in this question's duplicate and I believe it is relevant here as well: http://stackoverflow.com/questions/35088800/what-effect-would-lwg2349-have – Jonathan Mee Jan 29 '16 at 16:03
  • @HowardHinnant: What does the creation of the `basic_istream::sentry` object supposed to do? I don't have a copy of the Standard, but [from](http://en.cppreference.com/w/cpp/concept/UnformattedInputFunction) [cppreference](http://en.cppreference.com/w/cpp/io/basic_istream/sentry) it appears as if the sentry-creation of the fourth `std::getline()` call should have detected that `eofbit` is already set in `is` and therefore should have set `failbit` (and thrown an exception in doing so). – Tanz87 Aug 29 '16 at 14:18
  • @Tanz87: Here's a link to the latest working draft: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf It says that for unformatted functions a sentry should not skip white space, and if the sentry returns false it should set `badbit`. – Howard Hinnant Aug 29 '16 at 15:31
  • @HowardHinnant: Thanks. Note that you said "if the sentry **returns** false ...". If the sentry threw an exception **during its construction** we won't even get to that. In §27.7.2.1.3 paragraph 1 (`sentry` constructor): "_Effects:_ If `is.good()` is `false`, calls `is.setstate(failbit)`." -- which, from §27.5.5.4 paragraph 6 (`basic_ios::setstate` method): "_Effects:_ Calls `clear(rdstate() | state)` (which may throw `basic_ios::failure` (27.5.3.1.1))." – Tanz87 Aug 29 '16 at 15:52
  • Another thing: §27.7.2.3 paragraph 1 (unformatted input functions) says: "If the `sentry` object returns `true`, when converted to a value of type `bool`, the function endeavors to obtain the requested input. Otherwise, if the `sentry` constructor exits by throwing an exception or if the `sentry` object returns `false`, when converted to a value of type `bool`, the function returns without attempting to obtain any input." From this it would seem that `getline` shouldn't throw on `sentry`-construction failure by exception (effectively going against my previous comment) ... **<>** – Tanz87 Aug 29 '16 at 16:23
  • However, in §21.3.2.9 (`getline` for `basic_string`) paragraph 9: "If the function extracts no characters, it calls `is.setstate(ios_base::failbit)` which may throw `ios_base::failure` (27.5.5.4)." -- This bit seems to be an additional requirement on `getline` for `basic_string` (regardless of `sentry`-construction failure or failure in extracting input), beyond what is commonly required for unformatted input functions. – Tanz87 Aug 29 '16 at 16:24
  • 1
    @Tanz87: I recommend filing a bug report here if you believe libc++ is in error: https://llvm.org/bugs/ I am no longer a maintainer of libc++. – Howard Hinnant Aug 29 '16 at 16:34