Why does stringstream >> change value of target on failure?

C++StateStringstreamCin

C++ Problem Overview


From Stroustrup's TC++PL, 3rd Edition, Section 21.3.3:

> If we try to read into a variable v and the operation fails, the value of v should be unchanged (it is unchanged if v is one of the types handled by istream or ostream member functions).

The following example appears to contradict the above quote. Based on the above quote, I was expecting the value of v to remain unchanged -- but it gets zeroed. What's the explanation for this apparent contradictory behaviour?

#include <iostream>
#include <sstream>

int main( )
{
    std::stringstream  ss;

    ss  << "The quick brown fox.";

    int  v = 123;

    std::cout << "Before: " << v << "\n";

    if( ss >> v )
    {
        std::cout << "Strange -- was successful at reading a word into an int!\n";
    }

    std::cout << "After: " << v << "\n";

    if( ss.rdstate() & std::stringstream::eofbit  ) std::cout << "state: eofbit\n";
    if( ss.rdstate() & std::stringstream::failbit ) std::cout << "state: failbit\n";
    if( ss.rdstate() & std::stringstream::badbit  ) std::cout << "state: badbit\n";
    
    return 1;
}

The output I get using x86_64-w64-mingw32-g++.exe (rubenvb-4.7.2-release) 4.7.2 is:

Before: 123
After: 0
state: failbit

Thanks.

C++ Solutions


Solution 1 - C++

From this reference:

> If extraction fails (e.g. if a letter was entered where a digit is expected), value is left unmodified and failbit is set (until C++11) > > If extraction fails, zero is written to value and failbit is set. If extraction results in the value too large or too small to fit in value, std::numeric_limits::max() or std::numeric_limits::min() is written and failbit flag is set. (since C++11)

It seems that your compiler is compiling in C++11 mode, which changes the behavior.


The input operator uses the locale facet std::num_get whose get function invokes do_get. For C++11 it's specified to use std::strtoll et. al. type of functions. Before C++11 it apparently used std::scanf style parsing (going by the reference, I don't have access to the C++03 specification) to extract the numbers. The change in behavior is due to this change in parsing the input.

Solution 2 - C++

The operator >> is a formatted input operator.
As such is dependent on the locale for how input is read from the stream:

##[istream.formatted.arithmetic]

> As in the case of the inserters, these extractors depend on the locale’s num_get<> (22.4.2.1) object to perform parsing the input stream data. These extractors behave as formatted input functions (as described in 27.7.2.2.1). After a sentry object is constructed, the conversion occurs as if performed by the following code fragment:

   typedef num_get< charT,istreambuf_iterator<charT,traits> > numget;
   iostate err = iostate::goodbit;
   use_facet< numget >(loc).get(*this, 0, *this, err, val);
   setstate(err);

As we can see above the value is actually set by the numget facet of the locale imbuded onto the stream.

##num_get virtual functions [facet.num.get.virtuals]

>###Stage 3: >The numeric value to be stored can be one of:

>* zero, if the conversion function fails to convert the entire field. ios_base::failbit is assigned to err.

  • the most positive representable value, if the field represents a value too large positive to be represented in val. ios_base::failbit is assigned to err.
  • the most negative representable value or zero for an unsigned integer type, if the field repre- sents a value too large negative to be represented in val. ios_base::failbit is assigned to err.

The definition of stage 3 changed drastically between n2723 -> n2798

https://stackoverflow.com/questions/81656/where-do-i-find-the-current-c-or-c-standard-documents

num_get virtual functions [facet.num.get.virtuals]

> Stage 3: The result of stage 2 processing can be one of:

>* A sequence of chars has been accumulated in stage 2 that is converted (according to the rules of scanf) to a value of the type of val . This value is stored in val and ios_base::goodbit is stored in err .

  • The sequence of chars accumulated in stage 2 would have caused scanf to report an input failure. ios_base::failbit is assigned to err.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1823664View Question on Stackoverflow
Solution 1 - C++Some programmer dudeView Answer on Stackoverflow
Solution 2 - C++Martin YorkView Answer on Stackoverflow