Show Lecture.IosStateFlags as a slide show.
CS253 Ios State Flags
Overview
- By default, streams do not throw errors when things go bad.
- Though you can enable that if you wish.
- Instead, they maintain several bits (flags)
that indicate the state of the stream.
- Several methods exist which interrogate the stream’s state.
ios State Flags
- An ios (Input/Output Stream) object maintains state.
- Several sorts of things can go wrong, so three different types
of errors are maintained:
ios::eofbit
ios::failbit
ios::badbit
ios::goodbit
is not really a bit—just the absence of the other bits.
- Yes, “fail” and “bad” are quite similar words. Life is difficult.
Summary of flags
ios::eofbit
— End-of-file has been encountered.
ios::failbit
— Open or conversion failed.
ios::badbit
— Something underneath it all went bad.
ios::eofbit
ios::eofbit
is set when end-of-file
is encountered.
- That’s when you try to read, but can’t, because there’s no more data.
It doesn’t predict.
- Let’s try reading from the special file
/dev/null
,
which is always empty.
ifstream in("/dev/null");
if (in.rdstate() & ios::eofbit) cout << "Before, eofbit\n";
if (in.rdstate() & ios::failbit) cout << "Before, failbit\n";
if (in.rdstate() & ios::badbit) cout << "Before, badbit\n";
char c;
in.get(c);
if (in.rdstate() & ios::eofbit) cout << "After, eofbit\n";
if (in.rdstate() & ios::failbit) cout << "After, failbit\n";
if (in.rdstate() & ios::badbit) cout << "After, badbit\n";
After, eofbit
After, failbit
ios::failbit
ios::failbit
is set when an I/O operation fails—you didn’t
get what you wanted.
ios::failbit
was set in the previous example, because we
tried to read a character, and couldn’t.
- Let’s read a
float
from /etc/resolv.conf
,
which contains non-numeric data:
ifstream in("/etc/resolv.conf");
if (in.rdstate() & ios::eofbit) cout << "Before, eofbit\n";
if (in.rdstate() & ios::failbit) cout << "Before, failbit\n";
if (in.rdstate() & ios::badbit) cout << "Before, badbit\n";
float f;
in >> f;
if (in.rdstate() & ios::eofbit) cout << "After, eofbit\n";
if (in.rdstate() & ios::failbit) cout << "After, failbit\n";
if (in.rdstate() & ios::badbit) cout << "After, badbit\n";
After, failbit
ios::failbit
upon open failure
Also, ios::failbit
is set when a file can’t be opened.
ifstream in("/this/file/doesn’t/exist");
if (in.rdstate() & ios::eofbit) cout << "eofbit\n";
if (in.rdstate() & ios::failbit) cout << "failbit\n";
if (in.rdstate() & ios::badbit) cout << "badbit\n";
failbit
ios::badbit
ios::badbit
is not set upon a mere conversion error,
but, rather, when a low-level I/O error occurs:
the system runs out of memory, the disk becomes full, etc.
- Let’s try writing to the special file
/dev/full
, which simulates a
disk that is always full.
ofstream out("/dev/full");
if (out.rdstate() & ios::eofbit) cout << "Before, eofbit\n";
if (out.rdstate() & ios::failbit) cout << "Before, failbit\n";
if (out.rdstate() & ios::badbit) cout << "Before, badbit\n";
out << "foo" << endl;
if (out.rdstate() & ios::eofbit) cout << "After, eofbit\n";
if (out.rdstate() & ios::failbit) cout << "After, failbit\n";
if (out.rdstate() & ios::badbit) cout << "After, badbit\n";
After, badbit
The bits are sticky
ifstream in("/etc/resolv.conf");
double d;
in >> d;
if (in.rdstate() & ios::failbit) cout << "#1: failbit\n";
char c;
in >> c;
if (in.rdstate() & ios::failbit) cout << "#2: failbit\n";
#1: failbit
#2: failbit
- Why doesn’t the second read succeed? Because the bits are sticky.
- Once
ios::failbit
is set, it stays set.
- The second extraction gives up without trying,
because
ios::failbit
is set.
Clearing bits
ifstream in("/etc/resolv.conf");
double d;
in >> d;
if (in.rdstate() & ios::failbit) cout << "#1: failbit\n";
char c;
in >> c;
if (in.rdstate() & ios::failbit) cout << "#2: failbit\n";
in.clear();
in >> c;
if (in.rdstate() & ios::failbit) cout << "#3: failbit\n";
cout << "c=‘" << c << "’\n";
#1: failbit
#2: failbit
c=‘s’
«ios::clear(cpp)» clears ios::badbit
, ios::eofbit
,
and ios::failbit
.
Interrogating bits
The previous slides have been interrogating the bits in a straightforward,
but clumsy fashion. There are several other ways:
How | Results |
stream.rdstate() | ios::eofbit|ios::badbit|ios::failbit |
stream.good() | no bits are set |
stream.eof() | ios::eofbit is set |
stream.bad() | ios::badbit is set |
stream.fail() | ios::badbit or ios::failbit is set 😲 🤯 |
! stream | .fail() |
stream in boolean context | ! .fail() |
Advice
I generally only use boolean context, and occasionally .eof()
when
I want to be rigorous. I also avoid .is_open()
:
ifstream in("/etc/resolv.conf");
if (!in) {
cerr << "Can’t open /etc/resolv.conf for reading\n";
return 1;
}
for (float f; in >> f; )
cout << "Hooray, got a number: " << f << '\n';
// All done reading. Why did we stop?
if (!in.eof())
cerr << "Error!\n";
Error!
Why not just use .fail()
?