开发者

std::istream extraction sets failbit for no apparent reason

I'm creating a primitive type wrapper, which can use boost::lexical_cast for setting its value from a string. It works fine, but for some reason std::istream extraction operator sets the failbit. The following program prints:

123.45

EXCEPTION: ios_base::failbit set

But if you comment out the line "inStream.exceptions( ..." it works and prints:

123.45

123.45

It doesn't ma开发者_C百科ke a difference if you compile with unicode or not, or if you use int or float as ValueType, the failbit gets set in any case.

#include <conio.h>
#include <exception>
#include <iostream>
#include <string>
#include <tchar.h>

#include <boost/lexical_cast.hpp>

#if defined(UNICODE) || defined(_UNICODE)
    typedef std::wstring    StringType;
    typedef std::wistream   IStreamType;
#else
    typedef std::string     StringType;
    typedef std::istream    IStreamType;
#endif


#if 1 // Use float
    typedef float           ValueType;
    #define VALUE_STRING    _T("123.45")
#else // Use int
    typedef int             ValueType;
    #define VALUE_STRING    _T("123")
#endif


struct Castable {
    ValueType m_val;
};

inline IStreamType& operator>> ( IStreamType& inStream, Castable& castable )
{
    inStream.exceptions( IStreamType::failbit | IStreamType::badbit );
    inStream >> castable.m_val;
    return inStream;
}


int _tmain(int argc, _TCHAR* argv[])
{
    try{
        StringType sVal = VALUE_STRING;

        ValueType val;
        val = boost::lexical_cast<ValueType>(sVal);
        std::cout << val << std::endl;

        Castable cst;
        cst = boost::lexical_cast<Castable>(sVal);
        std::cout << cst.m_val << std::endl;

    }catch( std::exception& ex ){
        std::cout << "EXCEPTION: " << ex.what() << std::endl;
    }

    _getch();
    return 0;
}

Why would std::istream think something has gone wrong?


One reason for this might be that the implementation of lexical_cast might deliberately try to cause some stream to fail in order to check that all of the input text was consumed. For example, a naive implementation might look like this:

template <typename Target>
    Target lexical_cast(const string& s) {
    /* Insert the string into a stringstream to use extraction. */
    std::stringstream converter(s);

    /* Pull out an object of type Target, failing if we can't. */
    Target result;
    if (!(converter >> result)) throw bad_lexical_cast();

    /* To confirm that we read everything out of the stream, try pulling out a 
     * single character.  If we can do this, then there is something left in the
     * stream that wasn't picked up earlier and the input was malformed.
     */
    char ch;
    if (converter >> ch) throw bad_lexical_cast();

    return result;
}

The idea here is that the final check tries to break the stream to see if something was left over. If you enable exceptions, this will turn something that should have been a normal stream failure detectable with failbit into an exception, which is something the code didn't expect.

More generally, though, you shouldn't be setting stream settings inside of an extraction routine. That's up to the caller to do. Otherwise, no matter what you try to do with your stream before calling the extraction routine, the routine will override your preferences. It would be bad, after all, if I explicitly disabled exceptions and then had exceptions happen anyway because you turned them back on inside operator >>.

Hope this helps!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