Why does parse_config_file set failbit on a stream? - c++

This minimal program uses boost::program_options to parse a stringstream. Strangely, after parsing, the stream is not in a "good" state anymore and both failbit and eofbit are set.
#include <iostream>
#include <sstream>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
void test_stream(std::stringstream& s);
int main()
{
using namespace std;
namespace po = boost::program_options;
stringstream s;
s << "seed=3" << '\n';
test_stream(s);
po::options_description desc("");
desc.add_options()
("seed", po::value<int>());
po::variables_map vm;
po::store(po::parse_config_file(s, desc, true), vm);
po::notify(vm);
test_stream(s);
return 0;
}
void test_stream(std::stringstream& s)
{
using namespace std;
if (s.good())
{
cout << "stream is good" << endl;
}
else
{
cout << "stream is not good" << endl;
if (s.rdstate() & ios_base::badbit)
cout << "badbit is set" << endl;
if (s.rdstate() & ios_base::failbit)
cout << "failbit is set" << endl;
if (s.rdstate() & ios_base::eofbit)
cout << "eofbit is set" << endl;
}
}
Output:
stream is good
stream is not good
failbit is set
eofbit is set
Although the eof condition is somehow expected, since presumably the parser has read the stream until EOF, why is also the failbit set?

According to the documentation of the ios::eof flag this might happen in certain cases:
Operations that attempt to read at the End-of-File fail, and thus both the eofbit and the failbit end up set. This function can be used to check whether the failure is due to reaching the End-of-File or to some other reason.
Boost's parser uses std::copy() and an iterator on the stream to extract the options. As pointed out in Jerry Coffin's answer to another question, the iterator reads the items from the sequence. After reading the last one the sequence's eof bit is set. When the iterator is incremented another time to get the end-of-stream iterator, which is the condition to leave the loop in copy(), it tries to read again and therefor also the stream's fail bit is set.

Related

getline setting failbit along with eof

I am aware of the origin of this behavior since it has been very well explained in multiple posts here in SO, some notable examples are:
Why is iostream::eof inside a loop condition considered wrong?
Use getline() without setting failbit
std::getline throwing when it hits eof
C++ istream EOF does not guarantee failbit?
And it is also included in the std::getline standard:
3) If no characters were extracted for whatever reason (not even the discarded delimiter), getline sets failbit and returns.
My question is how does one deal with this behavior, where you want your stream to catch a failbit exception for all cases except the one caused by reaching the eof, of a file with an empty last line. Is there something obvious that I am missing?
A MWE:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
void f(const std::string & file_name, char comment) {
std::ifstream file(file_name);
file.exceptions(file.failbit);
try {
std::string line;
while (std::getline(file, line).good()) {
// empty getline sets failbit throwing an exception
if ((line[0] != comment) && (line.size() != 0)) {
std::stringstream ss(line);
// do stuff
}
}
}
catch (const std::ios_base::failure& e) {
std::cerr << "Caught an ios_base::failure.\n"
<< "Explanatory string: " << e.what() << '\n'
<< "Error code: " << e.code() << '\n';
}
}
int main() {
f("example.txt", '#');
}
where example.txt is a tab-delimited file, with its last line being only the \n char:
# This is a text file meant for testing
0 9
1 8
2 7
EDIT:
while(std::getline(file, line).good()){...} replicates the problem.
Another way to avoid setting failbit, is simply to refactor your if tests to detect the read of an empty-line. Since that is your final line in this case, you can simply return to avoid throwing the error, e.g.:
std::ifstream file (file_name);
file.exceptions (file.failbit);
try {
std::string line;
while (std::getline(file, line)) {
// detect empty line and return
if (line.size() == 0)
return;
if (line[0] != comment) {
std::stringstream ss(line);
// do stuff
}
}
}
...
You other alternative is to check whether eofbit is set in catch. If eofbit is set -- the read completed successfully. E.g.
catch (const std::ios_base::failure& e) {
if (!file.eof())
std::cerr << "Caught an ios_base::failure.\n"
<< "Explanatory string: " << e.what() << '\n'
<< "Error code: " /* << e.code() */ << '\n';
}
Edit: I misunderstood the OP, refer to David's answer above. This answer is for checking whether or not the file has a terminating newline.
At the end of your while (getline) loop, check for file.eof().
Suppose you just did std::getline() for the last line in the file.
If there is a \n after it, then std::getline() has read the delimiter and did not set eofbit. (In this case, the very next std::getline() will set eofbit.)
Whereas if there is no \n after it, then std::getline() has read EOF and did set eofbit.
In both cases, the very next std::getline() will trigger failbit and enter your exception handler.
PS: the line if ((line[0] != comment) && (line.size() != 0)) { is UB if line is empty. The conditions' order needs to be reversed.

ios::exceptions(...) changes when badbit is set

When executing the following code, I get the following output: EOF FAIL. So reading a line from an empty file sets eofbit and failbit, but not badbit.
If I enable exceptions by uncommenting line 11 the output changes: EOF BAD FAIL. Now reading a line from an empty file sets eofbit, badbit, but not failbit (otherwise an exception had occured).
Can someone explain why enabling exceptions changes the way, failbit and badbit are set. Is this documented somewhere?
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char const *argv[]) {
ifstream f("some_empty_file");
string line;
// f.exceptions(std::ifstream::failbit); // (line 11)
getline(f, line);
if(f.eof()) cout << "EOF ";
if(f.bad()) cout << "BAD ";
if(f.fail()) cout << "FAIL ";
cout << endl;
f.close();
return 0;
}
Edit: I read the answer on https://stackoverflow.com/a/11085193/3821618 on the difference of badbit and failbit. But that does not answer the question, why with exceptions enabled, reading from an empty file becomes a "serious error", while it is not, if I do not call f.exceptions(...)
There are 4 distinct stream states (taken from std::ios_base::iostate):
typedef /*implementation defined*/ iostate;
static constexpr iostate goodbit = 0;
static constexpr iostate badbit = /*implementation defined*/
static constexpr iostate failbit = /*implementation defined*/
static constexpr iostate eofbit = /*implementation defined*/
So setting of the eofbit doesn't necessarily trigger your exception mask.

