25

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

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

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

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

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

ec应该与std::errc.

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

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

但我不确定 map 返回的值是否GetLastError()很好地映射到std::errc. 我在 Boost 的系统库中读到了他们为 Boost 的实现error_code所做的工作,但我问的是std实现,而不是 Boost。

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

4

3 回答 3

10

这是实施质量问题。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())

于 2012-12-19T11:21:54.003 回答
6

这是一个老问题,但我还没有真正找到一个好的答案。接受的答案让我有点困惑,因为它似乎给出了一个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 代码的错误代码比较为不相等,因为一个 hasgeneric_category和另一个 has system_category

于 2018-07-13T13:59:36.050 回答
0

看起来你应该使用system_category()for 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

如果有人感兴趣,这里有一个std::errc映射到== WinError.h常量的 s 列表。这是检查if (std::error_code(static_cast<int>(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
于 2021-12-29T00:17:54.287 回答