8.2. Condition StatesBefore 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.
As an example of an IO error, consider the following code: int ival; cin >> ival; 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: if (cin) // ok to use cin, it is in a valid state while (cin >> word) // ok: read operation successful ... 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. Condition StatesMany 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 knowing that the stream is in an error state, we might want to know what kind of error was encountered. For example, we might want to distinguish between reaching end-of-file and encountering an error on the IO device. Each stream object contains a condition state member that is managed through the setstate and clear operations. This state member has type iostate, which is a machine-dependent integral type defined by each iostream class. It is used as a collection of bits, much the way we used the int_quiz1 variable to represent test scores in the example in 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 StreamWe might manage an input operation as follows: 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 } 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 its result. The condition, therefore, reads cin and ignores its result. The result of the condition is the result of !cin.eof(). If cin hit end-of-file, the condition is false and we fall out of the loop. If cin did not hit end-of-file, we enter the loop, regardless of any other error the read might have encountered. Inside the loop, we first check whether the stream is corrupted. If so, we exit by throwing an exception (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. Accessing the Condition StateThe 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 StatesOften 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: // sets both the badbit and the failbit is.setstate(ifstream::badbit | ifstream::failbit); tells the object is to turn on both the failbit and the badbit. The argument is.badbit | is.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. ![]() |