Getting meaningful error messages from fstream's in C++
Asked Answered
P

3

14

What is the best way to get meaningful file access error messages, in a portable way from std::fstreams ? The primitiveness of badbits and failbits is getting to be bit annoying. I have written my own exception hierarchies against win32 and POSIX before, and that was far more flexible than the way the STL does it.

I am getting "basic::ios_clear" as an error message from the what method of a downcasted catch (std::exception) of a fstream which has exceptions enabled. This doesn't mean much to me, although I do know what the problem is I'd like my program to be a tad more informative so that when I start deployment a few months later my life will be easier.

Is there anything in Boost to extract meaningful messages out of the fstream's implementation cross platform and cross STL implementation ?

Pizza answered 14/4, 2010 at 16:52 Comment(1)
+1 for the question - I'm having this issue, too. Seems ridiculous to have such a basic interface that cannot provide a meaningful error message. At the very least the APR (Apache Portable Runtime) has provided this. It is extremely important to be able to communicate the Operating System's issue with a filename, such as "invalid permissions", or "disk error", etc to the user. Otherwise the program, correctly, stops functioning, but the user is left with no idea what actions to take to rectify the problem.Clerestory
K
8

Nobody stops you from also checking errno/strerror (e.g. in your exception handler) for a more specific reason for failure.

UPDATE -- regarding portability

Incidentally, IIRC Visual Studio's fstream implementation calls the _open/_read/_write/etc. CRT methods, which set errno. Microsoft makes no guarantee about GetLastError still containing the correct value after the CRT methods return. Idem for the cygwin, mingw etc. implementations, which set errno with no claims or guarantees about GetLastError.

So I stand by my claim that all you need, can, and therefore want to do is check errno.

Now, given all of the above, if you still want to complicate your life and overengineer by using Boost::System instead of simply calling strerror then I guess my definition and your definition of elegance and simplicity are not the same. :)

Kelwin answered 14/4, 2010 at 19:8 Comment(7)
Just because "no body stops me" does not mean your suggestion is anywhere near elegent. Also I have said the following "I have written my own exception hierarchies against win32 and POSIX before" and finally I have mentioned portability as being a desirable feature as well. So this answer adds no value. Thanks though.Pizza
@Hassan Syed: The only thing in boost that comes close to this is Boost::System ( boost.org/doc/libs/1_42_0/libs/system/doc/index.html ), and that's not going to help you with iostreams.Roslynrosmarin
I don't want to sound finicky, but while " RE Writing " uniform error handling for OS abstractions is indeed the norm, this is the EXACT thing my question is aiming to address. Rehashing the obvious --which has also been addressed in the quesstion -- is not a solution. I don't want to re-invent the wheel, I am asking if there is a modern robus solution to my problem.Pizza
Thank you billy (+1), I have known of its existence, I guess I will give the library a closer look.Pizza
Billy upon closer inspection, boost::system is indeed the exact solution to my question. If you create the answer I can accept it :D. I was avoiding it due to it not having tutorial style documentation :DPizza
@Hassan, I fail to see what is inelegant about the solution, how errno is not portable (it is in fact your only portable choice), and what anyone other than you was supposed to make of your "I have written my own exception hierarchies". You can wrap and abstract it all you want, at the end of the day you will have to read errno.Kelwin
Someone "WILL HAVE TO" read errno, that someone does not have to be me -- "All problems in computer science can be solved by another level of indirection", I don't want to provide this level of indirection, yet again. I hope We can all agree that reuse is good in software engineering ?Pizza
B
1

What information do you want? badbit indicates an I/O error. eofbit indicates eof. failbit indicates a parse error.

To eliminate one solution, anyway, I don't think you can override the native-type input functions because of ADL. You could implement operator>>(istream, input_safe_int) where input_safe_int is constructed from int&. Put a try block inside, etc.

Benue answered 15/4, 2010 at 13:19 Comment(0)
P
1

I've had luck catching the std::ios_base::failure and then re-raising a std::system_error using errno:

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).

Punt answered 5/11, 2021 at 19:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.