Search code examples
c++istream

Expected behaviour when failing reading a custom type from an istream


Say I have a class with two data members:

class X {
    std::string a;
    int b;
public:
    ostream& print(ostream& os);
    istream& read(istream& is);
}

The print function outputs all of the data nicely formatted, like so:

ostream& X::print(ostream& os) {
    return os << a << ' ' << b;
}

Now, the read function is supposed to do the inverse: read something in a specified format (say, like horse 54 which would then result in a = "horse" and b = 54).

So say an input does not follow this format. My intuition says that as soon as we encounter an unexpected character (for example a letter when trying to read an int), we set the failbit and put every character we read so far back into the stream. What is the expected behavior of a read function (or operator>>) in this case? How does the standard library behave? Should we be required to undo all extraction of characters if the read fails?


Solution

  • In general in case of failure you're expected to leave any object involved into a valid state. Whether that means rolling back the variable being written to, partially writing to it or anything else depends on what you are trying to accomplish and what you think is going to be best for the user. In any case, be sure to document your choice so that users can program knowing how your implementation will behave.

    As Anton already said, the standard library does not seem to make any effort into putting characters back into the stream. Another practical example, which seems to be closer to what you are doing, is operator>> for the std::complex class, which actually has to read multiple tokens in a sequence before being done.

    template<typename _Tp, typename _CharT, class _Traits>
      basic_istream<_CharT, _Traits>&
      operator>>(basic_istream<_CharT, _Traits>& __is, complex<_Tp>& __x)
      {
        _Tp __re_x, __im_x;
        _CharT __ch;
        __is >> __ch;
        if (__ch == '(') 
      {
        __is >> __re_x >> __ch;
        if (__ch == ',') 
          {
            __is >> __im_x >> __ch;
            if (__ch == ')') 
          __x = complex<_Tp>(__re_x, __im_x);
            else
          __is.setstate(ios_base::failbit);
          }
        else if (__ch == ')') 
          __x = __re_x;
        else
          __is.setstate(ios_base::failbit);
      }
        else 
      {
        __is.putback(__ch);
        __is >> __re_x;
        __x = __re_x;
      }
        return __is;
      }