C ++ 11引入了<system_error>
标头,其中包含处理错误代码的通用系统。 std::error_code
是一个包含int
的元组,错误代码和对std::error_category
的引用,它定义了错误域和错误代码的处理。标准库包含四个类别:std::generic_category
,std::system_category
,std::future_category
和std::iostream_category
。
在使用std::error_code
和WinAPI错误代码创建std::system_error
s / throw errno
时,在SO和C ++参考网站上使用哪个类别都存在冲突:< / p>
errno
与std::generic_category
:SO answer,llvm-commits,cplusplus.com errno
与std::system_category
:SO answer,cppreference.com GetLastError()
与std::generic_category
:SO answer GetLastError()
与std::system_category
:SO answer,SO comment 但是,errno
和GetLastError()
无法使用相同的类别,否则某些错误代码将不明确。错误代码33是一个示例,因为它是EDOM
和ERROR_LOCK_VIOLATION
。
甚至有些地方主张WinAPI的用户制作类别,但目前我找不到任何引用。这种替代方案会特别痛苦。
errno
应使用哪个类别,哪个类别应与GetLastError()
一起使用,以便
std::error_code::default_error_condition()
std::error_code::message()
是否简单且适用于底层错误代码?
答案 0 :(得分:14)
system_category
C ++标准库中的某些函数报告错误 通过
std::error_code
(19.5.2.1)对象。那 对象的category()
成员应返回std::system_category()
对于源自操作系统的错误, 或者对实现定义error_category
的引用 对象来自其他地方的错误。实施 应为每一个定义value()的可能值 错误&gt;类别。 [示例:对于基于POSIX的操作系统, 鼓励实现定义std::system_category()
值与POSIXerrno
值相同,但附加值 操作系统文档定义的值。 不基于POSIX的操作系统的实现 鼓励定义与操作相同的值 系统的价值观。对于不是源自的错误 操作系统,实现可能会提供枚举 相关价值观。
不太清楚:
Windows上的errno
值会发生什么?
是来自POSIX呼叫的errno
&#34;来自操作系统&#34;或者这应该仅限于非POSIX电话?
generic_category
std::errc
是一个枚举,其值与C / POSIX EFOOBAR
错误代码相同;
每个
enum errc
常数的值应与。{ 上面的概要中显示的<cerrno>
宏的值。 是否实现暴露了 <{1}}宏未指定。
<cerrno>
使用make_error_code(std::errc)
erro_code
generic_category
返回:
error_code make_error_code(errc e) noexcept;
。
这意味着POSIX错误代码可以与error_code(static_cast<int>(e), generic_category())
一起使用。非POSIX值可能无法与generic_category
一起正常运行。在实践中,它们似乎得到了我一直在使用的实现的支持。
Boost文档对此功能非常简洁:
原始提案将错误类别视为二元选择 在errno(即POSIX风格)和本机操作系统之间 错误代码。
此外,您可以找到遗留声明,例如:
generic_catgeory
在static const error_category & errno_ecat = generic_category();
:
在API错误后构建error_code:
linux_error.hpp
在error_code( errno, system_category() )
:
在API错误后构建error_code:
windows_error.hpp
在error_code( ::GetLastError(), system_category() )
:
在API错误后构造error_code:error_code(errno,system_category())
对于Windows,Boost使用cygwin_error.hpp
表示非system_category
错误:
errno
我们在ASIO中找到了这种代码:
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() );
我们在POSIX代码中找到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;
}
为errno
:
system_category
我们在POSIX代码中找到int error = ::pthread_cond_init(&cond_, 0);
boost::system::error_code ec(error,
boost::asio::error::get_system_category());
errno
:
generic_category
我们找到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());
}
errno
:
generic_category
并且没有使用if (char* rp = ::realpath(pa.c_str(), buf.get())) {
[...]
}
if (errno != ENAMETOOLONG) {
ec.assign(errno, std::generic_category());
return result;
}
。
在实践中,似乎可以将system_category
用于非POSIX generic_category
和libstdc ++:
errno
给出:
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';
我们找到Advertise error
Advertise error
errno
:
system_category
但不使用int ec = pthread_join(__t_, 0);
if (ec)
throw system_error(error_code(ec, system_category()), "thread::join failed");
。
我在这里找不到任何一致的模式,但显然:
在Windows上使用Windows错误时,您需要使用generic_category
;
您可以安全地使用system_category
作为generic_category
的POSIX值;
您不应该将errno
用于std::generic_category
的非POSIX视图(可能不起作用);
如果您不想在基于POSIX的系统上检查您的errno
值是否为POSIX one:,那么您需要在基于POSIX的系统上,您可以errno
与{{}一起使用system_error
1}}(严格来说,对此的支持不是强制性的,只是鼓励)。errno
使用system_error
。
答案 1 :(得分:13)
对于&lt; system_error&gt;的困惑,我不得不承认一点意外。鉴于Chris在http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html中确切地总结了它是如何工作的,我个人发现上面的C ++标准文本非常清楚。但总结一下非常简洁的话:
如果在POSIX:
generic_category
=&gt; POSIX标准errno空间
system_category
=&gt;本地POSIX errno空间(通常使用专有的errno代码扩展POSIX)。使用strerror()
将代码扩展为message()
返回的字符串说明。
在POSIX的实践中,两个实现在底层相同并映射本机errno空间。
如果在Windows上:
generic_category
=&gt; POSIX标准errno空间,由MSVCRT中的各种POSIX仿真函数返回,如fopen()
等
system_category
=&gt; Win32 GetLastError()
空间。使用FormatMessage()
将代码扩展为message()
返回的字符串说明。
如何使用&lt; system_error&gt;便携强>
std::error_code ec;
#ifdef _WIN32
if((HANDLE)-1 == CreateFile(...))
ec = std::error_code(GetLastError(), std::system_category());
#else
if(-1 == open(...))
ec = std::error_code(errno, std::system_category());
#endif
// To test using portable code
if(ec == std::errc::no_such_file_or_directory)
...
// To convert into nearest portable error condition (lossy, may fail)
std::error_condition ec2(ec.default_error_condition())
其他想法:
有些评论员说过&lt; system_error&gt;设计很差,不应该使用。这根本不是真的,考虑到其设计时的C ++ 03习惯做法,它是非常优化的,它在除Dinkumware之外的所有主要STL上生成非常紧凑的高质量固定延迟代码。它的用户可以扩展到任意错误代码系统,并标准化统一到单个系统不同的第三方库错误处理。
今天看来,如果constexpr全局变量在设计时可用,那么它看起来会有所不同。也许这可能会在17之后的C ++标准中得到纠正。但如果你是一个需要四处走动的程序员来自第三方库的错误代码通过未编写的代码来了解这些第三方库,然后&lt; system_error&gt;是一个优秀的解决方案。
将其视为类似于第三方库错误代码处理的virtual
关键字 - 它消除了传输第三方代码的代码需要理解这些代码的需要。如果您的代码库中存在该问题 - 并且大多数大型代码库都存在 - 那么绝对应该使用&lt; system_error&gt;而不是您当前正在使用的任何错误代码映射或翻译系统。
答案 2 :(得分:0)
这里的混乱!
错误类别是错误的来源。 iostreams 库会产生自己的错误,因此它有自己的类别。同样,WinAPI 是其自身的错误源,因此需要用户定义的错误类别。
generic_category()
和 system_category()
都是 用于 errno 值。它们之间的区别是基于errno的值:
error_condition
error_code
<system_error>
库是围绕 error_code
和 error_condition
之间的(隐含)区别构建的。这两个类具有相同的方法和相同的接口,除了:
error_code
对象用于“低级别”和特定于系统的错误error_condition
对象是可移植错误,例如由标准定义error_category
对象,因为它们的数值被转换为不同的错误字符串。error_code
或其 error_condition
上使用 default_error_condition()
方法将系统特定的 error_code
映射到便携式 error_category
。 但是,当没有从 error_code
到 error_condition
的转换(或没有实现)时,您会得到一个基于系统特定 error_condition
的 error_category
,它已经违背了便携式映射的目的。这就是 C++ 标准库的状态,我猜......!
error_category
对象知道它们是代表可移植值还是非可移植值。 system_category()
标识系统特定值(errno
)并将它们转换为 POSIX 值(如果可用),然后使用 generic_category()
将这些值映射到 error_condition
.
每个类别都知道将数字错误转换为字符串(描述)。 system_category()
将特定的 ęrrno
值转换为字符串,最有可能使用标准的 strerror()
或 sys_errlist
。
因此,将 ::GetLastError()
中的 WinAPI 值用于此类别是一个编程错误。
例如,使用 WinSock2 API 提供来自 ::WSAGetLastError()
函数的错误值,需要另一个错误类别。