Handling stream errors

Error bits

Errors can happen during operations on stream objects. Stream objects possess a byte from which 4 bits are used to indicate errors. The bitmasks of those errors are std::ios::failbit, std::ios::badbit and std::ios::eofbit.

Note that the error bits are also called error flags.

The class std::ios

The classes std::ostream and std::istream both inherit from the class std::ios. This is from that class they inherit the member variable containing the error bits, and the methods to handle them.

Checking if the state of the stream object is error free

The method bool std::ios::good() const returns true only if none of the error bits/flags of the stream object it is called from is set, otherwise, false.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream> void printState(const std::ios &stream) { std::cout << (stream.good() ? "The stream is error free.\n" : "At least one of the error flags of the stream is set.\n"); } int main() { printState(std::cin); float n; std::cin >> n; printState(std::cin); return 0; }

In the example above, the input stream object std::cin tries to convert the string "abc" to a number of type float, but it fails so it sets its fail bit. The method std::ios::good, called from std::cin, then returns false as one of its error flags is set.

Checking the state of the EOF bit

The method bool std::ios::eof() const returns true if the eof bit is set, false otherwise. Note that the eof bit of stream objects linked to a console is never set as when the end of the stream is reached, they wait for the user to enter more data instead of failing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream> #include <fstream> void printEofBitState(const std::ios &stream) { std::cout << (stream.eof() ? "The eof bit is set.\n" : "The eof bit is unset.\n"); } int main() { std::ifstream file("file.txt"); std::string str; printEofBitState(file); file >> str; printEofBitState(file); file >> str; printEofBitState(file); return 0; }

Above, the eof bit of the file stream object is set after the first input operation. The input stream operator, with streams objects from the C++ Standard Library, reads a word until a blank space is reached (Example: space, newline character, tabulation...) Since it did not reached any blank character, after the string "Moon", it tried to read the following character, but the end of the file was already reached, so the operation failed and the eof bit was set. The result of the operation is that the string "Moon" is read and the eof bit of the stream object is set.

If there was a space, following "Moon", in the file "file.txt", the eof bit would have been set only after the second input operation:

The reason is that the first input stream operation would have stopped at the space instead of trying to read a character after the end of the file is reached.

Checking the state of the BAD bit

The method bool std::ios::bad() const returns true if the bad bit is set, false otherwise.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream> #include <fstream> int main() { std::ifstream file("file.txt"); std::cout << (file .bad() ? "The bad bit is set.\n" : "The bad bit is unset.\n"); return 0; }

Checking the state of the FAIL bit

The method bool std::ios::fail() const returns true if either the bad or fail bit is set, false otherwise.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream> #include <fstream> void printFailBitState(const std::ios &stream) { std::cout << (stream.fail() ? "The fail bit is set.\n" : "The fail bit is unset.\n"); } int main() { printFailBitState(std::cin); float n; std::cin >> n; printFailBitState(std::cin); return 0; }

Stream objects as booleans

We can directly put an object of type std::ios as condition inside a control structure. It equals true if neither of its fail or bad bit is set, false otherwise.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream> #include <fstream> int main() { std::ifstream file("fileThatDoesNotExist.txt"); if(file) // If neither the bad of fail bit is set. std::cout << "The file was successfully opened.\n"; else std::cout << "The file could not be opened.\n"; return 0; }

Retrieving the error flags

