【问题标题】:Construct std::error_code from errno on POSIX and GetLastError() on Windows从 POSIX 上的 errno 和 Windows 上的 GetLastError() 构造 std::error_code
【发布时间】:2012-12-06 17:16:32
【问题描述】:

我的问题:从 POSIX 上的 errno 值和 Windows 上的 GetLastError() 构造 std::error_code 实例的正确方法是什么,以便可以将实例与来自 std::errc 的众所周知的值进行比较?

更长的解释:我的目标是将std::error_code 实例添加到一个自制的异常对象中,该对象以 C++11 的方式在 POSIX 和 Windows 系统上运行。

在我的跨平台应用程序中,我使用的是自制的 I/O 类层次结构,它使用 POSIX fopen() 和 Windows 的 CreateFile() 调用来打开/创建文件。如果失败,则会抛出通用的自制open_error 异常(它是从std::exception 派生的,是的,但它不是 C++ 的预定义异常类之一)。我正在尝试使用错误代码扩展这个相当简单的异常;如果我理解正确的话,更准确地说是 C++11 的 std::error_code

我的问题是如何从errno(在 POSIX 情况下)或GetLastError()(在 Windows 情况下)构造这样的对象。对于 POSIX,据我所知,我可以简单地在 std::error_code 的构造函数中使用 errno,例如像这样:

std::error_code ec(errno, std::generic_category());

ec 应该与std::errc 中的知名值相当。

对于 Windows,当然可以进行类似的调用:

std::error_code ec(::GetLastError(), std::generic_category());

但我不确定GetLastError() 返回的值是否很好地映射到std::errc 的知名常量。我读过in Boost's system library 他们为 Boost 的error_code 实现做了,但我问的是std 的实现,而不是 Boost。

请不要建议切换到使用 C++ 流进行文件访问。我很乐意,但目前我不想重构我的一半代码。

