Title
istream::operator>>(int&) broken
Status
c++11
Section
[istream.formatted.arithmetic]
Submitter
Martin Sebor

Created on 2007-06-23.00:00:00 last changed 161 months ago

Messages

Date: 2010-10-21.18:28:33

Proposed resolution:

Change [facet.num.get.virtuals], p1:

-1- Effects: Reads characters from in, interpreting them according to str.flags(), use_facet<ctype<charT> >(loc), and use_facet< numpunct<charT> >(loc), where loc is str.getloc(). If an error occurs, val is unchanged; otherwise it is set to the resulting value.

Change [istream.formatted.arithmetic], p2 and p3:

operator>>(short& val);

-2- The conversion occurs as if performed by the following code fragment (using the same notation as for the preceding code fragment):

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

-3- The conversion occurs as if performed by the following code fragment (using the same notation as for the preceding code fragment):

typedef num_get<charT,istreambuf_iterator<charT,traits> > numget;
iostate err = iostate_base::goodbit;
long lval;
use_facet<numget>(loc).get(*this, 0, *this, err, lval);
if (err != 0)
  ;
else if (lval < numeric_limits<int>::min()
  || numeric_limits<int>::max() < lval)
     err = ios_base::failbit;
if (lval < numeric_limits<int>::min())
{
  err |= ios_base::failbit;
  val = numeric_limits<int>::min();
}
else if (lval > numeric_limits<int>::max())
{
  err |= ios_base::failbit;
  val = numeric_limits<int>::max();
}
else
  val = static_cast<int>(lval);
setstate(err);
Date: 2010-10-21.18:28:33

[ 2009-07 Frankfurt ]

Move to Ready.

Date: 2009-05-28.00:00:00

[ 2009-05-28 Howard adds: ]

I've moved this issue from Tentatively NAD to Open.

The current wording of N2857 in [facet.num.get.virtuals] p3, stage 3 appears to indicate that in parsing arithmetic types, the value is always set, but sometimes in addition to setting failbit.

  • If there is a range error, the value is set to min or max, else
  • if there is a conversion error, the value is set to 0, else
  • if there is a grouping error, the value is set to whatever it would be if grouping were ignored, else
  • the value is set to its error-free result.

However there is a contradictory sentence in [facet.num.get.virtuals] p1.

[istream.formatted.arithmetic] should mimic the behavior of [facet.num.get.virtuals] (whatever we decide that behavior is) for int and short, and currently does not. I believe that the correct code fragment should look like:

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<int>::min())
{
  err |= ios_base::failbit;
  val = numeric_limits<int>::min();
}
else if (lval > numeric_limits<int>::max())
{
  err |= ios_base::failbit;
  val = numeric_limits<int>::max();
}
else
  val = static_cast<int>(lval);
setstate(err);
Date: 2010-10-21.18:28:33

[ Batavia (2009-05): ]

We believe this part of the Standard has been recently adjusted and that this issue was addressed during that rewrite.

Move to NAD.

Date: 2007-06-23.00:00:00

From message c++std-lib-17897:

The code shown in [istream.formatted.arithmetic] as the "as if" implementation of the two arithmetic extractors that don't have a corresponding num_get interface (i.e., the short and int overloads) is subtly buggy in how it deals with EOF, overflow, and other similar conditions (in addition to containing a few typos).

One problem is that if num_get::get() reaches the EOF after reading in an otherwise valid value that exceeds the limits of the narrower type (but not LONG_MIN or LONG_MAX), it will set err to eofbit. Because of the if condition testing for (err == 0), the extractor won't set failbit (and presumably, return a bogus value to the caller).

Another problem with the code is that it never actually sets the argument to the extracted value. It can't happen after the call to setstate() since the function may throw, so we need to show when and how it's done (we can't just punt as say: "it happens afterwards"). However, it turns out that showing how it's done isn't quite so easy since the argument is normally left unchanged by the facet on error except when the error is due to a misplaced thousands separator, which causes failbit to be set but doesn't prevent the facet from storing the value.

History
Date User Action Args
2011-08-23 20:07:26adminsetstatus: wp -> c++11
2010-10-21 18:28:33adminsetmessages: + msg3466
2010-10-21 18:28:33adminsetmessages: + msg3465
2010-10-21 18:28:33adminsetmessages: + msg3464
2010-10-21 18:28:33adminsetmessages: + msg3463
2007-06-23 00:00:00admincreate