In the C++ standard:
system_category
The current C++17 draft states that:
Certain functions in the C ++ standard library report errors
via a std::error_code
(19.5.2.1) object. That
object’s category()
member shall return std::system_category()
for errors originating from the operating system,
or a reference to an implementation-defined error_category
object for errors originating elsewhere. The implementation
shall define the possible values of value() for each of these
error > categories.
[ Example: For operating systems that are based on POSIX,
implementations are encouraged to define the std::system_category()
values as identical to the POSIX errno
values, with additional
values as defined by the operating system’s documentation.
Implementations for operating systems that are not based on POSIX
are encouraged to define values identical to the operating
system’s values. For errors that do not originate from the
operating system, the implementation may provide enums for the
associated values.
It's not so clear:
generic_category
This means that POSIX error code can be used with generic_category
. Non POSIX values might possibly not work correctly with generic_catgeory
. In practice, they seem to be supported by the implementations I've been using.
In Boost
Boost system itself
The Boost documentation is quite terse about this feature:
The original proposal viewed error categories as a binary choice
between errno (i.e. POSIX-style) and the native operating system's
error codes.
Moreover you can find legacy declaration such as:
static const error_category & errno_ecat = generic_category();
In linux_error.hpp
:
To construct an error_code after a API error: error_code( errno, system_category() )
In windows_error.hpp
:
To construct an error_code after a API error: error_code( ::GetLastError(), system_category() )
In cygwin_error.hpp
:
To construct an error_code after a API error: error_code( errno, system_category() )
For Windows, Boost uses system_category
for non errno
errors:
ec = error_code( ERROR_ACCESS_DENIED, system_category() );
ec = error_code( ERROR_ALREADY_EXISTS, system_category() );
ec = error_code( ERROR_BAD_UNIT, system_category() );
ec = error_code( ERROR_WRITE_PROTECT, system_category() );
ec = error_code( WSAEWOULDBLOCK, system_category() );
In ASIO
We find this kind of code in ASIO:
template <typename ReturnType>
inline ReturnType error_wrapper(ReturnType return_value,
boost::system::error_code& ec)
{
#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
ec = boost::system::error_code(WSAGetLastError(),
boost::asio::error::get_system_category());
#else
ec = boost::system::error_code(errno,
boost::asio::error::get_system_category());
#endif
return return_value;
}
We find errno
as system_category
in POSIX code:
int error = ::pthread_cond_init(&cond_, 0);
boost::system::error_code ec(error,
boost::asio::error::get_system_category());
Filesystem
We find errno
with generic_category
in POSIX code:
if (::chmod(p.c_str(), mode_cast(prms)))
{
if (ec == 0)
BOOST_FILESYSTEM_THROW(filesystem_error(
"boost::filesystem::permissions", p,
error_code(errno, system::generic_category())));
else
ec->assign(errno, system::generic_category());
}
In GNU libstdc++
Filesystem
We find errno
with generic_category
:
if (char* rp = ::realpath(pa.c_str(), buf.get())) {
[...]
}
if (errno != ENAMETOOLONG) {
ec.assign(errno, std::generic_category());
return result;
}
and no usage of system_category
.
Using libstdc++
In practice, it seems you can use generic_category
for non-POSIX errno
with libstdc++:
std::error_code a(EADV, std::generic_category());
std::error_code b(EADV, std::system_category());
std::cerr << a.message() << '\n';
std::cerr << b.message() << '\n';
Gives:
Advertise error
Advertise error
Libc++
We find errno
with system_category
:
int ec = pthread_join(__t_, 0);
if (ec)
throw system_error(error_code(ec, system_category()), "thread::join failed");
but no usage of generic_category
.
Conclusion
I don't find any consistent pattern here but apparently:
you are expected to use system_category
when using Windows error on Windows;
you can safely use generic_category
for POSIX values of errno
;
you are not supposed to be able to use std::generic_category
for non-POSIX vales of errno
(it might not work);
If you do not want to check if your errno
value is a POSIX one: on POSIX-based systems you are expected to be able to use system_error
with errno
(strictly speaking the support for this is not mandated, only encouraged). on POSIX-based systems you can use system_error
with errno
.
New proposals (Update 2019-12)
There is a proposal to introduce a new error systems (std::error
, std::status_code
).
See the relevant discussion and its section 4 for a discussion about the issues with the <system_error>
facilities:
- use of std::string
- proliferation of "two-API" libraries
- no wording sets aside the 0 enumerator
- reliance on singletons
- no error_category subclass can be a literal type
- no guidance on attaching extra information to error_code
- reliance on a surprising overload of operator==
- error_category should properly have been named error_domain
- standard error_code-yielding functions can throw exceptions anyway
- underspecified error_code comparison semantics