The method std::ios::iostate std::ios::rdstate() const returns a value of type std::ios::iostate containing the current state of the error flags of the stream it is called from. We can retrieve/change the state of the individual error bits by using bitwise operators with the masks std::ios::eofbit, std::ios::failbit and std::ios::badbit, which are constants of type std::ios::iostate. If the variable containing the error flags equals 0, that means that no error bit is set. The constant std::ios::goodbit equals 0.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream> int main() { // Retrieves the error flags of the stream object 'std::cin'. std::ios::iostate state = std::cin.rdstate(); // Checks if no error bit is set. if(state == std::ios::goodbit) std::cout << "No error.\n"; else std::cout << "The good bit is not set.\n"; float n; std::cin >> n; // Retrieves the error flags of the stream object 'std::cin'. state = std::cin.rdstate(); // Checks the state of the fail bit of the stream object 'std::cin'. if(state & std::ios::failbit) std::cout << "The fail bit is set.\n"; else std::cout << "The fail bit is not set.\n"; return 0; }

Setting the error flags

The method void std::ios::setstate(std::ios::iostate state) const is used to set error flags from the stream object it is called from. What it does is combining the mask given in argument to the member of the object, of type std::ios::iostate, containing the error flags, using the | operator (OR). Using that method, we can set error flags, but we can not unset them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream> int main() { unsigned n; std::cin >> n; std::cout << "failbit: " << (int)std::cin.fail() << std::endl; std::cout << "eofbit: " << (int)std::cin.eof() << std::endl; std::cout << "badbit: " << (int)std::cin.bad() << std::endl << std::endl; // Sets the EOF and BAD error bits. std::cin.setstate(std::ios::eofbit | std::ios::badbit); std::cout << "failbit: " << (int)std::cin.fail() << std::endl; std::cout << "eofbit: " << (int)std::cin.eof() << std::endl; std::cout << "badbit: " << (int)std::cin.bad() << std::endl; return 0; }

Clearing the error flags

The method void clear(std::ios::iostate state = std::ios::goodbit) is used to replace the value of the member, of the stream object, containing the error flags by the value of the argument. The default value of the argument is std::ios::goodbit, so by default, what it does is clearing all error bits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream> int main() { std::cin.clear(std::ios::eofbit); std::cout << "failbit: " << (int)std::cin.fail() << std::endl; std::cout << "eofbit: " << (int)std::cin.eof() << std::endl; std::cout << "badbit: " << (int)std::cin.bad() << std::endl << std::endl; std::cin.clear(std::ios::failbit); std::cout << "failbit: " << (int)std::cin.fail() << std::endl; std::cout << "eofbit: " << (int)std::cin.eof() << std::endl; std::cout << "badbit: " << (int)std::cin.bad() << std::endl << std::endl; std::cin.clear(); std::cout << "failbit: " << (int)std::cin.fail() << std::endl; std::cout << "eofbit: " << (int)std::cin.eof() << std::endl; std::cout << "badbit: " << (int)std::cin.bad() << std::endl; return 0; }

Handling input of unexpected data

When an error bit of a stream object is set, as long as it stays set, all following input/output operations on it will fail. Therefore, when an error happens, we must clear the error flags of the stream object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream> int main() { long n; std::cin >> n; if(!std::cin.good()) { /* If an error flag is set, we clear it, so the following operations on that stream do not fail. */ std::cin.clear(); } std::cin >> n; return 0; }

In the example above, there was two input operations, but the program stopped after only one word entered to the console. Why? The reason is that, after an error, only clearing the error flags of a stream object is not enough. When a stream operation fails to convert and store the inputted data to its right operand, it does not take it off its buffer. Therefore, in the example above, the first stream operation tries to convert "abc" to a value of type long, but it fails, so "abc" stays in its buffer. The second stream operation tries again to convert the string "abc", because it stayed in the buffer of the stream, to a value of type long, and it fails again. To resolve that, we must, after an input operation failed, extract, from the buffer, the data that the stream was not able to convert.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream> int main() { long n; std::cin >> n; if(!std::cin.good()) { /* If an error flag is set, we clear it, so the following operations on that stream do not fail. */ std::cin.clear(); std::string str; /* Extracts the previously entered word, that the stream failed to convert to a value of type long. */ std::cin >> str; std::cout << '"' << str << '"' << " is not a number.\n"; } std::cin >> n; return 0; }

If we do not want to retrieve the entered data that the stream was not able to convert, we can extract and discard it using the method std::istream & std::istream::ignore(streamsize n = 1, int delim = EOF).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream> int main() { long n; std::cin >> n; if(!std::cin.good()) { /* If an error flag is set, we clear it, so the following operations on that stream do not fail. */ std::cin.clear(); // Discards all the entered data. std::cin.ignore(unsigned(-1), '\n'); } std::cin >> n; return 0; }

In the example above, the method std::istream::ignore discards everything in the buffer of the stream, until it reaches a newline character '\n', or until it discarded unsigned(-1) characters, which equals the maximum value a variable of type unsigned can hold.