8

In C++ primer I found this code:

 if (cin.fail())
 { // bad input
      cerr<< "bad data, try again"; // warn the user
      cin.clear(istream::failbit); // reset the stream
      continue; // get next input
 }

I am confused that why the istream::failbit is used to set the error state flag, I mean that since the error has occurred (hence the flow is right now in if block then the failbit must be set, they why use it to set the error flag with that again. Where am I wrong in understanding this?

EDIT: The book says "We print a warning and clear the failbit state", but IMO clear(istream::failbit) is setting the current state of stream with the value contained in failbit. So why the book is setting the stream's state with that of failbit as it will stop cin from functioning as it will be in error state. ++++ By state of a stream, what bit actually is being talked about, is it eofbit, badbit, goodbit, failbit or a combination of them? How can I know the value of these individual bit as well?

Gaurav
  • 201
  • 1
  • 11
  • cin.clear(istream::failbit) clears the failbit. It does not set it. – cup Aug 12 '17 at 08:17
  • 2
    @cup read the reference/doc: http://www.cplusplus.com/reference/ios/ios/clear/ – Gaurav Aug 12 '17 at 08:24
  • @Swift refer this: http://en.cppreference.com/w/cpp/io/basic_ios/clear – Gaurav Aug 12 '17 at 08:32
  • I've updated answer, but I do not understand how you miss the point of a state field and constants used to represent its possible states. This is pretty common pattern to store and access flags, the reference on `failbit` redirects to `iostate` and contained comprehensive state table – Swift - Friday Pie Aug 14 '17 at 19:13
  • 3
    Looks like an error in the book. See any reference on what "clear" does or https://stackoverflow.com/a/11247530/273767 – Cubbi Aug 15 '17 at 23:33
  • @Cubbi if continue actually returns to `cin >> ...` it is an error, because stream will not be a able to read until iostate would be `good()`. either author of primer never tested code (which is true in case of many primers, especially online ones) or it was taken from more complex example. Comments suggest former – Swift - Friday Pie Aug 16 '17 at 04:41

1 Answers1

1

std::basic_ios::clear

void clear( std::ios_base::iostate state = std::ios_base::goodbit ); 

Sets the stream error state flags by assigning them the value of state. By default, assigns std::ios_base::goodbit which has the effect of clearing all error state flags.

If rdbuf() is a null pointer (i.e. there is no associated stream buffer), then state | badbit is assigned. May throw an exception.

Essentially in this case to set bit means that it sets bit to clear state.

If you call clear without parameters, it sets all bits to clear state, by setting "goodbit", which is exclusive with other states. If you mark only certain bit, only that bit will will be set, clearing other bits ( and good bit as well). Anyway, as said above, if during call of this method input buffer of stream is not valid, then clear() also sets badbit to true, so method good() and operator bool will return false and fail() will still return true.

To wit, why one need to clear those bits but keep a error state is depends on further code, often it is to be able to detect that error happened , but being able to request more data from stream (ask for correct input?)

#include <iostream>
#include <limits> 
#include <string> 
int main() {
    using std::cout;
    using std::cin;

    int a;
    do
    {
        cout << " Please enter an integer number:";
        cin.clear();
        cin >> a;
        if(cin.fail())
        {
            cout << "Error occured while parsing input.\n";
            cin.clear(std::istream::failbit);
        }


        // do something

        if(cin.fail())
        {
            std::string str;
                    //now clear everything,  to unlock the input.
            cin.clear(); 
            cin >> str;
            cout << "Wrong input was: " << str << "\n";
            cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                    // setting fail bit again, so   loop will go on
            cin.clear(std::istream::failbit);
        }

    } while(cin.fail());
    cout << "Thank you!";
}

Without calling ::clear(std::istream::failbit) and ::ignore the loop would be working forever, because state of the flags and buffer would force an attempt to parse same buffer content over and over. Actually, a that point you may try to reparse it , e.g. read the string and print it. It would be ok to call just clear() but then we need to create own flag that would allow us to react correctly.

The "state" of stream is a private field of type std::ios_base::iostate, which value is equal to a binary combination of it eofbit, badbit, and failbit constants. goodbit constant is equal to zero and represents no-error state. Two accessors that provide read and write operations to this field:

void setstate( iostate state );
iostate rdstate() const;

Note, setstate(state) got effect of clear(rdstate() | state), which means that if clear can set exact value of iostate, setstate can only set new bits to true, but can't clear bits that already set.

int main()
{
  std::ostringstream stream;

  if (stream.rdstate() == std::ios_base::goodbit) {
    std::cout << "stream state is goodbit\n";
  }

  stream.setstate(std::ios_base::eofbit);

  // check state is exactly eofbit (no failbit and no badbit)
  if (stream.rdstate() == std::ios_base::eofbit) {
    std::cout << "stream state is eofbit\n";
  }
}

for each bit there are accessors: fail(), bad(), eof(), good(). Essentially, fail() returns true if (rdstate()|std::ios_base::failbit) != 0, and so on (See 30.5.5.4 basic_ios flags functions, ISO/IEC 14882:2017, Programming Languages — C++)

  • operator bool is defined and returns good()
  • operator! is defined and returns !good()

The line

if (stream.rdstate() == std::ios_base::goodbit)

can be replaced by

if (stream)

because the latter results in contextual conversion to bool.

Effects, associated with iostate's bits (according to ISO C++):

  • badbit indicates a loss of integrity in an input or output sequence (such as an irrecoverable read error from a file);
  • eofbit indicates that an input operation reached the end of an input sequence;
  • failbit indicates that an input operation failed to read the expected characters, or that an output operation failed to generate the desired characters.
Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • Okay now I understand it - so one can clear some specific bit (be it any of these : failbit, goodbit, eofbit, badbit) and then set it - by specifically mentioning the name of that bit/flag as argument of clear(). But how does one clear the flag and keep there error state? – Gaurav Aug 12 '17 at 09:13
  • @Gaurav added example, I suspect there is something similar in code of example you was looking at – Swift - Friday Pie Aug 12 '17 at 09:34
  • 1
    just one more thing - when I am printing the value of flagbit then what value is being printed - decimal value corresponding to the bit value? – Gaurav Aug 12 '17 at 16:56
  • @Gaurav do you mean the value returned by `state`? then yes, it's result of bitwise OR of bit constants you can use ( `badbit` , `failbit` and `eofbit`) their values are implementation defined, `goodbit` always defined as 0. – Swift - Friday Pie Aug 12 '17 at 17:02
  • on printing cin.eofbit outputs 2, cin.failbit outputs 4 & cin.goodbit outputs 0 when there is no error But when I introduce the error - input-ing char when cin expects int the failbit prints the same value 4 after that like it's not been affected. #include using namespace std; int main() { // your code goes here int I; cout<>I, cin.fail()) { cout< – Gaurav Aug 12 '17 at 18:47
  • 1
    @Gauarev those are constants! `std::istream::failbit` is a static member constant that inherited by all input classes. the actual state is returned by method `rdstate()`, each bit of it returned by those accessors – Swift - Friday Pie Aug 12 '17 at 19:07