Why getline() throws 'std::ios_base::failure' when exception mask is not set to eofbit?

Razorfever picture Razorfever · Nov 30, 2012 · Viewed 10.5k times · Source

Consider the following code:

    ifstream in;
    try {
        in.exceptions ( ifstream::failbit | ifstream::badbit );
        in.open(pConfLocation);
    } catch ( ifstream::failure e ) {
        throw std::runtime_error("Can't open configuration file\n");
    }

    vector<string> lns;
    string s;

    in.clear();
    while ( !in.eof() ){
        getline( in, s );
        boost::algorithm::trim(s);
        lns.push_back( s+='\n');
    }

So:

  1. I set the following "exception mask" ( ifstream::failbit | ifstream::badbit ) for the needs of the try-catch block. The file opens without problems.
  2. In while{} block, I know that at the end of the file eofbit will be set. But

The exception mask is an internal value of all stream objects specifying which state flags have to throw an exception when they are set.

I did NOT set ifstream::eofbit, but anyway the following error appears at runtime:

terminate called after throwing an instance of 'std::ios_base::failure'
  what():  basic_ios::clear
The program has unexpectedly finished.

I cannot understand this behavior. I tried to use in.clear() right before while{} but with no effect. clear() itself sets goodbit, and as far as I understand, "flags have to throw an exception" (see the quote above), but when googbit set it's not causing to throw any exceptions…

If to delete

        in.exceptions ( ifstream::failbit | ifstream::badbit );

it works.


How to make getline() work in this case?

Answer

The problem is in your input iteration. eofbit is only set when the last read reached EOF, not if the next read will read only EOF. When the latter happens, failbit is set at the same time. See the discussion here.

In your particular case, if the file ends with a newline (as it probably does), the getline() reads up to and including that newline and returns. eofbit is still not set. The next getline() then encounters EOF directly, and as per its documentation, "If the function extracts no elements, it calls setstate(failbit)."