8.2. Condition States
Before we explore the types defined in fstream and sstream, we need to understand a bit more about how the IO library manages its buffers and the state of a stream. Keep in mind that the material we cover in this section and the next applies equally to plain streams, file streams, or string streams.Inherent in doing IO is the fact that errors can occur. Some errors are recoverable; others occur deep within the system and are beyond the scope of a program to correct. The IO library manages a set of condition state members that indicate whether a given IO object is in a usable state or has encountered a particular kind of error. The library also defines a set of functions and flags, listed in Table 8.2, that give us access to and let us manipulate the state of each stream.
Table 8.2. IO Library Condition State
strm::iostateName of the machine-dependent integral type, defined by each iostream class that is used to define the condition states.strm::badbitstrm::iostate value used to indicate that a stream is corrupted.strm::failbitstrm::iostate value used to indicate that an IO operation failed.strm::eofbitstrm::iostate value used to indicate the a stream hit end-of-file.s.eof()true if eofbit in the stream s is set.s.fail()true if failbit in the stream s is set.s.bad()TRue if badbit in the stream s is set.s.good()true if the stream s is in a valid state.s.clear()Reset all condition values in the stream s to valid state.s.clear(flag)Set specified condition state(s) in s to valid. Type of flag is strm::iostate.s.setstate(flag)Add specified condition to s. Type of flag is strm::iostate.s.rdstate()Returns current condition of s as an strm::iostate value.As an example of an IO error, consider the following code:If we enter Borges on the standard input, then cin will be put in an error state following the unsuccessful attempt to read a string of characters as an int. Similarly, cin will be in an error state if we enter an end-of-file. Had we entered 1024, then the read would be successful and cin would be in a good, non-error state.To be used for input or output, a stream must be in a non-error state. The easiest way to test whether a stream is okay is to test its truth value:
int ival;
cin >> ival;
The if directly tests the state of the stream. The while does so indirectly by testing the stream returned from the expression in the condition. If that input operation succeeds, then the condition tests true.
if (cin)
// ok to use cin, it is in a valid state
while (cin >> word)
// ok: read operation successful ...
Condition States
Many programs need only know whether a stream is valid. Other programs need more fine-grained access to and control of the state of the stream. Rather than Section 5.3.1 (p. 156).Each IO class also defines three const values of type iostate that represent particular bit patterns. These const values are used to indicate particular kinds of IO conditions. They can be used with the bitwise operators (Section 5.3, p. 154) to test or set multiple flags in one operation.The badbit indicates a system level failure, such as an unrecoverable read or write error. It is usually not possible to continue using a stream after such an error. The failbit is set after a recoverable error, such as reading a character when numeric data was expected. It is often possible to correct the problem that caused the failbit to be set. The eofbit is set when an end-of-file is encountered. Hitting end-of-file also sets the failbit.The state of the stream is revealed by the bad, fail, eof, and good operations. If any of bad, fail, or eof are true, then testing the stream itself will indicate that the stream is in an error state. Similarly, the good operation returns TRue if none of the other conditions is true.The clear and setstate operations change the state of the condition member. The clear operations put the condition back in its valid state. They are called after we have remedied whatever problem occurred and we want to reset the stream to its valid state. The setstate operation turns on the specified condition to indicate that a problem occurred. setstate leaves the existing state variables unchanged except that it adds the additional indicated state(s).
Interrogating and Controlling the State of a Stream
We might manage an input operation as follows:
This loop reads cin until end-of-file or an unrecoverable read error occurs. The condition uses a comma operator (Section 5.9, p. 168). Recall that the comma operator executes by evaluating each operand and returns its rightmost operand as Section 6.13, p. 215). If the input was invalid, we print a warning, and clear the failbit state. In this case, we execute a continue (Section 6.11, p. 214) to return to the start of the while to read another value into ival. If there were no errors, the rest of the loop can safely use ival.
int ival;
// read cin and test only for EOF; loop is executed even if there are other IO failures
while (cin >> ival, !cin.eof()) {
if (cin.bad()) // input stream is corrupted; bail out
throw runtime_error("IO stream corrupted");
if (cin.fail()) { // bad input
cerr<< "bad data, try again"; // warn the user
cin.clear(istream::failbit); // reset the stream
continue; // get next input
}
// ok to process ival
}
Accessing the Condition State
The rdstate member function returns an iostate value that corresponds to the entire current condition state of the stream:
// remember current state of cin
istream::iostate old_state = cin.rdstate();
cin.clear();
process_input(); // use cin
cin.clear(old_state); // now reset cin to old state
Dealing with Multiple States
Often we need to set or clear multiple state bits. We could do so by making multiple calls to the setstate or clear functions. Alternatively, we could use the bitwise OR (Section 5.3, p. 154) operator to generate a value to pass two or more state bits in a single call. The bitwise OR generates an integral value using the bit patterns of its operands. For each bit in the result, the bit is 1 if the corresponding bit is 1 in either of its operands. For example:
tells the object is to turn on both the failbit and the badbit. The argument
// sets both the badbit and the failbit
is.setstate(ifstream::badbit | ifstream::failbit);
creates a value in which the bits corresponding to the badbit and to the failbit are both turned onthat is they are both set to 1. All other bits in the value are zero. The call to setstate uses this value to turn on the bits corresponding to badbit and failbit in the stream's condition state member.
is.badbit | is.failbit