Reputation:
My question: What is the correct way to construct std::error_code
instances from errno
values on POSIX and GetLastError()
on Windows so that the instances can be compared to the well-known values from std::errc
?
The longer explanation: My goal is to add an std::error_code
instance to a self-made exception object that works on POSIX and Windows systems in a C++11ish way.
In my cross-platform application I'm using a self-made I/O class hierarchy that uses the POSIX fopen()
and Windows' CreateFile()
calls for opening/creating files. If that fails a generic, self-made open_error
exception is thrown (it is derived from std::exception
, yes, but it's not one of C++'s predefined exception classes). I'm trying to extend this rather bare-bones exception with an error code; to be more precise with C++11's std::error_code
if I understood correctly.
My problem is how to construct such an object from errno
(in the POSIX case) or GetLastError()
(in the Windows case). For POSIX, as far as I've understood things, I can simply use errno
in std::error_code
's constructor, e.g. like this:
std::error_code ec(errno, std::generic_category());
And that ec
should be comparable to the well-known values from std::errc
.
For Windows a similar call can be made, of course:
std::error_code ec(::GetLastError(), std::generic_category());
But I'm not sure whether or not the values returned by GetLastError()
map nicely to the well-known constants from std::errc
. I've read in Boost's system library that they do for Boost's implementation of error_code
, but I'm asking about the std
implementation, not about Boost's.
Please don't advice to switch to using C++ streams for file access. I'd love to, but refactoring half of my code is not something I'd like to do right at this very moment.
Upvotes: 36
Views: 11083
Reputation: 3246
It looks like you should use system_category()
for GetLastError()
/errno
and it will do the right thing on both platforms.
If you already have an errc
, use generic_category()
(or make_error_code
) instead.
Here's some testing with an "address already in use" error.
#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;
}
On 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
On 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
I get similar results testing with these triplets of equivalent error codes:
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
If anyone's interested, here's a list of std::errc
s mapped to ==
WinError.h
constants. This is checking 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
Upvotes: 2
Reputation: 2704
This is an old question, but I haven't really found a good answer on SO. The accepted answer confuses me a little as it seems to give an error_condition
rather than an error_code
. I have settled on the following for myself on POSIX:
std::error_code error_code_from_errno(int errno_code) {
return std::make_error_code(static_cast<std::errc>(errno_code));
}
This always gives me the correct category (generic or system). I've had problems in the past where error codes with the same errno code compared as not equal because one had generic_category
and the other had system_category
.
Upvotes: 7
Reputation: 157514
That's a quality of implementation issue. The const static object returned by std::system_category()
is relied upon to perform the mapping from the platform-native error code enumeration to the standard std::error_condition
enumeration. Under 17.6.5.14 Value of error codes [value.error.codes]:
Implementations for operating systems that are not based on POSIX are encouraged to define values identical to the operating system’s values.
You can see in http://www.boost.org/doc/libs/1_46_1/libs/system/src/error_code.cpp how Boost performs the mapping; any standard library supplied by your compiler vendor for use on Windows should do something similar.
The intended behaviour is covered in 19.5.1.5p4, describing system_category().default_error_condition(int ev)
:
If the argument
ev
corresponds to a POSIX errno valueposv
, the function shall returnerror_condition(posv, generic_category())
. Otherwise, the function shall returnerror_condition(ev, system_category())
.
So, for example, error_code(ERROR_FILE_NOT_FOUND, std::system_category()).default_error_condition()
will invoke std::system_category().default_error_condition(ERROR_FILE_NOT_FOUND)
, which should return std::error_condition(std::no_such_file_or_directory, std::generic_category())
.
Upvotes: 12