如何在C ++中创建文件时获取IO错误消息?

时间:2016-07-20 02:49:48

标签: c++ c++11 exception exception-handling io

古老的反模式之一是人们检查错误状态然后返回相当无用的消息,如“操作失败”而不是“操作失败,因为......”。我希望C ++文件I / O操作失败并出现异常,并获取有关失败原因的错误消息。具体来说,我希望ofstream对象在文件创建失败时引发异常并获得更多有用的消息,例如“权限被拒绝”或“无文件或路径”。

在C#或Java或Python等语言中这是微不足道的,但不知何故没有详细记录的方法来执行此C ++。默认情况下,iostream对象只是静默失败。有一些全局错误代码,但我宁愿有例外。经过大量搜索后,我读到您可以使用以下代码行启用异常:

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

这样可行,但现在引发的异常是std::ios_base::failure,ex.what()返回无用的字符串,如“basic_ios :: clear”。根据C ++ 11规范,std::ios_base::failure应该是从system_error继承的,它具有.code()。message(),它将提供异常消息。让我们在这里保持这种古怪,而不是指向决定什么()不应该返回实际错误消息的人:)。问题是,即使使用C ++ 11和G ++ 4.8.4进行编译,我发现std::ios_base::failure实际上并不是从system_error继承的。

问题

  1. 为什么std::ios_base::failure在最新的G ++ 4.8.4中不会从system_error继承,即使在使用C ++ 11模式进行编译时也是如此? GCC在这个领域的C ++ 11实现是不完整的还是我需要做更多的事情?
  2. 如果IO操作在C ++中失败并收到错误消息,我如何实现提高异常的目标?即使在最新的C ++ 11或C ++ 14中也没有办法做到这一点?有什么选择?
  3. 以下是示例代码。你可以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();
        }
    }
    

2 个答案:

答案 0 :(得分:3)

  1. 根据海湾合作委员会的C++11 status documentation,完全支持“系统错误支持”。

    根据Bug 57953 - no C++11 compliant std::ios_base::failure foundstd::ios_base::failure中的system_error已更改为从C ++ 11中的ios_base.h派生而来。如果您查看更新后的std::ios_base::failure system_error,则会_GLIBCXX_USE_CXX11_ABI派生std::ios_base::failure如果_GLIBCXX_USE_CXX11_ABI已定义。 GCC的Revision 217559文档中提到了该定义。

    然而,由于标准库的某些部分没有定义_GLIBCXX_USE_CXX11_ABI,因此Map<Integer, List<String>> transposeMap = new HashMap<>(); map.forEach((key, list) -> list.stream().forEach( elm -> transposeMap.put(elm, transposeMap.get(elm) == null ? Arrays.asList(key) : (Stream.concat(transposeMap.get(elm).stream(), Arrays.asList(key).stream()).collect(Collectors.toList()))))); 的ABI问题仍然存在,但仍存在问题。

    Using Dual ABI

  2. 简短的回答是 - 你可能不能,至少不是GCC目前的实施方式。除非您可以使用Map<String, List<Integer>> map定义的库重新编译库中的所有内容。

答案 1 :(得分:2)

POSIX系统ios失败时设置errno,以便您可以使用它获取有意义的错误消息。我经常这样做:

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';
    }
}

<强>输出:

No such file or directory