<system_error>类别和标准/系统错误代码</system_error>

时间:2015-02-26 15:27:36

标签: c++ c++11 winapi posix system-error

C ++ 11引入了<system_error>标头,其中包含处理错误代码的通用系统。 std::error_code是一个包含int的元组,错误代码和对std::error_category的引用,它定义了错误域和错误代码的处理。标准库包含四个类别:std::generic_categorystd::system_categorystd::future_categorystd::iostream_category

在使用std::error_code和WinAPI错误代码创建std::system_error s / throw errno时,在SO和C ++参考网站上使用哪个类别都存在冲突:< / p>

但是,errnoGetLastError()无法使用相同的类别,否则某些错误代码将不明确。错误代码33是一个示例,因为它是EDOMERROR_LOCK_VIOLATION

甚至有些地方主张WinAPI的用户制作类别,但目前我找不到任何引用。这种替代方案会特别痛苦。

errno应使用哪个类别,哪个类别应与GetLastError()一起使用,以便

  • std::error_code::default_error_condition()
  • std::error_code::message()

是否简单且适用于底层错误代码?

3 个答案:

答案 0 :(得分:14)

在C ++标准中:

system_category

current C++17 draft表示:

  

C ++标准库中的某些函数报告错误   通过std::error_code(19.5.2.1)对象。那   对象的category()成员应返回std::system_category()   对于源自操作系统的错误,   或者对实现定义error_category的引用   对象来自其他地方的错误。实施   应为每一个定义value()的可能值   错误&gt;类别。   [示例:对于基于POSIX的操作系统,   鼓励实现定义std::system_category()   值与POSIX errno值相同,但附加值   操作系统文档定义的值。   不基于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中

提升系统本身

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中

我们在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

在GNU libstdc ++

文件系统

我们找到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; }

使用libstdc ++

在实践中,似乎可以将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';

Libc函数++

我们找到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:,那么您需要errno与{{}一起使用system_error 1}}(严格来说,对此的支持不是强制性的,只是鼓励)。在基于POSIX的系统上,您可以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的值:

  • POSIX 指定的值与 generic_category() 一起使用以创建可移植的 error_condition
  • 具有 POSIX 等效项的值被翻译并与 generic_category() 一起使用
  • 操作系统实现提供的值,在 POSIX 值之外,与 system_category() 一起使用 -- 创建不可移植的 error_code

<system_error> 库是围绕 error_codeerror_condition 之间的(隐含)区别构建的。这两个类具有相同的方法和相同的接口,除了:

  • error_code 对象用于“低级别”和特定于系统的错误
  • error_condition 对象是可移植错误,例如由标准定义
  • 它们使用不同的 error_category 对象,因为它们的数值被转换为不同的错误字符串。
  • 您可以尝试在 error_code 或其 error_condition 上使用 default_error_condition() 方法将系统特定的 error_code 映射到便携式 error_category

但是,当没有从 error_codeerror_condition 的转换(或没有实现)时,您会得到一个基于系统特定 error_conditionerror_category,它已经违背了便携式映射的目的。这就是 C++ 标准库的状态,我猜......!

error_category 对象知道它们是代表可移植值还是非可移植值。 system_category() 标识系统特定值(errno)并将它们转换为 POSIX 值(如果可用),然后使用 generic_category() 将这些值映射到 error_condition .

每个类别都知道将数字错误转换为字符串(描述)。 system_category() 将特定的 ęrrno 值转换为字符串,最有可能使用标准的 strerror()sys_errlist

因此,将 ::GetLastError() 中的 WinAPI 值用于此类别是一个编程错误。

例如,使用 WinSock2 API 提供来自 ::WSAGetLastError() 函数的错误值,需要另一个错误类别。