In today's C++, can I reliably get errors when constructing an fstream?

386 Views Asked by At

I want to read from a file using C++ standard library facilities (std::ifstream) - while, of course, reliably reporting errors if I encounter them.

Apparently, this is not at all an easy task!

  • std::basic_fstream's (the template of which std::ifstream is an instantiation) do not throw exceptions by default.
  • You can make a basic fstream throw exceptions - but only after construction, so the construction won't fail. See basic_ios::exceptions() (that's a super-superclass of std::ifstream).

14 years ago, this question was asked:

Get std::fstream failure error messages and/or exceptions

and the answers tell us that:

  1. There are no guarantees the thrown exception will tell us what the cause of the error was (just that some error occurred)
  2. We are not guaranteed that when a failbit or badbit is set on an fstream, errno / GetLastError() provide us with a non-zero/non-success value.

That's quite bad. On the other hand, 14 years have passed. Has anything changed? That is, are there better guarantees regarding the thrown exception, or of errno / GetLastError() being set? If not, what is the "best effort" approach to reporting an error in the construction of an std::fstream?

(I am tempted to ask "why does the constructor not throw on failure?", but let's not talk about that.)

1

There are 1 best solutions below

7
einpoklum On

Here's the best thing I can think of doing right now - "covering my ass" in case errno is somehow not set. At worst I'm wasting some cycles re-throwing in the "unhappy path".

// TODO: Consider checking errno here
std::filesystem::path file_path = whatever();
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
try {
    file.exceptions(std::ios::failbit | std::ios::badbit);
} catch (std::ios_base::failure& exception) {
    if (errno == 0) {
        throw;
    }
    throw std::system_error{ errno, std::generic_category(),
        "opening file " + file_path.native());
}