Expected behavior when reading custom type from istream fails

Let's 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 function print

outputs all data well formatted, for example:

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

      

Now the function read

should do the opposite: read something in the specified format (say how horse 54

, which will then result in a = "horse"

and b = 54

).

That is to say, the input does not fit this format. My intuition says that as soon as we encounter an unexpected character (like a letter while trying to read int

), we set failbit

and put every character we read so far into the stream. What is the expected behavior of the function read

(or operator>>

) in this case? How does the standard library work? Should we need to undo all character extraction if the read fails?

+3


source to share


2 answers


In general, in the event of a failure, you expect any object to be invoked in a valid state. Whether it refers to rolling back from the variable being written, partially writing it, or something else depends on what you are trying to accomplish and what you think is best for the user. In any case, be sure to document your choices so that users can program knowing how your implementation will behave.

As Anton already said, the standard library doesn't seem to make any effort to translate characters into a stream. Another practical example that seems to be closer to what you are doing is operator>>

for a class std::complex

that actually needs to read multiple tokens in sequence before they are executed.



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

      

+1


source


The standard library never tries to put characters on the stream on error. For example, here's an excerpt from the standard on how to short

read from stream ( 27.7.2.2.2/2

):

operator>>(short& val);

      

The conversion happens as if it were done by the following piece of code (using the same notation as for the previous piece of code):



typedef num_get<charT,istreambuf_iterator<charT,traits> > numget;
iostate err = ios_base::goodbit;
long lval;
use_facet<numget>(loc).get(*this, 0, *this, err, lval);
if (lval < numeric_limits<short>::min()) {
    err |= ios_base::failbit;
    val = numeric_limits<short>::min();
} else if (numeric_limits<short>::max() < lval) {
    err |= ios_base::failbit;
    val = numeric_limits<short>::max();
} else
    val = static_cast<short>(lval);
setstate(err);

      

You can see that even if the number does not fit in short

, nothing is returned.

+1


source







All Articles