36

I'm using fstream. Is there any way to get the failure message/exception?

For example if I'm unable to open the file?

Jan Rüegg
  • 9,587
  • 8
  • 63
  • 105
sofr
  • 5,407
  • 6
  • 28
  • 29

4 Answers4

32

Streams by default do not throw exceptions on error, they set flags. You can make them throw exceptions by using the stream's exception() member function:

ifstream ifs;
ifs.exceptions( std::ios::failbit );   // throw if failbit get set

Theoretically, you could then do something like this:

try {
  int x;
  ifs >> x;
}
catch( const std::exception & ex ) {
   std::cerr << "Could not convert to int - reason is " 
                  << ex.what();
}

Unfortunately, the C++ Standard does not specify that thrown exceptions contain any error message, so you are in implementation specific territory here.

10

Short answer: no. Even checking errno after you detect failure (using e.g. bad(), fail()) after various operations doesn't reliably work. Creating an ifstream/ofstream wrapping a file that can't be opened doesn't necessarily set a failure bit until you try to read, write, or close it.

Long answer: you can call ios::exceptions(ios_base::iostate) to request that ios_base::ios_failure exceptions be thrown when a corresponding bit (badbit, failbit, eofbit) is set, but this (at least on GNU and Microsoft C++ libraries) doesn't get you any more information than manually checking the bits, and ends up being largely pointless, IMHO.

Doug
  • 8,780
  • 2
  • 27
  • 37
3

From checking it out I found that also errno and also GetLastError() do set the last error and checking them is quite helpful. For getting the string message use:

strerror(errno);
Rolando Isidoro
  • 4,983
  • 2
  • 31
  • 43
sofr
  • 5,407
  • 6
  • 28
  • 29
2

If you are looking for a more detailed message than what is provided by std::ios_base::failure then here is an example which uses errno to re-raise a std::system_error:

terminate called after throwing an instance of 'std::system_error'
  what():  broken/path: No such file or directory
#include <fstream>

int main() {
  const std::string filename{ "broken/path" };
  try {
    std::ifstream file{ filename };
    file.exceptions(std::ios::failbit); // std::ios_base_failure is thrown here
  } catch (std::ios_base::failure&) {
    throw std::system_error{ errno, std::generic_category(), filename };
  }
}

This works on UNIX and Windows because "All errno values are … UNIX-compatible" (source).

If you want even-more-specific Windows error codes (i.e. file is already open in another program), you should look to the platform-specific GetLastError() api instead of errno.