在 C++ std::streams 中,失败后,如何获取失败原因?必需:线程安全且 Windows 和 Linux 通用(或至少 Msvc/Gcc)

In C++ std:: streams, after a failure, how to get a failure reason? Required: threadsafe and common to Windows and Linux (or at least Msvc/Gcc)

对不起,奇怪的标题。限于 150 个字符,因此无法使用正确的句子。

假设我已完成以下操作以发现我的文件流出现问题:

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) 错误: 不是线程安全的

2) strerror_s:不在 GCC 中?或者是?

3) strerror_r: 不在 Msvc 中?或者是?

4) #ifdef/#define/etc:糟糕,但可能是唯一的选择

我确实做了一些搜索,但我没有找到 "this will definitely work in a sensible yet slightly platform-dependent way" 答案...也就是说,我觉得这是 "obviously a duplicate question",但我找不到原来的.. .

这是我能想到的最好的。这是 "yuck" 的答案,但至少您可以将 "yuck" 放在一个函数中并将其隐藏在某个 cpp 文件中的某个地方。 std::ios 当然也包括 boost 流。

需要#ifdefs 所以这是作弊。我相信 Visual Studio 默认情况下 #defines _WIN32,所以至少你不必自己设置该基础结构。

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

我自己的解决方案让我很伤心,所以希望能有更好的解决方案出现。但是时间是有限的,所以只要屈服于黑暗面,使用这样的东西,然后继续你的生活。 :P

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

您始终可以使用 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;
}

这个returnsError: generic:13 - Permission denied.

自 C++11 起,您可以为此使用 class std::error_code

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

事实上,你甚至可以做得比这短一点:

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

虽然我必须说我发现第一个更地道一点。

作为旁注,还有std::system_category(),它似乎在很大程度上等同于Unix上的generic_category(),但在Windows上,它可以用来翻译Windows API 返回的错误码如GetLastError()

这与 std::system_error 异常中也使用的 class 相同,但如果您只想获取错误,则无需创建异常的实例消息。