Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
The Date extractor shown earlier sets a stream s fail
bit under certain conditions. How does the user know when such a failure
occurs? You can detect stream errors by either calling certain stream member
functions to see if an error state has occurred, or if you don t care what the
particular error was, you can just evaluate the stream in a Boolean context.
Both techniques derive from the state of a stream s error bits.
Stream state
The ios_base class, from which ios derives, defines four
flags that you can use to test the state of a stream:
Flag
|
Meaning
|
badbit
|
Some fatal (perhaps physical) error occurred. The stream
should be considered unusable.
|
eofbit
|
End-of-input has occurred (either by encountering the
physical end of a file stream or by the user terminating a console stream,
such as with Ctrl-Z or Ctrl‑D).
|
failbit
|
An I/O operation failed, most likely because of invalid
data (e.g., letters were found when trying to read a number). The stream is
still usable. The failbit flag is also set when end-of-input occurs.
|
goodbit
|
All is well; no errors. End-of-input has not yet occurred.
|
You can test whether any of these conditions have occurred
by calling corresponding member functions that return a Boolean value
indicating whether any of these have been set. The good( ) stream
member function returns true if none of the other three bits are set. The eof( )
function returns true if eofbit is set, which happens with an attempt to
read from a stream that has no more data (usually a file). Because end-of-input
happens in C++ when trying to read past the end of the physical medium, failbit
is also set to indicate that the expected data was not successfully read. The
fail( ) function returns true if either failbit or badbit
is set, and bad( ) returns true only if the badbit is set.
Once any of the error bits in a stream s state are set, they
remain set, which is not always what you want. When reading a file, you might
want to reposition to an earlier place in the file before end-of-file occurred.
Just moving the file pointer doesn t automatically reset eofbit or failbit;
you must do it yourself with the clear( ) function, like this:
myStream.clear(); // Clears all error bits
After calling clear( ), good( ) will
return true if called immediately. As you saw in the Date
extractor earlier, the setstate( ) function sets the bits you pass
it. It turns out that setstate( ) doesn t affect any other bits if
they re already set, they stay set. If you want to set certain bits but at the
same time reset all the rest, you can call an overloaded version of clear( ),
passing it a bitwise expression representing the bits you want to set, as in:
myStream.clear(ios::failbit | ios::eofbit);
Most of the time you won t be interested in checking the
stream state bits individually. Usually you just want to know if everything is
okay. This is the case when you read a file from beginning to end; you just
want to know when the input data is exhausted. You can use a conversion function
defined for void* that is automatically called when a stream occurs in a
Boolean expression. Reading a stream until end-of-input using this idiom looks
like the following:
int i;
while(myStream >> i)
cout << i <<
endl;
Remember that operator>>( ) returns its
stream argument, so the while statement above tests the stream as a
Boolean expression. This particular example assumes that the input stream myStream
contains integers separated by white space. The function ios_base::operator
void*( ) simply calls good( ) on its stream and returns
the result. Because
most stream operations return their stream, using this idiom is convenient.
Streams and exceptions
Iostreams existed as part of C++ long before there were
exceptions, so checking stream state manually was just the way things were done.
For backward compatibility, this is still the status quo, but modern iostreams
can throw exceptions instead. The exceptions( ) stream member
function takes a parameter representing the state bits for which you want
exceptions to be thrown. Whenever the stream encounters such a state, it throws
an exception of type std::ios_base::failure, which inherits from std::exception.
Although you can trigger a failure exception for any of the
four stream states, it s not necessarily a good idea to enable exceptions for
all of them. As Chapter 1 explains, use exceptions for truly exceptional
conditions, but end-of-file is not only not exceptional it s expected!
For that reason, you might want to enable exceptions only for the errors
represented by badbit, which you would do like this:
myStream.exceptions(ios::badbit);
You enable exceptions on a stream-by-stream basis, since exceptions( )
is a member function for streams. The exceptions( ) function
returns a bitmask (of
type iostate, which is some compiler-dependent type convertible to int)
indicating which stream states will cause exceptions. If those states have
already been set, an exception is thrown immediately. Of course, if you use
exceptions in connection with streams, you had better be ready to catch them,
which means that you need to wrap all stream processing with a try block
that has an ios::failure handler. Many programmers find this tedious and
just check states manually where they expect errors to occur (since, for
example, they don t expect bad( ) to return true most of the
time anyway). This is another reason that having streams throw exceptions is
optional and not the default. In any case, you can choose how you want to
handle stream errors. For the same reasons that we recommend using exceptions
for error handling in other contexts, we do so here.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |