Создайте std::error_code из errno в POSIX и GetLastError() в Windows

Мой вопрос: как правильно построить std::error_code экземпляры из errno значения на POSIX и GetLastError() в Windows, чтобы экземпляры можно было сравнить с известными значениями из std::errc?

Более длинное объяснение: моя цель - добавить std::error_code экземпляр самодельного объекта исключения, который работает в системах POSIX и Windows на C++11ish.

В моем кроссплатформенном приложении я использую самодельную иерархию классов ввода / вывода, которая использует POSIX fopen() и винда CreateFile() призывает к открытию / созданию файлов. Если это не удается, самодельный open_error выдается исключение (оно получено из std::exception, да, но это не один из предопределенных классов исключений C++). Я пытаюсь расширить это довольно простое исключение кодом ошибки; чтобы быть более точным с C++ 11 std::error_code если я правильно поняла.

Моя проблема в том, как построить такой объект из errno (в случае POSIX) или GetLastError() (в случае с Windows). Для POSIX, насколько я понял, я могу просто использовать errno в std::error_codeконструктор, например, так:

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

И это ec должны быть сопоставимы с известными значениями из std::errc,

Для Windows аналогичный вызов можно сделать, конечно:

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

Но я не уверен, что значения возвращаются GetLastError() сопоставить хорошо известные константы из std::errc, Я прочитал в системной библиотеке Boost, что они делают для реализации Boosterror_codeно я спрашиваю о std реализация, а не о бусте.

Пожалуйста, не советуйте переходить на использование потоков C++ для доступа к файлам. Я бы с удовольствием, но рефакторинг половины моего кода - это не то, что я хотел бы сделать прямо сейчас.

3 ответа

Решение

Это вопрос качества реализации. Статический объект const, возвращаемый std::system_category() полагается для выполнения преобразования из перечисления кода ошибки платформы в стандарт 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()),

Это старый вопрос, но я не нашел хорошего ответа на 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));
}

Это всегда дает мне правильную категорию (общая или системная). В прошлом у меня были проблемы, когда коды ошибок с одинаковым кодом ошибки сравнивались как не равные, потому что generic_category а другой имел system_category,

Похоже, вы должны использовать 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

Если кому интересно, вот список std::errcs сопоставляется с == WinError.hконстанты. это проверка 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
Другие вопросы по тегам