【问题讨论】:

    标签: c++ c++11


    【解决方案1】:

    这是实施质量问题。依赖 std::system_category() 返回的 const 静态对象来执行从平台原生错误代码枚举到标准 std::error_condition 枚举的映射。 17.6.5.14 错误码值[value.error.codes]下:

    鼓励不基于 POSIX 的操作系统实现定义与操作系统值相同的值。

    您可以在http://www.boost.org/doc/libs/1_46_1/libs/system/src/error_code.cpp 中看到 Boost 如何执行映射;您的编译器供应商提供的用于 Windows 的任何标准库都应该执行类似的操作。

    19.5.1.5p4 中涵盖了预期的行为,描述了system_category().default_error_condition(int ev)

    如果参数ev 对应于POSIX errno 值posv,则函数应返回error_condition(posv, generic_category())。否则,函数将返回error_condition(ev, system_category())

    因此,例如,error_code(ERROR_FILE_NOT_FOUND, std::system_category()).default_error_condition() 将调用 std::system_category().default_error_condition(ERROR_FILE_NOT_FOUND),它应该返回 std::error_condition(std::no_such_file_or_directory, std::generic_category())

    【讨论】:

    • 所以我的平台意识 I/O 类可以用error_code(::GetLastError(), std::generic_category()) 构造,平台无关的客户端代码可以用例如检查。 if (std::errc::no_such_file_or_directory == caught_exception.code()),假设供应商实际上遵循 17.6.5.14 的鼓励,它应该可以工作,即使在 Windows 上也是如此?
    • 你应该使用error_code(::GetLastError(), std::system_category()),而不是generic_category()
    • 我实际上已经在使用 g++ 4.7.2 的 Linux 上使用 std::system_category()errno 进行了尝试,但在这种情况下,比较失败了。使用std::generic_category() 效果很好。
    • 啊,我看到你的编辑了。所以在 POSIX 上我应该使用 error_code(errno, std::generic_category()) 构建,而在 Windows 上我应该使用 error_code(::GetLastError(), std::system_category())?
    • 是的,这似乎是 gcc 中的一个错误(好吧,在 libstdc++ 中);根据 1.9.5.1p4,它应该将 POSIX 错误代码映射到 generic_category。您提出的解决方法目前听起来不错。
    【解决方案2】:

    这是一个老问题,但我还没有真正找到关于 SO 的好答案。接受的答案让我有点困惑,因为它似乎给出了error_condition 而不是error_code。我在 POSIX 上为自己解决了以下问题:

    std::error_code error_code_from_errno(int errno_code) {
      return std::make_error_code(static_cast<std::errc>(errno_code));
    }
    

    这总是给我正确的类别(通用或系统)。过去我遇到过一些问题,即具有相同 errno 代码的错误代码比较不相等,因为一个有 generic_category 而另一个有 system_category

    【讨论】:

    • 这不适用于从GetLastError() 获得的值的窗口。 make_error_code 使用std::generic_category(),而GetLastError() 需要std::system_category()
    【解决方案3】:

    看起来你应该使用system_category() 代替GetLastError()/errno,它会在两个平台上做正确的事情。

    如果您已有errc,请改用generic_category()(或make_error_code)。

    这是一些带有“地址已在使用”错误的测试。

    #include <iostream>
    #include <system_error>
    
    #ifdef _WIN32
    #include <WinError.h>
    #define LAST_ERROR WSAEADDRINUSE
    #else
    #include <errno.h>
    #define LAST_ERROR EADDRINUSE
    #endif
    
    #define ERRC std::errc::address_in_use
    
    #define TRY(...)                                                              \
      {                                                                           \
        std::error_code ec = {__VA_ARGS__};                                       \
        std::cout << std::boolalpha << (ec == ERRC) << "\t" << ec.value() << "\t" \
                  << ec.message() << "\n";                                        \
      }
    
    int main() {
      TRY(static_cast<int>(ERRC), std::system_category())
      TRY(static_cast<int>(ERRC), std::generic_category()) // note: same as make_error_code
      TRY(static_cast<int>(LAST_ERROR), std::system_category())
      TRY(static_cast<int>(LAST_ERROR), std::generic_category()) // note: same as make_error_code
      return 0;
    }
    

    在 Windows 上:

    false   100 Cannot create another system semaphore.
    true    100 address in use
    true    10048   Only one usage of each socket address (protocol/network address/port) is normally permitted.
    false   10048   unknown error
    

    在 POSIX 上:

    true    98  Address already in use
    true    98  Address already in use
    true    98  Address already in use
    true    98  Address already in use
    

    使用这些三元组等效错误代码进行测试时,我得到了类似的结果:

    equivalent errc               Windows                 POSIX
    
    errc::broken_pipe             ERROR_BROKEN_PIPE       EPIPE
    errc::filename_too_long       ERROR_BUFFER_OVERFLOW   ENAMETOOLONG
    errc::not_supported           ERROR_NOT_SUPPORTED     ENOTSUP
    errc::operation_would_block   WSAEWOULDBLOCK          EWOULDBLOCK
    

    如果有人感兴趣,这里是映射到==WinError.h 常量的std::errcs 列表。这是检查if (std::error_code(static_cast&lt;int&gt;(win_error_constant), std::system_category()) == errc)

    address_family_not_supported:
            WSAEAFNOSUPPORT
    address_in_use:
            WSAEADDRINUSE
    address_not_available:
            WSAEADDRNOTAVAIL
    already_connected:
            WSAEISCONN
    argument_list_too_long:
    argument_out_of_domain:
    bad_address:
            WSAEFAULT
    bad_file_descriptor:
            WSAEBADF
    bad_message:
    broken_pipe:
            ERROR_BROKEN_PIPE
    connection_aborted:
            WSAECONNABORTED
    connection_already_in_progress:
            WSAEALREADY
    connection_refused:
            WSAECONNREFUSED
    connection_reset:
            WSAECONNRESET
    cross_device_link:
            ERROR_NOT_SAME_DEVICE
    destination_address_required:
            WSAEDESTADDRREQ
    device_or_resource_busy:
            ERROR_BUSY_DRIVE
            ERROR_BUSY
            ERROR_OPEN_FILES
            ERROR_DEVICE_IN_USE
    directory_not_empty:
            ERROR_DIR_NOT_EMPTY
    executable_format_error:
    file_exists:
            ERROR_FILE_EXISTS
            ERROR_ALREADY_EXISTS
    file_too_large:
    filename_too_long:
            ERROR_BUFFER_OVERFLOW
            WSAENAMETOOLONG
    function_not_supported:
            ERROR_INVALID_FUNCTION
    host_unreachable:
            WSAEHOSTUNREACH
    identifier_removed:
    illegal_byte_sequence:
    inappropriate_io_control_operation:
    interrupted:
            WSAEINTR
    invalid_argument:
            ERROR_INVALID_HANDLE
            ERROR_INVALID_PARAMETER
            ERROR_NEGATIVE_SEEK
            ERROR_DIRECTORY
            ERROR_REPARSE_TAG_INVALID
            WSAEINVAL
    invalid_seek:
    io_error:
            ERROR_SEEK
            ERROR_WRITE_FAULT
            ERROR_READ_FAULT
            ERROR_OPEN_FAILED
            ERROR_CANTOPEN
            ERROR_CANTREAD
            ERROR_CANTWRITE
    is_a_directory:
    message_size:
            WSAEMSGSIZE
    network_down:
            WSAENETDOWN
    network_reset:
            WSAENETRESET
    network_unreachable:
            WSAENETUNREACH
    no_buffer_space:
            WSAENOBUFS
    no_child_process:
    no_link:
    no_lock_available:
            ERROR_LOCK_VIOLATION
            ERROR_LOCKED
    no_message_available:
    no_message:
    no_protocol_option:
            WSAENOPROTOOPT
    no_space_on_device:
            ERROR_HANDLE_DISK_FULL
            ERROR_DISK_FULL
    no_stream_resources:
    no_such_device_or_address:
    no_such_device:
            ERROR_INVALID_DRIVE
            ERROR_BAD_UNIT
            ERROR_DEV_NOT_EXIST
    no_such_file_or_directory:
            ERROR_FILE_NOT_FOUND
            ERROR_PATH_NOT_FOUND
            ERROR_BAD_NETPATH
            ERROR_INVALID_NAME
    no_such_process:
    not_a_directory:
    not_a_socket:
            WSAENOTSOCK
    not_a_stream:
    not_connected:
            WSAENOTCONN
    not_enough_memory:
            ERROR_NOT_ENOUGH_MEMORY
            ERROR_OUTOFMEMORY
    not_supported:
            ERROR_NOT_SUPPORTED
    operation_canceled:
            ERROR_OPERATION_ABORTED
    operation_in_progress:
            WSAEINPROGRESS
    operation_not_permitted:
    operation_not_supported:
            WSAEOPNOTSUPP
    operation_would_block:
            WSAEWOULDBLOCK
    owner_dead:
    permission_denied:
            ERROR_ACCESS_DENIED
            ERROR_INVALID_ACCESS
            ERROR_CURRENT_DIRECTORY
            ERROR_WRITE_PROTECT
            ERROR_SHARING_VIOLATION
            ERROR_CANNOT_MAKE
            ERROR_NOACCESS
            WSAEACCES
    protocol_error:
    protocol_not_supported:
            WSAEPROTONOSUPPORT
    read_only_file_system:
    resource_deadlock_would_occur:
    resource_unavailable_try_again:
            ERROR_NOT_READY
            ERROR_RETRY
    result_out_of_range:
    state_not_recoverable:
    stream_timeout:
    text_file_busy:
    timed_out:
            WSAETIMEDOUT
    too_many_files_open_in_system:
    too_many_files_open:
            ERROR_TOO_MANY_OPEN_FILES
            WSAEMFILE
    too_many_links:
    too_many_symbolic_link_levels:
    value_too_large:
    wrong_protocol_type:
            WSAEPROTOTYPE.0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-02
      • 2011-12-18
      • 2011-06-12
      • 1970-01-01
      • 1970-01-01
      • 2015-11-20
      相关资源
      最近更新 更多