Weird behavior with ifstreams and rdbuf()

I've noticed that using .rdbuf() on an ifstream seems to change it somehow. The following code should show the problem.
#include <fstream>
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
ifstream ifs("Sample.csv");
cout << "Reading buffer: " << endl;
cout << ifs.rdbuf(); // Outputs buffer as expected
cout << "Reading buffer again: " << endl;
cout << ifs.rdbuf(); // Returns nothing
return 0;
}
The reason this is bothering me is that I'm currently trying to copy the contents of one text file into another using ofstream ofs; ofs << ifs.rdbuf(). This works fine but makes reading from ifs using getline(ifs, str) fail, effectively "breaking" the stream.
This isn't particularly "weird"; it's the same stream behaviour you see every day. rdbuf isn't like std::stringstream::str() and it isn't magic — it's a pointer to the buffer, that your cout is then reading from just as you would read from the original stream yourself:
std::stringstream ss("1");
int x;
if (ss >> x)
cout << x;
if (ss >> x) // doesn't work a second time; "1" is already extracted
cout << x;
As your stream is a file stream, you can seek it back to the beginning to start from scratch (which will inherently do the same to its underlying buffer).
ifs.rdbuf() returns a pointer to the ifs's corresponding stream buffer object. Sending it to std::cout via << overload pulls information from the stream until the end of the buffer is reached (eof). Calling .rdbuf() again returns "nothing" because there's nothing to read at the end of the buffer. The buffer seek position be explicitly reset to zero by calling ifs.seekg (0);.

Use getline() without setting failbit

Is it possible use getline() to read a valid file without setting failbit? I would like to use failbit so that an exception is generated if the input file is not readable.
The following code always outputs basic_ios::clear as the last line - even if a valid input is specified.
test.cc:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
ifstream inf;
string line;
inf.exceptions(ifstream::failbit);
try {
inf.open(argv[1]);
while(getline(inf,line))
cout << line << endl;
inf.close();
} catch(ifstream::failure e) {
cout << e.what() << endl;
}
}
input.txt:
the first line
the second line
the last line
results:
$ ./a.out input.txt
the first line
the second line
the last line
basic_ios::clear
You can't. The standard says about getline:
If the function extracts no characters, it calls is.setstate(ios_base::failbit) which may throw ios_base::failure (27.5.5.4).
If your file ends with an empty line, i.e. last character is '\n', then the last call to getline reads no characters and fails. Indeed, how did you want the loop to terminate if it would not set failbit? The condition of the while would always be true and it would run forever.
I think that you misunderstand what failbit means. It does not mean that the file cannot be read. It is rather used as a flag that the last operation succeeded. To indicate a low-level failure the badbit is used, but it has little use for standard file streams. failbit and eofbit usually should not be interpreted as exceptional situations. badbit on the other hand should, and I would argue that fstream::open should have set badbit instead of failbit.
Anyway, the above code should be written as:
try {
ifstream inf(argv[1]);
if(!inf) throw SomeError("Cannot open file", argv[1]);
string line;
while(getline(inf,line))
cout << line << endl;
inf.close();
} catch(const std::exception& e) {
cout << e.what() << endl;
}

