Создайте 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 errnoposv
функция должна вернуть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::errc
s сопоставляется с
==
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