Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clarification regarding use of flagbit to set internal error flag

Tags:

c++

io

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?

like image 300
Gaurav Avatar asked Aug 12 '17 08:08

Gaurav


1 Answers

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.
like image 54
Swift - Friday Pie Avatar answered Sep 21 '22 21:09

Swift - Friday Pie