views:

184

answers:

3

I was playing around with istream iterators and exception handling a few days ago and I came across with this curiousity:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char* argv[])
{
   if (argc < 2) {
      cout << argv[0] << " <file>" << endl;
      return -1;
   }

   try {
      ifstream ifs(argv[1]);
      ifs.exceptions(ios::failbit | ios::badbit);
      istream_iterator<string> iss(ifs), iss_end;
      copy(iss, iss_end, ostream_iterator<string>(cout, "\n"));
   }
   catch (const ios_base::failure& e) {
      cerr << e.what() << endl;
      return -2;
   }

   return 0;
}

Why a failbit exception is always raised after reading the last word of the input file?

A: 

One detect the EOF condition by reading until a failure -- which triggers the exception -- and then checking the cause of the failure.

To expand: the istream_iterator becomes invalid when after having read a value with >>, the stream operator void* returns NULL. But for that, the operator >> has to set the fail bit, so raise the exception.

AProgrammer
A: 

Good question. It would be nice to be able to catch other failures in that call, but have it continue normally when it hits eof.

That said, I haven't used exceptions with streams before. I think you could do the copy and check the state of the stream afterwards to detect other errors, for example:

ifstream ifs(argv[1]);
if (!ifs) {
    cerr << "Couldn't open " << argv[1] << '\n';
    return -1;
}
//ifs.exceptions(ios::failbit | ios::badbit);
istream_iterator<std::string> iss(ifs), iss_end;
copy(iss, iss_end, ostream_iterator<std::string>(cout, "\n"));
if (!ifs.eof()) {
    cerr << "Failed to read the entire file.\n";
    return -2;
}
UncleBens
@Roman: I can't test for end of file condition myself in this program since I don't have control of the reading loop. It is the algorithm "copy" which must test for it. Anyway, the default constructor of istream_iterator creates a "past-the-end" iterator (using STL terminology), which is this case is EOF, to finish the loop (second argument of "copy").
null_pointer
@AProgrammer: but that's the trick of the program (and the reason for my question). I didn't specified ios::eofbit in ifs.exceptions(...), so "copy" should reach the end of file normally and raise no exception. I mean, what you say would be correct if I had done it like this: ifs.exceptions(ios::failbit | ios::badbit | ios::eofbit).
null_pointer
You seem to have put comments to a wrong post :) But I think the end comes for istream_iterator when it fails, not at eof. Besides, it seems these conditions can't be reliably tested separately. For example, add a space to "foo " in coppro's reply, and you'll find that eofbit and failbit are set at the same time. So either you don't use exceptions, or you accept that EOF is exceptional...
UncleBens
Sorry, UncleBens! I thought this text box was "global" for the page until I've realized that each reply has its own. I'd rather follow your advice and not use exceptions. Thanks. Well, thanks to everybody, too.
null_pointer
+2  A: 

failbit is set whenever a read operation fails to extract any characters, whether this is because it hit EOF or not.

stringstream ss ("foo");
string s;
int i;

ss >> i; // sets failbit because there is no number in the stream
ss.clear();
ss >> s; // sets eofbit because EOF is hit
ss.clear();
ss >> s; // sets eofbit and failbit because EOF is hit and nothing is extracted.
coppro