1

Sorry for weird title. Limited to 150 chars so couldn't use proper sentences.

So let's say I've done the following to find out that something went wrong with my file stream:

std::ofstream ofs;
do_stuff_with(ofs);
// streams don't throw on error because C++ [edit: we can make them do so, but the .what()s aren't very user-friendly]
// so we have to check for failure manually
if(!ofs){
    auto fail_code = errno; // threadsafe on Win/Linux
    // but what goes here?
}

1) strerror: Not threadsafe

2) strerror_s: Not in GCC? Or is it?

3) strerror_r: Not in Msvc? Or is it?

4) #ifdef/#define/etc: yuck, but may be the only choice

I did do some searching but I didn't find a "this will definitely work in a sensible yet slightly platform-dependent way" answer... That said, I feel like this is "obviously a duplicate question", but I can't find the original...

JamEnergy
  • 720
  • 8
  • 21
  • You can make stream throw on error if you want. – Holt Apr 28 '16 at 14:44
  • 1
    The closest you can get in c++ is [`std::io_errc`](http://en.cppreference.com/w/cpp/io/io_errc) – πάντα ῥεῖ Apr 28 '16 at 14:45
  • 4
    ["`// streams don't throw on error because C++`"](http://en.cppreference.com/w/cpp/io/basic_ios/exceptions). Not that it is guaranteed to have a useful `what()`. – BoBTFish Apr 28 '16 at 14:45
  • 1
    Ah I knew about the exceptions() thing, but for some reason I had the idea that it wasn't properly implemented either. But it seems to work, just it gives messages like "ios_base::failbit set" rather than "No such file or directory." as discovered elsewhere on stackoverflow http://stackoverflow.com/questions/17337602/how-to-get-error-message-when-ifstream-open-fails – JamEnergy Apr 28 '16 at 15:17
  • check out `throw`, `what()`, `try` and `catch`. – Charles Apr 28 '16 at 15:17

3 Answers3

2

You can always throw your own exceptions using std::system_error:

#include <cerrno>
#include <fstream>
#include <iostream>
#include <system_error>

int main()
{
    try
    {
        std::ofstream foo{"/root/bar.baz"};
        foo << "bla" << std::endl;
        foo.close();

        if(!foo)
            throw std::system_error{errno, std::generic_category()};
    }
    catch(const std::system_error& err)
    {
         std::cout << "Error: " << err.code() << " - " << err.what() << std::endl;
    }

    return 0;
}

This returns Error: generic:13 - Permission denied.

Jan Stephan
  • 403
  • 3
  • 11
  • I think std::system_error may be what I've been looking for! I'll try this and then mark this as the answer if it turns out to satisfy my requirements. – JamEnergy Apr 28 '16 at 17:15
  • I edited in a snippet for getting the error message without going via an exception, too, since one of my main issues was getting a readable message out (rather than throwing exceptions, though that's also useful). – JamEnergy Apr 29 '16 at 11:10
1

Since C++11, you can use the class std::error_code for that:

std::cout << std::error_code{errno, std::generic_category()}.message();

In fact you can even do it a little shorter than that:

std::cout << std::generic_category().message(errno);

Though I must say I find the first one a little more idiomatic.

As a sidenote, there is also std::system_category(), which seems to be largely equivalent to generic_category() on Unix but on Windows, it can be used to translate Windows API error codes such as returned by GetLastError() etc.

This is the same class that's also used within the std::system_error exception, but you don't need to create an instance of the exception if all you want to do is to get the error message.

Jan Andres
  • 11
  • 1
0

This is the best I could come up with. It's the "yuck" answer, but at least you can put the "yuck" in a single function and hide it away in some cpp file somewhere. std::ios covers boost streams as well, of course.

Requires #ifdefs so it's a cheat. I believe Visual Studio #defines _WIN32 by default, so at least you don't have to set up that infrastructure per se.

void check_stream(std::ios & stream)
{
    if (!stream){
        char err[1024] = { 0 };
#ifdef _WIN32
        strerror_s(err, errno);
#else
        strerror_r(errno, err, 1024);
#endif
        throw MyException(err);
    }
}

My own solution makes me sad so hopefully a better one will come along. But time is finite, so just submit to the Dark Side, use something like this, and get on with your life. :P

try{
    boost::filesystem::ifstream ifs("testfile");
    check_stream(ifs);
}
catch (std::exception & e){
    std::cout << e.what(); // "No such file or directory"
}
JamEnergy
  • 720
  • 8
  • 21