Visual Studio 2019: Strange behavior in c++ stream implementation

111 views Asked by At

Consider the following simple streambuf implementation:

#include <iostream>
#include <sstream>

class mystreambuf : public std::streambuf
{
    int_type underflow() override
    {
        // [27.6.3.4.5/6] ... 'or throws an exception if the function fails'
        throw std::runtime_error("here");
    }
};

int main()
{
    mystreambuf rdbuf;
    std::ostringstream os;
    std::cout << "State: " << os.exceptions() << std::endl;
    try
    {
        os << &rdbuf;
        std::cout << "No except: " << os.good() << std::endl;
    }
    catch (std::runtime_error const&)
    {
        std::cout << "With except: " << os.good() << std::endl;
    }

    return 0;
}

The above implementation seems to be following my understanding of other posts:

However I am seeing different behaviors. With gcc/linux I get:

State: 0
No except: 0

On Visual Studio 2019 I get:

State: 0
With except: 0

Which behavior is the correct one ? Both ?

For reference:

2

There are 2 answers

1
Jerry Coffin On

What you're really supposed to throw in a case like this is an object fo the class std::ios_base::failure, or some other class derived from it.

When/if you do that:

class mystreambuf : public std::streambuf
{
    int_type underflow() override
    {
        throw std::ios_base::failure("here");
    }
};

// remainder as in question

...g++ has the same behavior as MS VC++:

State: 0
No except: 0

It seems pretty clear to me that the real intent is that the exception you throw be an instance of std::ios_base::failure or a derived class, but it's much less clear whether you're entitled to throw other exceptions and still expect the same behavior. In fact, the only mention I can find out underflow() exiting with an error is in a non-normative footnote that reads:

  1. underflow or uflow can fail by throwing an exception prematurely. The intention is not only that the calls will not return eof() but that they will return “immediately”.

This doesn't provide much indication in either direction. It doesn't specifically restrict the exception to failure, but it doesn't specifically say it can be something else. And it's a non-normative footnote, so even if it did say that would only reveal the author's intent, not an actual requirement on the implementation.

Most of the rest of the text deals with what happens if an exception is thrown during execution of an input or output function (in which case, a sentry object is created before starting the operation, and there's clear requirements for re-throwing the exception if badbit is set).

But your code throws before it ever gets as far as trying to read input from the stream. In that circumstance, it's not clear whether the exception should be re-thrown or not.

Summary

It seems pretty clear to me that both VC++ and g++ have sort of an intent that you should normally expect the behavior you're getting from VC++. But I don't see any requirement that you'll actually get that behavior under these circumstances. So I'd say they're both correct, but purely as a quality of implementation issue, VC++'s behavior is probably preferable.

0
malat On

This has been acknowledged as defect in VS2022:

and fix is at: