在 C++ 标准中:
system_category
current C++17 draft 声明:
C++ 标准库中的某些函数报告错误
通过std::error_code (19.5.2.1) 对象。那
对象的category() 成员应返回std::system_category()
对于源自操作系统的错误,
或对实现定义的error_category 的引用
对象来自其他地方的错误。实施
应为这些中的每一个定义 value() 的可能值
错误 > 类别。
[ 示例:对于基于 POSIX 的操作系统,
鼓励实现定义std::system_category()
值与 POSIX errno 值相同,具有附加的
操作系统文档定义的值。
不基于 POSIX 的操作系统的实现
鼓励定义与操作相同的值
系统的价值观。对于并非源自
操作系统,实现可以为
关联值。
不是很清楚:
generic_category
这意味着 POSIX 错误代码可以与 generic_category 一起使用。非 POSIX 值可能无法与 generic_catgeory 一起正常工作。在实践中,它们似乎得到了我一直在使用的实现的支持。
在增强中
Boost 系统本身
Boost 文档对此功能非常简洁:
最初的提议将错误类别视为二元选择
在 errno(即 POSIX 风格)和本机操作系统的
错误代码。
此外,您还可以找到遗留声明,例如:
static const error_category & errno_ecat = generic_category();
在linux_error.hpp:
在 API 错误后构造 error_code:error_code( errno, system_category() )
在windows_error.hpp:
在 API 错误后构造 error_code:error_code( ::GetLastError(), system_category() )
在cygwin_error.hpp:
在 API 错误后构造 error_code:error_code( errno, system_category() )
对于 Windows,Boost 使用 system_category 处理非 errno 错误:
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() );
在 ASIO
我们在 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;
}
我们在 POSIX 代码中发现 errno 为 system_category:
int error = ::pthread_cond_init(&cond_, 0);
boost::system::error_code ec(error,
boost::asio::error::get_system_category());
文件系统
我们在 POSIX 代码中找到 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());
}
在 GNU libstdc++ 中
文件系统
我们找到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。
使用 libstdc++
实际上,您似乎可以将generic_category 用于非POSIX errno 和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';
给予:
Advertise error
Advertise error
Libc++
我们找到errno 和system_category:
int ec = pthread_join(__t_, 0);
if (ec)
throw system_error(error_code(ec, system_category()), "thread::join failed");
但没有使用generic_category。
结论
我在这里找不到任何一致的模式,但显然:
在 Windows 上使用 Windows 错误时,您应该使用 system_category;
您可以安全地将generic_category 用于errno 的POSIX 值;
您不应该将std::generic_category 用于errno 的非POSIX 值(它可能不起作用);
如果您不想检查您的 errno 值是否为 POSIX 值:在基于 POSIX 的系统上,您应该能够使用 system_error 和 errno(严格来说对此的支持不是强制性的,只是鼓励)。在基于 POSIX 的系统上,您可以使用 system_error 和 errno。
新提案(2019-12 更新)
有人提议引入一个新的错误系统(std::error,std::status_code)。
有关<system_error> 设施问题的讨论,请参阅relevant discussion 及其第4 节:
- std::string 的使用
- “双 API”库的激增
- 没有任何措辞将 0 枚举数放在一边
- 对单例的依赖
- error_category 子类不能是文字类型
- 没有关于将额外信息附加到 error_code 的指导
- 依赖于令人惊讶的 operator== 过载
- error_category 应该正确命名为 error_domain
- 标准的 error_code-yielding 函数无论如何都会抛出异常
- 未指定的 error_code 比较语义