Infinite loop on EOF in C++

This code works as desired for the most part, which is to prompt the user for a single character, perform the associated action, prompt the user to press return, and repeat. However, when I enter ^D (EOF) at the prompt, an infinite loop occurs. I am clearing the error state via std::cin.clear() and calling std::cin.ignore(...) to clear the buffer. What could be causing the infinite loop?
#include <iostream>
#include <limits>
void wait()
{
std::cout << std::endl << "press enter to continue.";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.clear();
std::cin.get();
}
int main()
{
char response;
while (true)
{
std::cout << "enter a character at the prompt." << std::endl << "> ";
std::cin >> response;
switch (response)
{
case 'q':
exit(0);
break;
}
wait();
}
}
I am running this in the Mac OS X terminal, if it matters.
UPDATE: What I am really asking here is, when the user enters EOF (^D) at the prompt, how do I (a) detect it and (b) reset the stream so that the user can continue to enter data.
The following example is different from the code above, but illustrates the same principle of clearing the stream after a ^D has been detected and continuing to read from that stream.
> a
you entered: a
> b
you entered: b
> ^D
you entered EOF
> c
you entered: c
...
You should always check whether any of a stream's failure flags are set after calling formatted extraction operation, in your example you are checking response without checking whether response was correctly extracted.
Also, you are using std::endl in your prompt output where it doesn't make sense. std::endl prints \n and then flushes the buffer, but you then immediately print more characters so the flush is redundant. As cin and cout are (usually) tied, calling an input function for std::cin will cause std::cout to be flushed in any case so you may as well put a \n into your prompt string and save on the verbose extra << operators.
Why not make a prompting function that prints the prompt, retrieves the input an returns a reference to the stream so that you can test it for success using the usual stream to boolean type conversion.
This way you can get rid of the while true and explicit break.
std::istream& prompt_for_input( std::istream& in, std::ostream& out, char& response )
{
out << "enter a character at the prompt.\n> ";
in >> response;
return in;
}
int main()
{
char response;
while ( prompt_for_input( std::cin, std::cout, response ) && response != 'q' )
{
wait();
}
}
The question does not really make sense for standard input. It will be hard to read something from standard input after that stream has ended -- you'll have to re-open it somehow, but there is no way to re-open standard input. It might be connected to a pipe, or to a file, or to a terminal -- and there's no behaviour suitable for all of these.
So you're going to be reading explicitly from the terminal, I assume. On UN*X systems, that means reading /dev/tty, and re-opening it when needed. Here's a simple example that does it; most error-checking omitted.
// Warning: UN*X-specific
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
for(unsigned i=0; ; i++) {
ifstream tty("/dev/tty");
if (! tty) {
cerr << "Failed to open TTY" << endl;
return 2;
}
string s;
while (getline(tty,s))
cout << i << ": " << s << endl;
}
return 0; // (unreached)
}
You'll need to clear the flags to get the stream to do much of anything after it encounters EOF.
Err, I may be missing something, but I don't ever see you break out of the while (true) loop.
// ...
while (true) {
if (std::cin.eof()) {
break;
}
// ...
}
Upon reading an EOF, you just ignore it and loop back, without exiting the loop, so you'll continually read the EOF and continually loop. If you want to do something on seeing an EOF, you need to handle it either in your switch or before.
Perhaps you want to read input from somewhere after the user has closed your stdin with ^D? In that case, you'll have to close cin and reopen it to read from the other place you want to read input from.
As mentioned, you need to make sure the stream is not in a bad state. I would change while condition to use good(). Don't just check EOF as there are several ways a stream can become "bad" other than EOF.
while (std::cin.good()) {...
while ((std::cout << "Enter a character at the prompt ")
&& (!(std::cin >> response) || response =='q')) {
std::cout << "Not a valid input";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}