Search code examples
c++c++17fstreamifstreamfilebuf

Detect loss of integrity of the underlying file when using std::filebuf?


I have some code which uses std::ifstream to read from a file, but doesn't use any of the formatting features provided by std::ifstream. It essentially does something like this:

std::ifstream in;
in.open("example.txt", std::ios_base::in | std::ios_base::binary);
if (in.is_open()) {
    while (in.good()) {
        in.read(buffer, buffer_size);
        auto num_bytes = in.gcount();
        /* do stuff with the bytes */
    }
    in.close();
}

Since I'm directly processing the raw bytes from the file, it seems like a better fit to use std::filebuf instead of std::ifstream:

std::filebuf in;
if (in.open("example.txt", std::ios_base::in | std::ios_base::binary) != nullptr) {
    while (true) {
        auto num_bytes = in.sgetn(buffer, buffer_size);
        /* do stuff with the bytes */
        if (num_bytes < buffer_size) break;
    }
    in.close();
}

On the surface, it might look like these two code snippets achieve the same outcome. However, upon inspection of the C++ reference, std::basic_istream::read() says this:

If an internal operation throws an exception, it is caught and badbit is set. If exceptions() is set for badbit, the exception is rethrown.

Since badbit is used to signal that the underlying file is broken for some reason (maybe the OS can't access the file any more), it appears that the std::ifstream code snippet would handle this possibility by breaking out of the loop (good() will return false).

However, std::basic_streambuf::sgetn() doesn't say anything about the possibility that the underlying file becomes inaccessible. It also does not mention the possiblity of any exceptions being thrown (but it is not marked as noexcept).

Is there a way, when using std::filebuf, to correctly handle the case when the file is prematurely inaccessible (i.e. not yet EOF)? (Or perhaps, my understanding of file I/O is wrong?)


Solution

  • std::filebuf doesn't seem to have any form of error handling / reporting at all. That's just pathetic. I mean, why not?

    So, to use it, the only option would be to fall back on good old errno, and since the entry at cppreference doesn't mention that at all, I guess you can't really rely on it (although I'm sure that, in practise, it works).

    So, std::ifstream has to be the way to go, if you can plough your way through that mammoth and somewhat obscure list of which exception bit does what. And of course, if you do things this way, you get to control whether exceptions are thrown or not.

    Again, there's no apparent way to retrieve any sort of error code when something does go wrong, so again, if you want to try to present any sort of meaningful error message to your user (or for the benefit of tech support, perhaps), then errno is the only game in town.

    This is all just going to lead to a proliferation of 'Something went wrong' error messages. Yuk.