Извлечение std :: istream устанавливает failbit без видимой причины - PullRequest
1 голос
/ 08 сентября 2011

Я создаю оболочку примитивного типа, которая может использовать boost :: lexical_cast для установки его значения из строки.Работает нормально, но по какой-то причине оператор извлечения std :: istream устанавливает бит битов.Следующая программа печатает:

123.45
ИСКЛЮЧЕНИЕ: ios_base :: failbit set

Но если вы закомментируете строку "inStream.exceptions (...", она работает и печатает:

123.45
123.45

Не имеет значения, если вы компилируете с юникодом или нет, или если вы используете int или float в качестве ValueType, битовый бит устанавливается в любом случае.

#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;
}

Почему std :: istream считает, что что-то пошло не так?

1 Ответ

2 голосов
/ 08 сентября 2011

Одной из причин этого может быть то, что реализация lexical_cast может намеренно попытаться вызвать сбой какого-либо потока, чтобы проверить, что весь входной текст был использован. Например, наивная реализация может выглядеть так:

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

Идея состоит в том, что последняя проверка пытается разорвать поток, чтобы увидеть, осталось ли что-то еще. Если вы включите исключения, это превратит что-то, что должно было быть нормальной ошибкой потока, обнаруживаемой с помощью failbit, в исключение, чего код не ожидал.

В более общем смысле вы не должны устанавливать параметры потока внутри процедуры извлечения. Это зависит от звонящего. В противном случае, независимо от того, что вы пытаетесь сделать со своим потоком перед вызовом процедуры извлечения, подпрограмма переопределит ваши предпочтения. В конце концов, было бы плохо, если бы я явно отключил исключения, а затем все равно возникали исключения, потому что вы включили их обратно в operator >>.

Надеюсь, это поможет!

...