How to get IO error messages when creating a file in C++?

走远了吗. 提交于 2021-02-16 14:26:08

问题


One of the ancient anti-pattern is people checking error status and then returning fairly useless messages like "operation failed" instead of "operation failed because ...". I want C++ file I/O operations to fail with exception and get the error message on why it failed. Specifically I want ofstream object to raise exception when file creation fails and get bit more useful message such as "permission denied" or "No file or path".

This is trivial to do in languages such as C# or Java or Python but somehow there is no well documented way to do this C++. By default, iostream objects just fail silently. There is some global errorcode that gets but I would rather have exceptions. After lot of searching, I read that you can enable exceptions using following line of code:

my_file.exceptions(flog.exceptions() | std::ios::failbit | std::ifstream::badbit);

That works but now the exception that gets raised is std::ios_base::failure and the ex.what() returns useless strings like "basic_ios::clear". As per the C++11 specs std::ios_base::failure was supposed to be inherited from system_error which has .code().message() that will give the exception message. Let's keep aside this weirdness here and not finger point to person who decided what() should not be returning actual error message :). The problem is that even when compiling with C++11 and G++ 4.8.4, I find that std::ios_base::failure is not actually inherited from system_error.

Questions

  1. Why std::ios_base::failure is not inherited from system_error in latest G++ 4.8.4 even when compiling with C++11 mode? Is GCC's implementation of C++11 incomplete in this area or do I need to do something more?
  2. How do I get to my goal of raising exceptions when IO operations fail in C++ and getting error messages? Is there no way to do this even in latest C++11 or C++14? What are the alternatives?

Here's the sample code. You can compile and run it here.

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

int main() {
    try {
        std::ofstream flog;
        flog.exceptions(flog.exceptions() | std::ios::failbit | std::ifstream::badbit);
        flog.open("~/watever/xyz.tsv", std::ios::trunc);
    }
    catch (const std::ios_base::failure &ex) {    
        std::cout << "ios_base::failure: " << ex.what();
    }
    catch(const std::system_error& ex) {
        std::cout << "system_error: " << ex.code().message();
    }
}

回答1:


  1. According to GCC's C++11 status documentation, "System error support" is fully supported.

    And according to Bug 57953 - no C++11 compliant std::ios_base::failure found, std::ios_base::failure was changed in Revision 217559 to derive from system_error in C++11. If you look in the updated ios_base.h, std::ios_base::failure derives from system_error if _GLIBCXX_USE_CXX11_ABI is defined. That define is mentioned in GCC's Using Dual ABI documentation.

    However, there is a regression regarding ABI issues with std::ios_base::failure that is still open, due to the fact that some pieces of the standard library do not define _GLIBCXX_USE_CXX11_ABI:

    Bug 66145 - [5/6/7 Regression] std::ios_base::failure objects thrown from libstdc++.so use old ABI

  2. the short answer is - you probably can't, at least not with GCC's current implementation anyway. Unless you can recompile everything in the library with _GLIBCXX_USE_CXX11_ABI defined.




回答2:


On POSIX systems ios failures set errno so you can get meaningful error messages using that. I often do this:

std::string getenv_as_string(std::string const& var)
{
    auto ptr = std::getenv(var.c_str());
    return ptr ? ptr : "";
}

// ~ doesn't work from C++
const std::string HOME = getenv_as_string("HOME");

int main()
{
    try
    {
        std::ofstream ifs;

        ifs.open(HOME + "/watever/xyz.tsv", std::ios::trunc);

        if(!ifs)
            throw std::runtime_error(std::strerror(errno));

        // Do stuff with ifs
    }
    catch(std::exception const& e)
    {
        std::cerr << e.what() << '\n';
    }
}

Output:

No such file or directory


来源:https://stackoverflow.com/questions/38471518/how-to-get-io-error-messages-when-creating-a-file-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!