简介

源自操作系统或其他底层应用程序接口 (API) 的错误通常通过整数表示的错误代码来报告,可以通过直接从函数返回代码(例如 pthread_mutex_init),或者使用边信道,例如 POSIX 下的 errno 伪变量或 Windows 下的 GetLastError()

但是,只有当已知这些整数错误值的来源时,才能对其进行解释。在 Windows 下,当 GetLastError() 返回值 5 时,表示 ERROR_ACCESS_DENIED,但当从 errno 检索时,表示 EIO。相反,相同的错误条件“拒绝访问”在 GetLastError() 返回时用值 5 表示,在从 errno 检索时用 13 (EACCES) 表示。

这意味着,为了使代码能够处理来自这两个来源的错误(检索描述错误的文本消息,或检查错误是否表示“拒绝访问”),它需要知道整数错误值的来源。为了实现这一点,整数错误值需要附带一条标识来源的信息。

Boost.System 提供了一个框架,使这成为可能。错误由类 error_code 表示,该类同时包含错误值和指向其来源(称为“类别”)的指针,来源表示为从 error_category 派生的类。

类别提供了成员函数,例如 message,它为特定的错误值返回文本消息;以及 equivalent,它可以用于测试特定的错误值是否对应于错误条件,例如“拒绝访问”。error_code 在其 messageoperator== 成员函数的实现中使用了这些类别提供的函数。

Boost.System 包含两个预定义的类别类,通用类别(通过 generic_category() 返回对其的引用)和系统类别 (system_category())。通用类别表示 POSIX 标准定义的 errno 值的可移植子集的错误值,而系统类别是操作系统相关的。在 POSIX 下,系统类别表示操作系统 API 返回的 errno 值(通用类别中的值的超集),而在 Windows 下,系统类别表示 GetLastError() 返回的错误值。

该框架是可扩展的。用户可以通过从 error_category 派生一个类并实现一个返回对其实例引用的函数来定义自己的类别。此功能对于描述库定义的错误值以及适配返回整数错误值的现有 C API 库都很有用。

对于那些喜欢通过异常报告错误的人,Boost.System 提供了一个标准异常类 system_error,它存储一个 error_code

Boost.System 在 C++11 中被标准化为 <system_error>。一段时间以来,两者是等效的,但 Boost.System 从那时起不断发展,现在包含许多对其标准同级产品的扩展

  • message 的非分配重载;

  • 通过 failed 成员函数支持非零错误代码表示成功;

  • 支持 64 位类别标识符,以解决有时无法确保程序中只存在一个类别实例的问题;

  • 支持将源位置(文件/行/函数)附加到错误代码;

  • 一个类 result<T>,可用于从函数返回一个值或一个错误代码;

  • 各种其他小的改进。

boost::system::error_code 可以转换为 std::error_code,也可以从 std::error_code 构造。

用法

以下所有代码片段都假定这些行

#include <boost/system.hpp>
namespace sys = boost::system;

生效。

从 POSIX 下的操作系统 API 返回错误

假设我们正在实现一个可移植的 file 包装器,用于操作系统文件 API。其一般轮廓如下所示

class file
{
private:

    int fd_;

public:

    // ...

    std::size_t read( void * buffer, std::size_t size, sys::error_code& ec );
    std::size_t write( void const * buffer, std::size_t size, sys::error_code& ec );
};

由于我们正在实现 file 的 POSIX 版本,因此其数据成员是 POSIX 文件描述符 int fd_;,尽管其他实现会有所不同。

我们的 readwrite 函数返回传输的字节数,并通过 ec 输出参数(类型为 boost::system::error_code)来指示错误。

file::read 的实现可能如下所示

std::size_t file::read( void * buffer, std::size_t size, sys::error_code& ec )
{
    ssize_t r = ::read( fd_, buffer, size );

    if( r < 0 )
    {
        ec.assign( errno, sys::system_category() );
        return 0;
    }

    ec = {}; // ec.clear(); under C++03
    return r;
}

我们首先调用 POSIX API read;如果它返回错误,我们使用系统类别将 errno 值存储在 ec 中,并返回 0 作为传输的字节数。否则,我们清除 ec 以表示成功,并返回 ::read 的结果。

注意
在成功返回时清除 ec 是一个重要的步骤;不要省略它。

在 POSIX 下,系统类别对应于 POSIX errno 值,这就是我们使用它的原因。

原则上,由于通用类别对应于所有平台下的 errno 值,我们本可以使用它;但是,按照 POSIX 下的惯例,如果 errno 值来自操作系统(“系统”),我们为其使用系统类别。这是因为系统类别值可能是通用(平台无关)值的平台特定超集。

file::write 的实现基本相同。我们在此处显示它以供完整性参考

std::size_t file::write( void const * buffer, std::size_t size, sys::error_code& ec )
{
    ssize_t r = ::write( fd_, buffer, size );

    if( r < 0 )
    {
        ec.assign( errno, sys::system_category() );
        return 0;
    }

    ec = {}; // ec.clear(); under C++03
    return r;
}

从 Windows 下的操作系统 API 返回错误

在 Windows 下,我们的 file 对象将存储一个 HANDLE 而不是 int

class file
{
private:

    HANDLE fh_;

public:

    // as before
};

并且 file::read 的实现将如下所示

std::size_t file::read( void * buffer, std::size_t size, sys::error_code& ec )
{
    DWORD r = 0;

    if( ::ReadFile( fh_, buffer, size, &r, 0 ) )
    {
        // success
        ec = {}; // ec.clear(); under C++03
    }
    else
    {
        // failure
        ec.assign( ::GetLastError(), sys::system_category() );
    }

    // In both cases, r is bytes transferred
    return r;
}

在此,系统类别对应于系统头文件 <winerror.h> 中定义并由 GetLastError() 返回的值。由于我们使用 Win32 API ReadFile 来实现 file::read,并且它通过 GetLastError() 返回错误代码,因此我们再次将该值存储在 ec 中,并将其归类为系统类别。

file::write 的实现再次相同。

std::size_t file::write( void const * buffer, std::size_t size, sys::error_code& ec )
{
    DWORD r = 0;

    if( ::WriteFile( fh_, buffer, size, &r, 0 ) )
    {
        ec = {}; // ec.clear(); under C++03
    }
    else
    {
        ec.assign( ::GetLastError(), sys::system_category() );
    }

    return r;
}

在 POSIX 下返回特定错误

我们的 file::read 实现存在一个问题;它接受 std::size_t 值作为 size,但是当请求的值不适合 ssize_t 时,::read 的行为未指定。为了避免依赖未指定的行为,让我们添加对此条件的检查并返回错误

std::size_t file::read( void * buffer, std::size_t size, sys::error_code& ec )
{
    if( size > SSIZE_MAX )
    {
        ec.assign( EINVAL, sys::generic_category() );
        return 0;
    }

    ssize_t r = ::read( fd_, buffer, size );

    if( r < 0 )
    {
        ec.assign( errno, sys::system_category() );
        return 0;
    }

    ec = {}; // ec.clear(); under C++03
    return r;
}

在这种情况下,由于我们返回固定的 errnoEINVAL,它是通用类别定义的便携式子集的一部分,因此我们将 ec 中的错误值标记为属于通用类别。

也可以使用系统类别,因为 EINVAL 在 POSIX 下也是系统类别值;但是,对于属于便携式 errno 子集的值,使用通用类别稍微更可取。

我们的 file::write 实现需要进行类似的处理。但是,在那里,我们将应用另一个更改。当磁盘上没有剩余空间时,::write 返回的写入字节数低于我们用 size 请求的字节数,但我们的函数不发出错误信号。在这种情况下,我们将使其返回 ENOSPC

std::size_t file::write( void const * buffer, std::size_t size, sys::error_code& ec )
{
    if( size > SSIZE_MAX )
    {
        ec.assign( EINVAL, sys::generic_category() );
        return 0;
    }

    ssize_t r = ::write( fd_, buffer, size );

    if( r < 0 )
    {
        ec.assign( errno, sys::system_category() );
        return 0;
    }

    if( r < size )
    {
        ec.assign( ENOSPC, sys::system_category() );
    }
    else
    {
        ec = {}; // ec.clear(); under C++03
    }

    return r;
}

我们使用了系统类别来使其看起来 ENOSPC 值来自 ::write API,主要是为了说明这也是一种可能的方法。使用通用值也可以正常工作。

在 Windows 下返回特定错误

没什么好说的;Windows 下的情况完全相同。唯一的区别是我们必须使用通用类别来返回 errno 值。系统类别不起作用;系统类别中的整数值与通用类别中的整数值完全不同。

std::size_t file::read( void * buffer, std::size_t size, sys::error_code& ec )
{
    DWORD r = 0;

    if( size > MAXDWORD )
    {
        ec.assign( EINVAL, sys::generic_category() );
    }
    else if( ::ReadFile( fh_, buffer, size, &r, 0 ) )
    {
        ec = {}; // ec.clear(); under C++03
    }
    else
    {
        ec.assign( ::GetLastError(), sys::system_category() );
    }

    return r;
}

std::size_t file::write( void const * buffer, std::size_t size, sys::error_code& ec )
{
    DWORD r = 0;

    if( size > MAXDWORD )
    {
        ec.assign( EINVAL, sys::generic_category() );
    }
    else if( ::WriteFile( fh_, buffer, size, &r, 0 ) )
    {
        if( r < size )
        {
            ec.assign( ENOSPC, sys::generic_category() );
        }
        else
        {
            ec = {}; // ec.clear(); under C++03
        }
    }
    else
    {
        ec.assign( ::GetLastError(), sys::system_category() );
    }

    return r;
}

将源位置附加到错误代码

与标准 <system_error> 不同,Boost.System 允许将源位置(文件/行/函数)存储在 error_code 中,以便处理错误的函数可以显示或记录错误发生的源代码位置。为了利用此功能,我们的 POSIX file::read 函数需要如下增强

std::size_t file::read( void * buffer, std::size_t size, sys::error_code& ec )
{
    if( size > SSIZE_MAX )
    {
        static constexpr boost::source_location loc = BOOST_CURRENT_LOCATION;
        ec.assign( EINVAL, sys::generic_category(), &loc );
        return 0;
    }

    ssize_t r = ::read( fd_, buffer, size );

    if( r < 0 )
    {
        static constexpr boost::source_location loc = BOOST_CURRENT_LOCATION;
        ec.assign( errno, sys::system_category(), &loc );
        return 0;
    }

    ec = {}; // ec.clear(); under C++03
    return r;
}

也就是说,在每个 ec.assign 语句之前,我们需要声明一个 static constexpr 变量来保存当前源位置,然后将指向它的指针传递给 assign。由于 error_code 很小,并且其中没有空间容纳超过一个指针,因此我们不能直接按值在其中存储 source_location

BOOST_CURRENT_LOCATION 是一个宏,它扩展为当前源位置(__FILE____LINE__BOOST_CURRENT_FUNCTION 的组合)。它在 Boost.Assert 中定义和文档化。

在 C++03 下,需要使用 static const 而不是 static constexpr。另一种选择是 BOOST_STATIC_CONSTEXPR,这是一个 Boost.Config 宏,它根据需要扩展为 static constexprstatic const

为了避免每次执行 ec.assign 时都重复此样板代码,我们可以定义一个宏

#define ASSIGN(ec, ...) { \
    BOOST_STATIC_CONSTEXPR boost::source_location loc = BOOST_CURRENT_LOCATION; \
    (ec).assign(__VA_ARGS__, &loc); }

现在我们可以使用它来增强,例如,file::write 的 POSIX 实现

std::size_t file::write( void const * buffer, std::size_t size, sys::error_code& ec )
{
    if( size > SSIZE_MAX )
    {
        ASSIGN( ec, EINVAL, sys::generic_category() );
        return 0;
    }

    ssize_t r = ::write( fd_, buffer, size );

    if( r < 0 )
    {
        ASSIGN( ec, errno, sys::system_category() );
        return 0;
    }

    if( r < size )
    {
        ASSIGN( ec, ENOSPC, sys::generic_category() );
    }
    else
    {
        ec = {}; // ec.clear(); under C++03
    }

    return r;
}

获取错误代码的文本表示形式以进行日志记录和显示

假设我们有一个 error_code 实例 ec,由某个函数返回给我们,我们有多种方法可以获得其中表示的错误代码的文本表示形式。

ec.to_string() 为我们提供了将 ec 流式传输到 std::ostream 的结果,例如,如果 std::cout << ec << std::endl; 输出 system:6,这就是 ec.to_string() 将返回的内容。(Windows 下的 system:6 是来自 <winerror.h>ERROR_INVALID_HANDLE。)

要获得与此代码对应的可读错误消息,我们可以使用 ec.message()。对于 ERROR_INVALID_HANDLE,它将为我们提供“句柄无效” - 可能是本地化的。

如果 ec 包含源位置,我们可以通过 ec.location().to_string() 获得其文本表示形式。这将为我们提供类似以下的内容

C:\Projects\testbed2019\testbed2019.cpp:98 in function 'unsigned __int64 __cdecl file::read(void *,unsigned __int64,class boost::system::error_code &)'

如果 ec 中存在位置,以及

(unknown source location)

如果不存在。(当 ec 包含位置时,ec.has_location()true。)

最后,ec.what() 将为我们提供一个包含上述所有内容的字符串,类似于

The handle is invalid [system:6 at C:\Projects\testbed2019\testbed2019.cpp:98 in function 'unsigned __int64 __cdecl file::read(void *,unsigned __int64,class boost::system::error_code &)']

大多数不面向最终用户的日志记录和诊断输出可能会最终使用 what()。(ec.what(),加上构造时提供的 prefix 前缀,也是 boost::system::system_error::what() 将返回的内容。)

组合返回错误代码的函数

假设我们需要实现一个文件复制函数,具有以下接口

std::size_t file_copy( file& src, file& dest, sys::error_code& ec );

file_copy 使用 src.readsrc 读取字节,然后使用 dest.write 将这些字节写入 dest。这将持续进行,直到其中一个操作发出错误信号,或者直到到达文件末尾。它返回写入的字节数,并使用 ec 发出错误信号。

这是一个可能的实现

std::size_t file_copy( file& src, file& dest, sys::error_code& ec )
{
    std::size_t r = 0;

    for( ;; )
    {
        unsigned char buffer[ 1024 ];

        std::size_t n = src.read( buffer, sizeof( buffer ), ec );

        // read failed, leave the error in ec and return
        if( ec.failed() ) return r;

        // end of file has been reached, exit loop
        if( n == 0 ) return r;

        r += dest.write( buffer, n, ec );

        // write failed, leave the error in ec and return
        if( ec.failed() ) return r;
    }
}

请注意,POSIX 和 Windows 实现之间不再有任何区别;它们的差异包含在 file::readfile::write 中。file_copy 是可移植的,并且可以在任何平台下工作。

编写此类更高级别函数的通用模式是,它们将从调用者收到的输出 error_code 参数 ec 直接作为输出参数传递给它们所构建的较低级别函数。这样,当它们检测到中间操作中的失败时(通过测试 ec.failed()),它们可以立即返回给调用者,因为错误代码已在其正确的位置。

请注意,file_copy 甚至不需要通过使用 ec = {}; 在成功时清除 ec。由于我们已经测试过 ec.failed(),我们知道 ec 包含一个表示成功的值。

提供双重(抛出和非抛出)重载

通过输出 error_code& ec 参数发出错误信号的函数要求调用者在调用它们之后检查 ec,并采取适当的措施(例如立即返回,如上所述)。忘记检查 ec 会导致逻辑错误。

虽然对于某些人来说,这是一种首选的编码风格,但其他人更喜欢异常,而异常是无法忘记检查的。

Boost.Filesystem(后来变成了 std::filesystem)引入的一种方法是提供两种替代方案:一个非抛出函数,它接受 error_code& ec,如上面的 file_copy,以及一个抛出函数,它不接受 error_code 输出参数,并在失败时抛出异常。

这就是这个第二个抛出函数的典型实现方式

std::size_t file_copy( file& src, file& dest )
{
    sys::error_code ec;
    std::size_t r = file_copy( src, dest, ec );

    if( ec.failed() ) throw sys::system_error( ec, __func__ );

    return r;
}

也就是说,我们只需调用 file_copy 的非抛出重载,如果它在 ec 中发出失败信号,则抛出 system_error 异常。

我们使用我们的函数名 __func__ ("file_copy") 作为前缀,但这只是个人喜好问题。

请注意,通常在这种风格下,接受 error_code& ec 的重载会用 noexcept 修饰,以便清楚地表明它们不抛出异常(尽管我们在前面的示例中没有这样做,以便保持代码对 C++03 友好)。

result<T> 作为双重 API 的替代方案

除了为每个操作提供两个函数之外,另一种替代方法是使函数返回 sys::result<T> 而不是 Tresult<T> 是一个类,它保存 Terror_code,类似于 variant<T, error_code>

喜欢检查错误而不依赖异常的客户端可以通过 if( r ) 或其更详细的等效形式 if( r.has_value() ) 来测试 result<T> r 是否包含值,然后通过 *rr.value() 获取值。如果 r 不包含值,则可以使用 r.error() 获取它保存的 error_code

那些喜欢异常的人只需直接调用 r.value(),而无需检查。在无值的情况下,这将自动抛出一个与 r 中的 error_code 相对应的 system_error

假设我们的基本 file API 没有更改,则 file_copy 的这种变体将如下所示

sys::result<std::size_t> file_copy( file& src, file& dest )
{
    std::size_t r = 0;
    sys::error_code ec;

    for( ;; )
    {
        unsigned char buffer[ 1024 ];

        std::size_t n = src.read( buffer, sizeof( buffer ), ec );

        if( ec.failed() ) return ec;
        if( n == 0 ) return r;

        r += dest.write( buffer, n, ec );

        if( ec.failed() ) return ec;
    }
}

这里唯一的区别是我们在错误时返回 ec,而不是 r

但是请注意,我们不能再同时返回错误代码和传输的字节数;也就是说,我们不能再发出部分成功信号。这在更高级别通常不是问题,但诸如 file::readfile::write 之类的较低级别原语最好使用旧样式编写。

尽管如此,为了演示如何组合返回 result 的 API,我们将展示如果 file::readfile::write 返回 result<size_t>file_copy 将如何编写

class file
{
public:

    // ...

    sys::result<std::size_t> read( void * buffer, std::size_t size );
    sys::result<std::size_t> write( void const * buffer, std::size_t size );
};

sys::result<std::size_t> file_copy( file& src, file& dest )
{
    std::size_t m = 0;

    for( ;; )
    {
        unsigned char buffer[ 1024 ];

        auto r = src.read( buffer, sizeof( buffer ) );
        if( !r ) return r;

        std::size_t n = *r;
        if( n == 0 ) return m;

        auto r2 = dest.write( buffer, n );
        if( !r2 ) return r2;

        std::size_t n2 = *r2;
        m += n2;
    }
}

测试特定错误条件

假设我们调用了一个使用 error_code 发出失败信号的函数,我们向其传递了一个 error_code 变量 ec,现在出于某种原因想检查该函数是否因错误代码 EINVAL “无效参数”而失败。

由于 error_code 可以进行相等性比较,因此我们的第一个本能可能是 if( ec == error_code( EINVAL, generic_category() )

这是错误的,我们永远不应该这样做。

首先,在 POSIX 下,该函数可能从系统类别返回 EINVAL(因为错误可能由操作系统 API 返回,而不是由函数本身返回,就像我们在 readwrite 实现中所做的那样)。

由于 error_code 比较是精确的,因此来自通用类别的 EINVAL 与来自系统类别的 EINVAL 不相等。

(在您开始考虑仅将 ec.value()EINVAL 进行比较之前,请继续阅读。)

其次,在 Windows 下,该函数可能返回 error_code( ERROR_INVALID_PARAMETER, system_category() )。正如我们已经提到的,Windows 下系统类别中的整数错误值与整数 errno 值完全无关。

正确的方法是将 ecerror_condition( EINVAL, generic_category() ) 进行比较,而不是与特定的错误代码进行比较。错误条件是一种平台无关的方式来表示具体错误代码的含义。在我们的例子中,在 POSIX 和 Windows 下,所有表示 EINVAL 的错误代码都将与 error_condition( EINVAL, generic_category() ) 相等。

简而言之,您永远不应将错误代码与错误代码进行比较,而应将它们与错误条件进行比较。这就是 error_condition 类的目的,而这经常被误解。

由于

if( ec == sys::error_condition( EINVAL, sys::generic_category() ) )
{
    // handle EINVAL
}

有点冗长,Boost.System 为 errno 值提供了枚举器值,可以直接将错误代码与这些值进行比较。

这些枚举器在 <boost/system/errc.hpp> 中定义,并使上述测试可以写成

if( ec == sys::errc::invalid_argument )
{
    // handle EINVAL
}

作为最佳实践,这通常是测试特定错误条件时应使用的方法。

适配现有的整数错误值

具有 C(或 extern "C")API 的库通常通过返回库特定的整数错误代码来发出失败信号(通常保留零表示“无错误”)。在编写可移植的 C++ 包装器时,我们需要决定如何公开这些错误代码,而使用 error_code 是一种很好的方法。

由于整数错误代码是库特定的,并且通常与 errno 值或系统类别值都不匹配,因此我们需要定义一个库特定的错误类别。

适配 SQLite 错误

我们将以 SQLite 为例。自定义错误类别的一般轮廓如下

class sqlite3_category_impl: public sys::error_category
{
    // TODO add whatever's needed here
};

sys::error_category const& sqlite3_category()
{
    static const sqlite3_category_impl instance;
    return instance;
}

然后可以像预定义的通用和系统类别一样使用它

int r = some_sqlite3_function( ... );
ec.assign( r, sqlite3_category() );

如果我们尝试按原样编译上述类别定义,它会抱怨我们没有实现两个纯虚成员函数 namemessage,因此至少,我们需要添加这些函数。此外,我们还将实现 message 的非分配重载。它不是纯虚函数,但其默认实现会调用返回 std::string 的重载,而这几乎永远不是人们想要的。(提供此默认实现只是为了向后兼容,以便不破坏在此重载添加之前编写的现有用户定义的类别。)

因此,我们需要实现的最小值是这个

class sqlite3_category_impl: public sys::error_category
{
public:

    const char * name() const noexcept;
    std::string message( int ev ) const;
    char const * message( int ev, char * buffer, std::size_t len ) const noexcept;
};

name 很简单,它只返回类别名称

const char * sqlite3_category_impl::name() const noexcept
{
    return "sqlite3";
}

message 用于获取给定整数错误代码的错误消息。SQLite 提供了函数 sqlite3_errstr 来实现此目的,因此我们无需做任何工作

std::string sqlite3_category_impl::message( int ev ) const
{
    return sqlite3_errstr( ev );
}

char const * sqlite3_category_impl::message( int ev, char * buffer, std::size_t len ) const noexcept
{
    std::snprintf( buffer, len, "%s", sqlite3_errstr( ev ) );
    return buffer;
}

我们就完成了。现在可以将 sqlite3_category() 像预定义的类别一样使用,我们可以通过 ec.assign( r, sqlite3_category() ) 将 SQLite 错误代码 int r 放入 Boost.System error_code ec 中。

适配 ZLib 错误

另一个广泛使用的 C 库是 ZLib,下面显示了 zlib.h 中定义其错误代码的部分

#define Z_OK            0
#define Z_STREAM_END    1
#define Z_NEED_DICT     2
#define Z_ERRNO        (-1)
#define Z_STREAM_ERROR (-2)
#define Z_DATA_ERROR   (-3)
#define Z_MEM_ERROR    (-4)
#define Z_BUF_ERROR    (-5)
#define Z_VERSION_ERROR (-6)
/* Return codes for the compression/decompression functions. Negative values
 * are errors, positive values are used for special but normal events.
 */

与之前的 SQLite 示例相比,有三个相关的区别

  • 虽然对于 SQLite 来说,所有非零值都是错误,这是一种典型情况,但在这里,负值是错误,而正值是“特殊但正常”的,也就是说,它们表示成功,而不是失败;

  • ZLib 不提供返回与特定错误代码对应的错误消息的函数;

  • 当返回 Z_ERRNO 时,应从 errno 中检索错误代码。

我们的类别实现将如下所示

class zlib_category_impl: public sys::error_category
{
public:

    const char * name() const noexcept;

    std::string message( int ev ) const;
    char const * message( int ev, char * buffer, std::size_t len ) const noexcept;

    bool failed( int ev ) const noexcept;
};

sys::error_category const& zlib_category()
{
    static const zlib_category_impl instance;
    return instance;
}

与往常一样,name 的实现很简单

const char * zlib_category_impl::name() const noexcept
{
    return "zlib";
}

这次我们需要更加努力地实现 message,因为没有现有的函数可以依赖

char const * zlib_category_impl::message( int ev, char * buffer, std::size_t len ) const noexcept
{
    switch( ev )
    {
    case Z_OK:            return "No error";
    case Z_STREAM_END:    return "End of stream";
    case Z_NEED_DICT:     return "A dictionary is needed";
    case Z_ERRNO:         return "OS API error";
    case Z_STREAM_ERROR:  return "Inconsistent stream state or invalid argument";
    case Z_DATA_ERROR:    return "Data error";
    case Z_MEM_ERROR:     return "Out of memory";
    case Z_BUF_ERROR:     return "Insufficient buffer space";
    case Z_VERSION_ERROR: return "Library version mismatch";
    }

    std::snprintf( buffer, len, "Unknown zlib error %d", ev );
    return buffer;
}

这是非抛出 message 重载的典型实现。请注意,message 可以返回与 buffer 不同的内容,这意味着我们可以直接返回字符字面量,而无需先将其复制到提供的缓冲区中。这允许我们的函数即使在缓冲区太小时也返回正确的消息文本。

messagestd::string 重载现在很简单

std::string zlib_category_impl::message( int ev ) const
{
    char buffer[ 64 ];
    return this->message( ev, buffer, sizeof( buffer ) );
}

最后,我们需要实现 failed,以便覆盖其对所有非零值都返回 true 的默认行为

bool zlib_category_impl::failed( int ev ) const noexcept
{
    return ev < 0;
}

这完成了 zlib_category() 的实现并处理了上面的前两个要点,但我们仍然没有解决第三个要点;即,我们需要在 Z_ERRNO 情况下从 errno 中检索错误。

为此,我们将定义一个辅助函数,该函数将用于将 ZLib 错误代码分配给 error_code

void assign_zlib_error( sys::error_code & ec, int r )
{
    if( r != Z_ERRNO )
    {
        ec.assign( r, zlib_category() );
    }
    else
    {
        ec.assign( errno, sys::generic_category() );
    }
}

这样,代码将执行以下操作,而不是直接使用 ec.assign( r, zlib_category() )

int r = some_zlib_function( ... );
assign_zlib_error( ec, r );

我们可以在这里停止,因为这涵盖了我们计划完成的所有内容,但是我们可以采取额外的步骤并为我们的错误代码启用源位置。为此,我们需要更改 assign_zlib_error 以接受 source_location

void assign_zlib_error( sys::error_code & ec, int r, boost::source_location const* loc )
{
    if( r != Z_ERRNO )
    {
        ec.assign( r, zlib_category(), loc );
    }
    else
    {
        ec.assign( errno, sys::generic_category(), loc );
    }
}

定义一个辅助宏以避免每次都定义 static constexpr 源位置对象的样板代码

#define ASSIGN_ZLIB_ERROR(ec, r) { \
    BOOST_STATIC_CONSTEXPR boost::source_location loc = BOOST_CURRENT_LOCATION; \
    assign_zlib_error( ec, r, &loc ); }

然后使用宏而不是函数

int r = some_zlib_function( ... );
ASSIGN_ZLIB_ERROR( ec, r );

支持与条件进行比较

我们注意到,一些 ZLib 错误代码对应于可移植的 errno 条件。例如,在 POSIX 函数会返回 EINVAL 的情况下,返回 Z_STREAM_ERRORZ_MEM_ERRORENOMEMZ_BUF_ERROR,输出缓冲区中没有足够的空间来存储结果,大致对应于 ERANGE,结果超出范围。

为了编码这种关系,我们需要在我们的类别中实现 default_error_conditionequivalent。由于我们有一个简单的一对一映射,因此前者就足够了

class zlib_category_impl: public sys::error_category
{
public:

    const char * name() const noexcept;

    std::string message( int ev ) const;
    char const * message( int ev, char * buffer, std::size_t len ) const noexcept;

    bool failed( int ev ) const noexcept;

    sys::error_condition default_error_condition( int ev ) const noexcept;
};

实现很简单

sys::error_condition zlib_category_impl::default_error_condition( int ev ) const noexcept
{
    switch( ev )
    {
    case Z_OK:            return sys::error_condition();
    case Z_STREAM_ERROR:  return sys::errc::invalid_argument;
    case Z_MEM_ERROR:     return sys::errc::not_enough_memory;
    case Z_BUF_ERROR:     return sys::errc::result_out_of_range;
    }

    return sys::error_condition( ev, *this );
}

一旦添加了此项,我们将能够将 ZLib error_code ecerrc 枚举器进行比较

if( ec == sys::errc::not_enough_memory )
{
    // Z_MEM_ERROR, or ENOMEM
}

定义库特定的错误代码

假设我们正在编写一个库 libmyimg,用于读取某种假设的图像格式,并且我们为此定义了以下 API 函数

namespace libmyimg
{

struct image;

void load_image( file& f, image& im, sys::error_code& ec );

} // namespace libmyimg

(使用我们前面示例中的可移植 file 类。)

我们假设的图像格式很简单,由固定的标头和图像数据组成,因此 load_image 的实现可能具有以下结构

namespace libmyimg
{

struct image_header
{
    uint32_t signature;
    uint32_t width;
    uint32_t height;
    uint32_t bits_per_pixel;
    uint32_t channels;
};

void load_image_header( file& f, image_header& im, sys::error_code& ec );

struct image;

void load_image( file& f, image& im, sys::error_code& ec )
{
    image_header ih = {};
    load_image_header( f, ih, ec );

    if( ec.failed() ) return;

    if( ih.signature != 0xFF0AD71A )
    {
        // return an "invalid signature" error
    }

    if( ih.width == 0 )
    {
        // return an "invalid width" error
    }

    if( ih.height == 0 )
    {
        // return an "invalid height" error
    }

    if( ih.bits_per_pixel != 8 )
    {
        // return an "unsupported bit depth" error
    }

    if( ih.channels != 1 && ih.channels != 3 && ih.channels != 4 )
    {
        // return an "unsupported channel count" error
    }

    // initialize `im` and read image data

    // ...
}

} // namespace libmyimg

我们可以看到我们需要定义五个我们自己的错误代码。(我们的函数还可以返回 ec 中的其他类型的失败 - 这些失败将来自 file::readload_image_header 将使用它来读取标头。)

为了定义这些错误,我们将使用作用域枚举类型。(此示例将利用 C++11 功能。)

namespace libmyimg
{

enum class error
{
    success = 0,

    invalid_signature,
    invalid_width,
    invalid_height,
    unsupported_bit_depth,
    unsupported_channel_count
};

} // namespace libmyimg

Boost.System 支持被告知枚举类型表示错误代码,这使得枚举类型和 error_code 之间可以进行隐式转换。这是通过专门化 is_error_code_enum 类型特征来完成的,它像库的其余部分一样位于 namespace boost::system

namespace boost
{
namespace system
{

template<> struct is_error_code_enum< ::libmyimg::error >: std::true_type
{
};

} // namespace system
} // namespace boost

一旦到位,我们现在就可以将 libmyimg::error 的值分配给 sys::error_code,这使得 load_image 的实现可以编写如下

void load_image( file& f, image& im, sys::error_code& ec )
{
    image_header ih = {};
    load_image_header( f, ih, ec );

    if( ec.failed() ) return;

    if( ih.signature != 0xFF0AD71A )
    {
        ec = error::invalid_signature;
        return;
    }

    if( ih.width == 0 )
    {
        ec = error::invalid_width;
        return;
    }

    if( ih.height == 0 )
    {
        ec = error::invalid_height;
        return;
    }

    if( ih.bits_per_pixel != 8 )
    {
        ec = error::unsupported_bit_depth;
        return;
    }

    if( ih.channels != 1 && ih.channels != 3 && ih.channels != 4 )
    {
        ec = error::unsupported_channel_count;
        return;
    }

    // initialize `image` and read image data

    // ...
}

然而,这还不够;我们仍然需要为我们的枚举器定义错误类别,并将它们与其关联。

第一步非常密切地遵循我们之前的两个示例

namespace libmyimg
{

class myimg_category_impl: public sys::error_category
{
public:

    const char * name() const noexcept;

    std::string message( int ev ) const;
    char const * message( int ev, char * buffer, std::size_t len ) const noexcept;
};

const char * myimg_category_impl::name() const noexcept
{
    return "libmyimg";
}

std::string myimg_category_impl::message( int ev ) const
{
    char buffer[ 64 ];
    return this->message( ev, buffer, sizeof( buffer ) );
}

char const * myimg_category_impl::message( int ev, char * buffer, std::size_t len ) const noexcept
{
    switch( static_cast<error>( ev ) )
    {
    case error::success:                   return "No error";
    case error::invalid_signature:         return "Invalid image signature";
    case error::invalid_width:             return "Invalid image width";
    case error::invalid_height:            return "Invalid image height";
    case error::unsupported_bit_depth:     return "Unsupported bit depth";
    case error::unsupported_channel_count: return "Unsupported number of channels";
    }

    std::snprintf( buffer, len, "Unknown libmyimg error %d", ev );
    return buffer;
}

sys::error_category const& myimg_category()
{
    static const myimg_category_impl instance;
    return instance;
}

} // namespace libmyimg

第二步涉及在我们的枚举类型 error 的命名空间中实现一个函数 make_error_code,该函数接受 error 并返回 boost::system::error_code

namespace libmyimg
{

sys::error_code make_error_code( error e )
{
    return sys::error_code( static_cast<int>( e ), myimg_category() );
}

} // namespace libmyimg

现在 load_image 将编译,我们只需要用使用 file::read 读取图像数据的代码来填充其实现的其余部分。

我们可以进行一个额外的修饰。如我们所知,Boost.System 已被提议并被接受到 C++11 标准中,现在 <system_error> 中有一个标准的实现。我们也可以通过专门化标准类型特征 std::is_error_code_enum 使我们的错误枚举类型与 std::error_code 兼容

namespace std
{

template<> struct is_error_code_enum< ::libmyimg::error >: std::true_type
{
};

} // namespace std

这使我们的枚举器可以转换为 std::error_code

(这样做有效的原因是 boost::system::error_code 可以转换为 std::error_code,因此我们的 make_error_code 重载的返回值可用于初始化 std::error_code。)

定义库特定的错误条件

到目前为止,我们所有的 libmyimg::error 错误代码都表示相同的错误条件 - 无效或不支持的图像格式。使测试此条件成为可能,而无需枚举所有五个特定代码,这可能是有意义的。为此,我们可以定义一个错误条件枚举类型

namespace libmyimg
{

enum class condition
{
    invalid_format = 1
};

} // namespace libmyimg

我们可以通过专门化 is_error_condition_enum 将其标记为表示错误条件

namespace boost
{
namespace system
{

template<> struct is_error_condition_enum< ::libmyimg::condition >: std::true_type
{
};

} // namespace system
} // namespace boost

namespace std
{

template<> struct is_error_condition_enum< ::libmyimg::condition >: std::true_type
{
};

} // namespace std

与需要 make_error_code 重载的错误代码枚举类型类似,此枚举类型将需要具有 make_error_condition 重载和一个类别。

原则上,可以通过使条件值从例如 10000 而不是 1 开始来重用我们已为错误代码定义的类别。这节省了一些输入,但更好的做法是为错误条件使用单独的类别。因此,这就是我们将要做的

namespace libmyimg
{

class myimg_condition_category_impl: public sys::error_category
{
public:

    const char * name() const noexcept;

    std::string message( int ev ) const;
    char const * message( int ev, char * buffer, std::size_t len ) const noexcept;
};

const char * myimg_condition_category_impl::name() const noexcept
{
    return "libmyimg_condition";
}

std::string myimg_condition_category_impl::message( int ev ) const
{
    char buffer[ 64 ];
    return this->message( ev, buffer, sizeof( buffer ) );
}

char const * myimg_condition_category_impl::message( int ev, char * buffer, std::size_t len ) const noexcept
{
    switch( static_cast<condition>( ev ) )
    {
    case condition::invalid_format: return "Invalid or unsupported image format";
    }

    std::snprintf( buffer, len, "Unknown libmyimg condition %d", ev );
    return buffer;
}

sys::error_category const& myimg_condition_category()
{
    static const myimg_condition_category_impl instance;
    return instance;
}

sys::error_condition make_error_condition( condition e )
{
    return sys::error_condition( static_cast<int>( e ), myimg_condition_category() );
}

} // namespace libmyimg

我们有条件了,但它还没有做任何事情。为了使 libmyimg::condition::invalid_format 与我们的错误代码进行相等比较,我们需要在错误代码类别中实现 default_error_condition

namespace libmyimg
{

class myimg_category_impl: public sys::error_category
{
public:

    const char * name() const noexcept;

    std::string message( int ev ) const;
    char const * message( int ev, char * buffer, std::size_t len ) const noexcept;

    sys::error_condition default_error_condition( int ev ) const noexcept;
};

sys::error_condition myimg_category_impl::default_error_condition( int ev ) const noexcept
{
    switch( static_cast<error>( ev ) )
    {
    case error::success:

        return {};

    case error::invalid_signature:
    case error::invalid_width:
    case error::invalid_height:
    case error::unsupported_bit_depth:
    case error::unsupported_channel_count:

        return condition::invalid_format;
    }

    return sys::error_condition( ev, *this );
}

} // namespace libmyimg

就是这样;现在可以使用 ec == libmyimg::condition::invalid_format 来测试 ec 是否包含与“无效图像格式”条件对应的我们的错误代码之一。

修订历史

Boost 1.86 中的变更

  • 支持 result<T> & fv,其中 fv 返回 void

  • 支持 result<void> &= fv;,其中 fv 返回 void

Boost 1.85 中的变更

  • 不再支持 C++03;需要 C++11 编译器。(这包括 GCC 4.8 或更高版本,以及 MSVC 14.0 (VS 2015) 或更高版本。)

  • 已删除已弃用的头文件 boost/system/cygwin_error.hpp

  • 不再支持原始且过时的(32 位)MinGW。仍然支持 MinGW-w64(64 位和 32 位)。

  • operator& 现在适用于 result<void>(通过采用无参数函数的方式)。

  • result 添加了 operator|=

Boost 1.84 中的变更

  • result<U&, E> 添加了支持。

  • result 添加了 operator|

  • result 添加了 operator&

  • result 添加了 operator&=

Boost 1.81 中的变更

  • 现在可以定义宏 BOOST_SYSTEM_DISABLE_THREADS 以禁用 <mutex> 的使用(例如,在单线程 libstdc++ 上)。

  • result<> 添加了 value_typeerror_typein_place_valuein_place_error

  • result<> 添加了 emplace

Boost 1.80 中的变更

  • error_code 转换为 std::error_code,然后再转换回 error_code 时,如果可能,现在会恢复原始值。

  • 重新设计了从 error_categorystd::error_category 的转换,以避免在泄漏检查器上显示的一次性分配。

  • 添加了一个允许替换 error_code 的源位置的构造函数,以及相应的 assign

  • result 添加了一个转换构造函数。

Boost 1.79 中的变更

  • throw_exception_from_error 添加了一个 boost::source_location 参数。

  • errc::errc_tstd::error_codestd::errcstd::exception_ptr 添加了 throw_exception_from_error 重载。

  • result<T>::value 现在通过默认参数自动向 throw_exception_from_error 提供 BOOST_CURRENT_LOCATION

  • 添加了一个接受源位置的 errc::make_error_code 重载。

Boost 1.78 中的变更

  • error_code 添加了对源位置的支持。

  • 添加了 error_code::to_stringerror_condition::to_stringerror_code::what

  • system_error::what() 现在包含源位置(如果存在)。

  • 添加了 result<T, E = error_code>,一个保存值或错误的类,在 <boost/system/result.hpp> 中定义。

Boost 1.77 中的变更

  • 改进了从 error_categorystd::error_category 的转换运算符,不再需要 <map><mutex>

  • error_category 的比较运算符现在是内联友元函数,而不是成员函数(先前更改的副作用)。

  • error_condition 现在延迟调用 generic_category(),以避免在实际需要之前实例化对象。

  • error_condition::failederror_condition::message 已取消弃用,并且 operator bool() 现在再次返回 failed()

  • 系统类别现在不调用 generic_category(),以避免实例化对象。

  • 在某些情况下,default_error_condition 的返回值会更改为来自通用类别的 error_condition,而不是来自系统类别。当输入 error_code 来自系统类别且不对应于任何 errc_t 值时,POSIX 上会发生这种情况。

  • error_codestd::error_code 的互操作性已得到显著改进。现在可以从 std::error_code 构造 boost::system::error_code,并且可以将 boost::system::error_code 传递给接受 std::error_code& 的函数。

  • 已添加 error_condition 的流插入运算符。

Boost 1.76 中的变更

  • windows_error.hpp 不再被弃用。

Boost 1.75 中的变更

  • 平台特定的头文件 windows_error.hpplinux_error.hppcygwin_error.hpp 发出弃用消息,并计划移除。

  • generic_category()system_category() 的旧名称发出弃用消息,并计划移除。

  • error_condition::failed 已被弃用,并计划移除。error_conditionoperator bool() 已恢复到其旧含义 value() != 0。这样做是为了与 std::error_condition 兼容,因为预计下一个版本将进一步改进与 <system_error> 的互操作性。请注意,这不影响 error_code::failed,它仍然有效。

  • error_condition::message 的接受缓冲区的重载已被弃用,并计划移除,原因相同。请注意,这不影响 error_code::message

Boost 1.74 中的变更

  • operator bool() 现在返回 failed() 而不是 value() != 0

Boost 1.69 中的变更

  • Boost.System 现在是仅限头文件的库。为了兼容性,仍然构建了一个存根库,但不再需要链接到它。

  • 更多函数已被标记为 constexpr

  • error_category 的析构函数现在是受保护的,不再是虚函数。这是一个潜在的破坏性更改,但预计其影响有限。

  • error_category 现在有一个接受 64 位标识符的构造函数,使不同的类别对象可以比较相等。

  • error_category 的构造函数现在是受保护的。

  • 已添加 message 的非分配、非抛出重载。

  • 已添加虚函数 failed,允许成功与 0 不同义的类别。

  • 已移除已弃用的 boost::system::throws 对象。

  • boost::throws() 现在已被弃用,不鼓励使用。

  • 接受单个 error_code 参数的 system_error 的构造函数现在是显式的。

  • system_error::code() 现在按值返回。

Boost 1.68 中的变更

在 C++14 编译器上,许多 Boost.System 函数和成员函数现在是 constexpr,并且 error_codeerror_condition 是字面值类。

除了启用在常量表达式(和 constexpr 函数)中使用外,这还显著提高了生成的代码的质量。

然而,由于此更改,现在从 C++14 或 C++17 代码中使用 Boost.System 需要库也使用 C++14 或更高版本构建。这是 GCC 6 及更高版本上的默认设置,但不是 GCC 5 或 Clang。可以通过将 cxxstd=14 选项传递给 b2 来为 C++14 构建 Boost。

(以前的版本允许针对任何 C++ 标准构建的代码与针对任何 C++ 标准构建的 Boost.System 链接。在 1.68 中,使用任何 C++ 标准的代码都可以与使用 C++14 或更高版本构建的 Boost.System 链接,但如果 Boost.System 是使用 C++11 或更低版本构建的,则只有也使用 C++11 和更低版本构建的代码才能成功链接到它。)

Boost 1.65 中的变更

在 C++11 编译器上,Boost.System 现在提供从 boost::system::error_categoryerror_codeerror_condition 到其 <system_error> 中的标准等效项的隐式转换。

这允许库公开 C++11 接口并通过 std::error_code 报告错误,即使在使用 Boost.System 时,无论是直接使用还是通过 Boost.ASIO 等依赖项。

设计原理

error_codeerror_condition 被设计为值类型,因此它们可以被复制而不会发生切片,并且不需要堆分配,但仍然具有基于错误类别的多态行为。这是通过抽象基类 error_category 提供多态行为来实现的,而 error_codeerror_condition 包含指向从 error_category 派生的类型的对象的指针。

许多详细的设计决策都是由用户能够添加其他错误类别的需求驱动的,并且编写可移植代码应该不比编写特定于系统的代码更困难。

error_codeoperator<< 重载消除了代码(如 cout << ec,其中 ec 的类型为 error_code)中误导性的转换为 bool 的情况。它本身也很有用。

参考

当定义了 BOOST_SYSTEM_ENABLE_DEPRECATED 时,库会提供已弃用的功能以实现兼容性。这些功能注定最终会消失。

当定义了 BOOST_SYSTEM_USE_UTF8 时,在 Windows 上,库会使用代码页 CP_UTF8 而不是默认的 CP_ACP 返回 UTF-8 消息。此宏在 POSIX 上无效。

当定义了 BOOST_SYSTEM_DISABLE_THREADS 时,库会假定当前平台不支持多线程,并禁用标准头文件 <mutex> 的使用,从而消除互斥锁。单线程 libstdc++ 就是这样一个平台。

已弃用的名称

在将 Boost.System 添加到 C++11 标准库的过程中,C++ 委员会更改了一些名称。为了 облегчить 过渡,Boost.System 弃用了旧名称,但当定义了宏 BOOST_SYSTEM_ENABLE_DEPRECATED 时,将提供这些旧名称。

旧用法,现在已弃用 替换

get_generic_category()

generic_category()

get_system_category()

system_category()

namespace posix

namespace errc

namespace posix_error

namespace errc

get_posix_category()

generic_category()

posix_category

generic_category()

errno_ecat

generic_category()

native_ecat

system_category()

<boost/system/​is_error_code_enum.hpp>

is_error_code_enum

namespace boost {
namespace system {

template<class T>
  struct is_error_code_enum { static const bool value = false; };

} // namespace system
} // namespace boost

用户可以为其错误枚举类型专门化 is_error_code_enum,以指示它们应该有资格自动转换为 error_code。此转换调用 make_error_code(e),应在与枚举类型相同的命名空间中提供。

<boost/system/​is_error_condition_enum.hpp>

is_error_condition_enum

namespace boost {
namespace system {

template<class T>
  struct is_error_condition_enum { static const bool value = false; };

} // namespace system
} // namespace boost

用户可以为其错误枚举类型专门化 is_error_condition_enum,以指示它们应该有资格自动转换为 error_condition。此转换调用 make_error_condition(e),应在与枚举类型相同的命名空间中提供。

<boost/system/​errc.hpp>

errc

namespace boost {
namespace system {

namespace errc {
  enum errc_t
  {
    success = 0,
    address_family_not_supported,   //EAFNOSUPPORT
    address_in_use,                 //EADDRINUSE
    address_not_available,          //EADDRNOTAVAIL
    already_connected,              //EISCONN
    argument_list_too_long,         //E2BIG
    argument_out_of_domain,         //EDOM
    bad_address,                    //EFAULT
    bad_file_descriptor,            //EBADF
    bad_message,                    //EBADMSG
    broken_pipe,                    //EPIPE
    connection_aborted,             //ECONNABORTED
    connection_already_in_progress, //EALREADY
    connection_refused,             //ECONNREFUSED
    connection_reset,               //ECONNRESET
    cross_device_link,              //EXDEV
    destination_address_required,   //EDESTADDRREQ
    device_or_resource_busy,        //EBUSY
    directory_not_empty,            //ENOTEMPTY
    executable_format_error,        //ENOEXEC
    file_exists,                    //EEXIST
    file_too_large,                 //EFBIG
    filename_too_long,              //ENAMETOOLONG
    function_not_supported,         //ENOSYS
    host_unreachable,               //EHOSTUNREACH
    identifier_removed,             //EIDRM
    illegal_byte_sequence,          //EILSEQ
    inappropriate_io_control_operation, //ENOTTY
    interrupted,                    //EINTR
    invalid_argument,               //EINVAL
    invalid_seek,                   //ESPIPE
    io_error,                       //EIO
    is_a_directory,                 //EISDIR
    message_size,                   //EMSGSIZE
    network_down,                   //ENETDOWN
    network_reset,                  //ENETRESET
    network_unreachable,            //ENETUNREACH
    no_buffer_space,                //ENOBUFS
    no_child_process,               //ECHILD
    no_link,                        //ENOLINK
    no_lock_available,              //ENOLCK
    no_message_available,           //ENODATA
    no_message,                     //ENOMSG
    no_protocol_option,             //ENOPROTOOPT
    no_space_on_device,             //ENOSPC
    no_stream_resources,            //ENOSR
    no_such_device_or_address,      //ENXIO
    no_such_device,                 //ENODEV
    no_such_file_or_directory,      //ENOENT
    no_such_process,                //ESRCH
    not_a_directory,                //ENOTDIR
    not_a_socket,                   //ENOTSOCK
    not_a_stream,                   //ENOSTR
    not_connected,                  //ENOTCONN
    not_enough_memory,              //ENOMEM
    not_supported,                  //ENOTSUP
    operation_canceled,             //ECANCELED
    operation_in_progress,          //EINPROGRESS
    operation_not_permitted,        //EPERM
    operation_not_supported,        //EOPNOTSUPP
    operation_would_block,          //EWOULDBLOCK
    owner_dead,                     //EOWNERDEAD
    permission_denied,              //EACCES
    protocol_error,                 //EPROTO
    protocol_not_supported,         //EPROTONOSUPPORT
    read_only_file_system,          //EROFS
    resource_deadlock_would_occur,  //EDEADLK
    resource_unavailable_try_again, //EAGAIN
    result_out_of_range,            //ERANGE
    state_not_recoverable,          //ENOTRECOVERABLE
    stream_timeout,                 //ETIME
    text_file_busy,                 //ETXTBSY
    timed_out,                      //ETIMEDOUT
    too_many_files_open_in_system,  //ENFILE
    too_many_files_open,            //EMFILE
    too_many_links,                 //EMLINK
    too_many_symbolic_link_levels,  //ELOOP
    value_too_large,                //EOVERFLOW
    wrong_protocol_type             //EPROTOTYPE
  };

} // namespace errc

template<> struct is_error_condition_enum<errc::errc_t>
  { static const bool value = true; };

constexpr error_condition make_error_condition( errc::errc_t e ) noexcept;

constexpr error_code make_error_code( errc::errc_t e ) noexcept;

error_code make_error_code( errc::errc_t e,
  boost::source_location const * loc ) noexcept;

} // namespace system
} // namespace boost

预定义的枚举类型 errc::errc_t 提供了与 <cerrno> 宏的值相对应的命名常量。

constexpr error_condition make_error_condition( errc::errc_t e ) noexcept;
  • 返回值

    error_condition( e, generic_category() ).

由于 errc::errc_t 提供了 is_error_condition_enum 的特化和 make_error_condition 的重载,因此它可以隐式转换为 error_condition。当将从 API 返回的 error_code 值与可移植条件进行比较时,这通常很有用,如下例所示:*

+

void api_function( boost::system::error_code& ec );

void my_function()
{
    boost::system::error_code ec;
    api_function( ec );

    if( ec == boost::system::errc::no_such_file_or_directory )
    {
        // an entity wasn't found (ENOENT)
        // handle this condition
    }
}
constexpr error_code make_error_code( errc::errc_t e ) noexcept;
  • 返回值

    error_code( e, generic_category() ).

除了 make_error_condition 之外,errc::errc_t 还提供了 make_error_code 的重载。这允许创建通用错误代码,这是一种通常在函数需要发出不来自底层 API 的通用故障信号时有用的操作,例如内存不足的情况:*

+

void my_api_function( boost::system::error_code& ec )
{
    void* p = std::malloc( 16 );

    if( p == 0 )
    {
        // return ENOMEM
        ec = make_error_code( boost::system::errc::out_of_memory );
        return;
    }

    // use p
}
constexpr error_code make_error_code( errc::errc_t e,
  boost::source_location const * loc ) noexcept;
  • 返回值

    error_code( e, generic_category(), loc ).

与上面的重载相同,但接受源位置。*

+

void my_api_function( boost::system::error_code& ec )
{
    void* p = std::malloc( 16 );

    if( p == 0 )
    {
        // return ENOMEM

        BOOST_STATIC_CONSTEXPR boost::source_location loc =
          BOOST_CURRENT_LOCATION;

        ec = make_error_code( boost::system::errc::out_of_memory, &loc );
        return;
    }

    // use p
}

<boost/system/​error_category.hpp>

error_category

error_category 定义了用于标识特定错误代码类别的来源和编码的类型的基类。

可以从 error_category 派生类,以支持 Boost.System 中定义的错误类别之外的其他类别。

namespace boost {
namespace system {

class error_category
{
public: // noncopyable

    error_category( error_category const & ) = delete;
    error_category& operator=( error_category const & ) = delete;

protected:

    ~error_category() = default;

    constexpr error_category() noexcept;
    explicit constexpr error_category( unsigned long long id ) noexcept;

public:

    virtual const char * name() const noexcept = 0;

    virtual error_condition default_error_condition( int ev ) const noexcept;

    virtual bool equivalent( int code, const error_condition & condition )
      const noexcept;
    virtual bool equivalent( const error_code & code, int condition )
      const noexcept;

    virtual std::string message( int ev ) const = 0;
    virtual char const * message( int ev, char * buffer, std::size_t len )
      const noexcept;

    virtual bool failed( int ev ) const noexcept;

    friend constexpr bool operator==( const error_category & lhs,
      const error_category & rhs ) noexcept;
    friend constexpr bool operator!=( const error_category & lhs,
      const error_category & rhs ) noexcept;

    friend constexpr bool operator< ( const error_category & lhs,
      const error_category & rhs ) noexcept;

    operator std::error_category const & () const;

private:

    unsigned long long id_; // exposition only
};

} // namespace system
} // namespace boost
构造函数
constexpr error_category() noexcept;
  • 效果

    id_ 初始化为 0。

    备注

    由于没有标识符的类别的等价性基于比较对象地址,因此使用此构造函数的类型为 C 的用户定义的派生类别应确保程序中只存在一个类型为 C 的对象。

explicit constexpr error_category( unsigned long long id ) noexcept;
  • 效果

    id_ 初始化为 id

    备注

    当标识符匹配时,使用此构造函数的用户定义的派生类别被认为是等价的。因此,这些类别可能在程序中存在多个实例,但为了最大限度地减少冲突的可能性,它们的标识符必须是随机选择的(在实现类别时,而不是在运行时)。生成 64 位随机标识符的一种方法是 https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h

虚函数
virtual const char * name() const noexcept = 0;
  • 返回值

    在派生类中,一个字符字面量,命名错误类别。

virtual error_condition default_error_condition( int ev ) const noexcept;
  • 返回值
    • 在派生类中,与 ev 相对应的错误条件。返回的错误条件通常来自通用类别。

    • 在默认实现中,error_condition( ev, *this )

virtual bool equivalent( int code, const error_condition & condition )
  const noexcept;
  • 返回值
    • 在派生类中,当 error_code( code, *this ) 等价于 condition 时为 true

    • 在默认实现中,default_error_condition( code ) == condition

virtual bool equivalent( const error_code & code, int condition )
  const noexcept;
  • 返回值
    • 在派生类中,当 code 等价于 error_condition( condition, *this ) 时为 true

    • 在默认实现中,*this == code.category() && code.value() == condition

virtual std::string message( int ev ) const = 0;
  • 返回值

    在派生类中,描述由 ev 表示的错误的字符串。

virtual char const * message( int ev, char * buffer, std::size_t len )
  const noexcept;
  • 效果
    • 派生类应执行以下操作之一

      • 返回指向描述由 ev 表示的错误的字符字面量的指针,或

      • 将描述错误的字符串复制到 buffer 中,将其截断为 len-1 个字符并存储空终止符,然后返回 buffer。如果 len 为 0,则不复制任何内容,但函数仍然返回 buffer。请注意,当 len 为 0 时,buffer 可以为 nullptr

    • 默认实现调用 message(ev) 并将结果复制到 buffer 中,将其截断为 len-1 个字符并存储空终止符。如果 len 为 0,则不复制任何内容。返回 buffer。如果 message(ev) 抛出异常,则使用字符串 "Message text unavailable"

    示例
    const char* my_category::message(int ev, char* buffer, size_t len) const noexcept
    {
        switch(ev)
        {
        case 0: return "no error";
        case 1: return "voltage out of range";
        case 2: return "impedance mismatch";
    
        case 31:
        case 32:
        case 33:
    
            std::snprintf(buffer, len, "component %d failure", ev-30);
            return buffer;
    
        default:
    
            std::snprintf(buffer, len, "unknown error %d", ev);
            return buffer;
        }
    }
virtual bool failed( int ev ) const noexcept;
  • 返回值
    • 在派生类中,当 ev 表示失败时为 true

    • 在默认实现中,ev != 0

    备注

    对于相同的 ev 的所有调用,此函数必须返回相同的值。

比较
friend constexpr bool operator==( const error_category & lhs,
  const error_category & rhs ) noexcept;
  • 返回值

    rhs.id_ == 0? &lhs == &rhs: lhs.id_ == rhs.id_.

    备注

    当两个类别对象具有匹配的非零标识符,或为同一对象时,它们被认为是等价的。

friend constexpr bool operator!=( const error_category & lhs,
  const error_category & rhs ) noexcept;
  • 返回值

    !( lhs == rhs ).

constexpr bool operator< ( const error_category & lhs,
  const error_category & rhs ) const noexcept;
  • 返回值
    • 如果 lhs.id_ < rhs.id_,则为 true

    • 否则,如果 lhs.id_ > rhs.id_,则为 false

    • 否则,如果 rhs.id_ != 0,则为 false

    • 否则,std::less<error_category const *>()( &lhs, &rhs )

转换
operator std::error_category const & () const;
  • 返回值

    对与 *this 相对应的 std::error_category 对象的引用。

<boost/system/​system_category.hpp>

system_category

namespace boost {
namespace system {

constexpr const error_category & system_category() noexcept;

} // namespace system
} // namespace boost
constexpr const error_category & system_category() noexcept;
  • 返回值

    对预定义的 error_category 对象的引用,该对象标识源自操作系统的错误。

<boost/system/​generic_category.hpp>

generic_category

namespace boost {
namespace system {

constexpr const error_category & generic_category() noexcept;

} // namespace system
} // namespace boost
constexpr const error_category & generic_category() noexcept;
  • 返回值

    对预定义的 error_category 对象的引用,该对象标识可移植的错误代码和条件。

<boost/system/​error_code.hpp>

error_code

error_code 描述了一个用于保存错误代码值的对象,例如那些源自操作系统或其他底层应用程序程序接口的值。它是通过异常进行错误报告的辅助手段。

namespace boost {
namespace system {

class error_code {
public:

    // constructors

    constexpr error_code() noexcept;
    constexpr error_code( int val, const error_category & cat ) noexcept;

    error_code( int val, const error_category & cat,
      boost::source_location const * loc ) noexcept;

    template<class ErrorCodeEnum>
      constexpr error_code( ErrorCodeEnum e ) noexcept;

    error_code( error_code const& ec,
      boost::source_location const * loc ) noexcept;

    error_code( std::error_code const& ec ) noexcept;

    // modifiers

    constexpr void assign( int val, const error_category & cat ) noexcept;

    void assign( int val, const error_category & cat,
      boost::source_location const * loc ) noexcept;

    template<class ErrorCodeEnum>
      constexpr error_code & operator=( ErrorCodeEnum e ) noexcept;

    void assign( error_code const& ec,
      boost::source_location const * loc ) noexcept;

    constexpr void clear() noexcept;

    // observers

    constexpr int value() const noexcept;
    constexpr const error_category & category() const noexcept;

    error_condition default_error_condition() const noexcept;

    std::string message() const;
    char const * message( char * buffer, std::size_t len ) const noexcept;

    constexpr bool failed() const noexcept;
    constexpr explicit operator bool() const noexcept;

    bool has_location() const noexcept;
    boost::source_location const & location() const noexcept;

    // comparisons

    friend constexpr bool operator==( const error_code & lhs,
      const error_code & rhs ) noexcept;

    friend constexpr bool operator!=( const error_code & lhs,
      const error_code & rhs ) noexcept;

    friend constexpr bool operator<( const error_code & lhs,
      const error_code & rhs ) noexcept;

    friend bool operator==( const error_code & code,
      const error_condition & condition ) noexcept;
    friend bool operator==( const error_condition & condition,
      const error_code & code ) noexcept;

    friend bool operator!=( const error_code & code,
      const error_condition & condition ) noexcept;
    friend bool operator!=( const error_condition & condition,
      const error_code & code ) noexcept;

    friend bool operator==( const error_code & lhs,
      const std::error_code & rhs ) noexcept;
    friend bool operator==( const std::error_code & lhs,
      const error_code & rhs ) noexcept;

    friend bool operator!=( const error_code & lhs,
      const std::error_code & rhs ) noexcept;
    friend bool operator!=( const std::error_code & lhs,
      const error_code & rhs ) noexcept;

    template<class E>
      friend constexpr bool operator==( const error_code & lhs, E rhs ) noexcept;
    template<class E>
      friend constexpr bool operator==( E lhs, const error_code & rhs ) noexcept;

    template<class E>
      friend constexpr bool operator!=( const error_code & lhs, E rhs ) noexcept;
    template<class E>
      friend constexpr bool operator!=( E lhs, const error_code & rhs ) noexcept;

    // conversions

    operator std::error_code() const;
    operator std::error_code();
    template<class T> operator T& (); // only when T=std::error_code

    // to_string

    std::string to_string() const;

    // stream insertion

    template<class charT, class traits>
      friend std::basic_ostream<charT, traits>&
        operator<<( basic_ostream<charT, traits>& os, const error_code & ec );

    // what

    std::string what() const;
};

// non-member functions

std::size_t hash_value( const error_code & ec );

} // namespace system
} // namespace boost
构造函数
constexpr error_code() noexcept;
  • 确保

    value() == 0; category() == system_category(); !has_location()

constexpr error_code( int val, const error_category & cat ) noexcept;
  • 确保

    value() == val; category() == cat; !has_location()

error_code( int val, const error_category & cat,
  boost::source_location const * loc ) noexcept;
  • 要求

    loc 指向具有静态存储持续时间的有效 boost::source_location 对象。

    确保

    value() == val; category() == cat; has_location(); &location() == loc

template<class ErrorCodeEnum>
  constexpr error_code( ErrorCodeEnum e ) noexcept;
  • 确保

    *this == make_error_code( e ).

    备注

    仅当 is_error_code_enum<ErrorCodeEnum>::valuetrue 时,此构造函数才启用。

error_code( error_code const& ec,
  boost::source_location const * loc ) noexcept;
  • 要求

    loc 指向具有静态存储持续时间的有效 boost::source_location 对象,或为 nullptr

    确保

    *this == ec.

    备注

    ec 是默认构造的 error_code 或包装了 std::error_code,或者当 locnullptr 时,*this 不存储位置 (has_location()false)。否则,*this 存储 loc (has_location()true&location()loc)。

error_code( std::error_code const & ec ) noexcept;
  • 效果

    构造一个包装 ecerror_code

    备注

    value()category() 未指定。has_location()false

修改器
constexpr void assign( int val, const error_category & cat ) noexcept;
  • 效果

    *this = error_code( val, cat ).

void assign( int val, const error_category & cat,
  boost::source_location const * loc ) noexcept;
  • 效果

    *this = error_code( val, cat, loc ).

template<class ErrorCodeEnum>
  constexpr error_code & operator=( ErrorCodeEnum e ) noexcept;
  • 确保

    *this == make_error_code( e ).

    备注

    仅当 is_error_code_enum<ErrorCodeEnum>::valuetrue 时,此运算符才启用。

void assign( error_code const& ec,
  boost::source_location const * loc ) noexcept;
  • 效果

    *this = error_code( ec, loc ).

constexpr void clear() noexcept;
  • 效果

    *this = error_code().

观察器
constexpr int value() const noexcept;
  • 返回值

    错误值。

constexpr const error_category & category() const noexcept;
  • 返回值

    错误类别。

error_condition default_error_condition() const noexcept;
  • 返回值

    category().default_error_condition( value() ).

std::string message() const;
  • 返回值

    如果 *this 包装了 std::error_code 对象 ec,则为 ec.message()。否则,为 category().message( value() )

char const * message( char * buffer, std::size_t len ) const noexcept;
  • 效果

    如果 *this 包装了 std::error_code 对象 ec,则将从 ec.message() 返回的字符串复制到 buffer 中并返回 buffer。否则,返回 category().message( value(), buffer, len )

constexpr bool failed() const noexcept;
  • 返回值

    如果 *this 包装了 std::error_code 对象 ec,则为 ec.value() != 0。否则,为 category().failed( value() )

constexpr explicit operator bool() const noexcept;
  • 返回值

    failed().

bool has_location() const noexcept;
  • 返回值

    如果 *this 已使用指向源位置的指针构造,则为 true,否则为 false

boost::source_location const & location() const noexcept;
  • 返回值

    如果 *this 已使用指向源位置 loc 的指针构造,则为 *loc,否则为对默认构造的 boost::source_location 的引用。

比较
friend constexpr bool operator==( const error_code & lhs,
  const error_code & rhs ) noexcept;
  • 返回值

    如果 lhsrhs 都包装了 std::error_code 对象 e1e2,则为 e1 == e2。否则,为 lhs.value() == rhs.value() && lhs.category() == rhs.category()

friend constexpr bool operator!=( const error_code & lhs,
  const error_code & rhs ) noexcept;
  • 返回值

    !( lhs == rhs ).

friend constexpr bool operator<( const error_code & lhs,
  const error_code & rhs ) noexcept;
  • 返回值

    如果 lhsrhs 都包装了 std::error_code 对象 e1e2,则为 e1 < e2。否则,为 lhs.category() < rhs.category() || (lhs.category() == rhs.category() && lhs.value() < rhs.value())

friend bool operator==( const error_code & code,
  const error_condition & condition ) noexcept;
friend bool operator==( const error_condition & condition,
  const error_code & code ) noexcept;
  • 返回值

    如果 code 包装了 std::error_code 对象 ec,则为 ec == static_cast<std::error_condition>( condition )。否则,为 code.category().equivalent( code.value(), condition ) || condition.category().equivalent( code, condition.value() )

friend bool operator!=( const error_code & lhs,
  const error_condition & rhs ) noexcept;
friend bool operator!=( const error_condition & lhs,
  const error_code & rhs ) noexcept;
  • 返回值

    !( lhs == rhs ).

friend bool operator==( const error_code & lhs,
  const std::error_code & rhs ) noexcept;
  • 返回值

    static_cast<std::error_code>(lhs) == rhs.

friend bool operator==( const std::error_code & lhs,
  const error_code & rhs ) noexcept;
  • 返回值

    lhs == static_cast<std::error_code>(rhs).

friend bool operator!=( const error_code & lhs,
  const std::error_code & rhs ) noexcept;
friend bool operator!=( const std::error_code & lhs,
  const error_code & rhs ) noexcept;
  • 返回值

    !( lhs == rhs ).

template<class E>
  friend constexpr bool operator==( const error_code & lhs, E rhs ) noexcept;
  • 效果
    • is_error_code_enum<E>::valuetrue 时,返回 lhs == make_error_code(rhs)

    • is_error_condition_enum<E>::valuetrue 时,返回 lhs == make_error_condition(rhs)

    • 否则,此重载将被禁用。

template<class E>
  friend constexpr bool operator==( E lhs, const error_code & rhs ) noexcept;
  • 效果
    • is_error_code_enum<E>::valuetrue 时,返回 make_error_code(lhs) == rhs

    • is_error_condition_enum<E>::valuetrue 时,返回 make_error_condition(lhs) == rhs

    • 否则,此重载将被禁用。

template<class E>
  friend constexpr bool operator!=( const error_code & lhs, E rhs ) noexcept;
template<class E>
  friend constexpr bool operator!=( E lhs, const error_code & rhs ) noexcept;
  • 返回值

    !( lhs == rhs ).

    备注

    仅当 is_error_code_enum<E>::valuetrueis_error_condition_enum<E>::valuetrue 时,这些重载才启用。

转换
operator std::error_code() const;
operator std::error_code();
  • 返回值

    如果 *this 包装了 std::error_code 对象 ec,则为 ec。否则,为 std::error_code( value(), category() )

template<class T> operator T&();
  • 效果

    如果 *this 包装了 std::error_code 对象 ec,则返回对 ec 的引用。否则,使 *this 包装 std::error_code( *this ),然后返回对它的引用。

    备注

    仅当 Tstd::error_code 时,此运算符才启用。

to_string
std::string to_string() const;
  • 返回值

    如果 *this 包装了 std::error_code 对象 e2,则为由 "std:"e2.category().name()':'e2.value() 的字符串表示形式连接而成的字符串。否则,为由 category().name()':'value() 的字符串表示形式连接而成的字符串。

流插入
template <class charT, class traits>
  std::basic_ostream<charT, traits>&
    operator<<( basic_ostream<charT, traits>& os, const error_code & ec );
  • 效果

    os << to_string().

    返回值

    os.

what
std::string what() const;
  • 返回值

    *this 的字符串表示形式,适用于日志记录和诊断输出。通常包含 message()to_string()location().to_string()(如果可用)。

非成员
std::size_t hash_value( const error_code & ec );
  • 返回值

    如果 ec 包装了 std::error_code 对象 e2,则为 std::hash<std::error_code>()(e2)。否则,为表示 ec 的哈希值。

<boost/system/​error_condition.hpp>

error_condition

namespace boost {
namespace system {

class error_condition {
public:

    // constructors

    constexpr error_condition() noexcept;
    constexpr error_condition( int val, const error_category & cat ) noexcept;

    template <class ErrorConditionEnum>
      constexpr error_condition( ErrorConditionEnum e ) noexcept;

    // modifiers

    constexpr void assign( int val, const error_category & cat ) noexcept;

    template<typename ErrorConditionEnum>
      constexpr error_condition & operator=( ErrorConditionEnum e ) noexcept;

    constexpr void clear() noexcept;

    // observers

    constexpr int value() const noexcept;
    constexpr const error_category & category() const noexcept;

    std::string message() const;
    char const * message( char * buffer, std::size_t len ) const noexcept;

    constexpr bool failed() const noexcept;
    constexpr explicit operator bool() const noexcept;

    // comparisons

    friend constexpr bool operator==( const error_condition & lhs,
      const error_condition & rhs ) noexcept;

    friend constexpr bool operator!=( const error_condition & lhs,
      const error_condition & rhs ) noexcept;

    friend constexpr bool operator<( const error_condition & lhs,
      const error_condition & rhs ) noexcept;

    friend bool operator==( const std::error_code & code,
      const error_condition & condition ) noexcept;
    friend bool operator==( const error_condition & condition,
      const std::error_code & code ) noexcept;

    friend bool operator!=( const std::error_code & code,
      const error_condition & condition ) noexcept;
    friend bool operator!=( const error_condition & condition,
      const std::error_code & code ) noexcept;

    // conversions

    operator std::error_condition() const;

    // to_string

    std::string to_string() const;

    // stream insertion

    template <class charT, class traits>
      friend std::basic_ostream<charT, traits>&
        operator<<( basic_ostream<charT, traits>& os, const error_condition & en );
};

} // namespace system
} // namespace boost
构造函数
constexpr error_condition() noexcept;
  • 确保

    value() == 0; category() == generic_category()

constexpr error_condition( int val, const error_category & cat ) noexcept;
  • 确保

    value() == val; category() == cat

template <class ErrorConditionEnum>
  constexpr error_condition( ErrorConditionEnum e ) noexcept;
  • 确保

    *this == make_error_condition( e ).

    备注

    仅当 is_error_condition_enum<ErrorConditionEnum>::valuetrue 时,此构造函数才启用。

修改器
constexpr void assign( int val, const error_category & cat ) noexcept;
  • 确保

    value() == val; category() == cat

template <class ErrorConditionEnum>
  constexpr error_condition & operator=( ErrorConditionEnum e ) noexcept;
  • 确保

    *this == make_error_condition( e ).

    备注

    仅当 is_error_condition_enum<ErrorConditionEnum>::valuetrue 时,此运算符才启用。

constexpr void clear() noexcept;
  • 确保

    value() == 0; category() == generic_category()

观察器
constexpr int value() const noexcept;
  • 返回值

    错误值。

constexpr const error_category & category() const noexcept;
  • 返回值

    错误类别。

std::string message() const;
  • 返回值

    category().message( value() ).

char const * message( char * buffer, std::size_t len ) const noexcept;
  • 返回值

    category().message( value(), buffer, len ).

constexpr bool failed() const noexcept;
  • 返回值

    category().failed( value() ).

constexpr explicit operator bool() const noexcept;
  • 返回值

    failed().

比较
friend constexpr bool operator==( const error_condition & lhs,
  const error_condition & rhs ) noexcept;
  • 返回值

    lhs.value() == rhs.value() && lhs.category() == rhs.category().

friend constexpr bool operator!=( const error_condition & lhs,
  const error_condition & rhs ) noexcept;
  • 返回值

    !( lhs == rhs ).

friend constexpr bool operator<( const error_condition & lhs,
  const error_condition & rhs ) noexcept;
  • 返回值

    lhs.category() < rhs.category() || (lhs.category() == rhs.category() && lhs.value() < rhs.value()).

friend bool operator==( const std::error_code & code,
  const error_condition & condition ) noexcept;
friend bool operator==( const error_condition & condition,
  const std::error_code & code ) noexcept;
  • 返回值

    code == static_cast<std::error_condition>( rhs ).

friend constexpr bool operator!=( const std::error_code & lhs,
  const error_condition & rhs ) noexcept;
friend constexpr bool operator!=( const error_condition & lhs,
  const std::error_code & rhs ) noexcept;
  • 返回值

    !( lhs == rhs ).

转换
operator std::error_condition() const;
  • 返回值

    std::error_condition( value(), category() ).

to_string
std::string to_string() const;
  • 返回值

    "cond:"category().name()':'value() 的字符串表示形式连接而成的字符串。

流插入
template <class charT, class traits>
  std::basic_ostream<charT, traits>&
    operator<<( basic_ostream<charT, traits>& os, const error_condition & en );
  • 效果

    os << en.to_string().

    返回值

    os.

<boost/system/​system_error.hpp>

system_error

system_error 描述了一个异常对象,用于报告具有关联 error_code 的错误。此类错误通常源自操作系统或其他底层应用程序程序接口。

namespace boost {
namespace system {

class system_error: public std::runtime_error
{
public:

    explicit system_error( error_code ec );
    system_error( error_code ec, const char * what_arg );
    system_error( error_code ec, const std::string & what_arg );

    system_error( int ev, const error_category & ecat );
    system_error( int ev, const error_category & ecat,
      const char * what_arg );
    system_error( int ev, const error_category & ecat,
      const std::string & what_arg );

    error_code code() const noexcept;
    const char * what() const noexcept;
};

} // namespace system
} // namespace boost
构造函数
explicit system_error( error_code ec );
system_error( error_code ec, const char * what_arg );
system_error( error_code ec, const std::string & what_arg );
  • 确保

    code() == ec.

system_error( int ev, const error_category & ecat,
  const char * what_arg );
system_error( int ev, const error_category & ecat,
  const std::string & what_arg );
system_error( int ev, const error_category & ecat );
  • 确保

    code() == error_code( ev, ecat ).

观察器
error_code code() const noexcept;
  • 返回值

    ecerror_code( ev, ecat ),来自构造函数,视情况而定。

const char * what() const noexcept;
  • 返回值

    一个以 null 结尾的字符串,其中包含构造函数中提供的参数,通常形式为 what_arg + ": " + code().message()

<boost/system/result.hpp>

此头文件定义了类模板 result<T, E>。与库的其余部分不同,它需要 C++11。

概要

namespace boost {
namespace system {

// throw_exception_from_error

BOOST_NORETURN inline void throw_exception_from_error( error_code const & e,
  boost::source_location const & loc );

BOOST_NORETURN inline void throw_exception_from_error( std::error_code const & e,
  boost::source_location const & loc );

BOOST_NORETURN inline void throw_exception_from_error( errc::errc_t const & e,
  boost::source_location const & loc );

BOOST_NORETURN inline void throw_exception_from_error( std::errc const & e,
  boost::source_location const & loc );

BOOST_NORETURN inline void throw_exception_from_error( std::exception_ptr & e,
  boost::source_location const & loc );

// in_place_*

using in_place_value_t = /*unspecified*/;
constexpr in_place_value_t in_place_value{};

using in_place_error_t = /*unspecified*/;
constexpr in_place_error_t in_place_error{};

// result

template<class T, class E = error_code> class result;
template<class E> class result<void, E>;
template<class U, class E> class result<U&, E>;

// operator|

template<class T, class E, class U> T operator|( result<T, E> const& r, U&& u );
template<class T, class E, class U> T operator|( result<T, E>&& r, U&& u );

template<class T, class E, class F> T operator|( result<T, E> const& r, F&& f );
template<class T, class E, class F> T operator|( result<T, E>&& r, F&& f );

template<class T, class E, class F, class R = ...> R operator|( result<T, E> const& r, F&& f );
template<class T, class E, class F, class R = ...> R operator|( result<T, E>&& r, F&& f );
template<class E, class F, class R = ...> R operator|( result<void, E> const& r, F&& f );
template<class E, class F, class R = ...> R operator|( result<void, E>&& r, F&& f );

// operator&

template<class T, class E, class F, class U = ...>
  result<U, E> operator&( result<T, E> const& r, F&& f );
template<class T, class E, class F, class U = ...>
  result<U, E> operator&( result<T, E>&& r, F&& f );

template<class T, class E, class F, class R = ...> R operator&( result<T, E> const& r, F&& f );
template<class T, class E, class F, class R = ...> R operator&( result<T, E>&& r, F&& f );

// operator&=

template<class T, class E, class F, class U = ...>
  result<T, E>& operator&=( result<T, E>& r, F&& f );

template<class T, class E, class F, class R = ...>
  result<T, E>& operator&=( result<T, E>& r, F&& f );

} // namespace system
} // namespace boost

throw_exception_from_error

当结果持有错误时,result<T, E>::value() 会调用函数 throw_exception_from_error。其目的是抛出一个表示结果中持有的错误的异常。

已经为 Eerror_code 的常见和默认情况提供了实现。它抛出 system_error(e)

如果 result<T, E> 与其他错误类型一起使用,则用户应在 E 的命名空间中提供 throw_exception_from_error 的适当重载。

BOOST_NORETURN inline void throw_exception_from_error( error_code const & e,
  boost::source_location const & loc );
  • 效果

    boost::throw_with_location( system_error( e ), loc ).

BOOST_NORETURN inline void throw_exception_from_error( std::error_code const & e,
  boost::source_location const & loc );
  • 效果

    boost::throw_with_location( std::system_error( e ), loc ).

BOOST_NORETURN inline void throw_exception_from_error( errc::errc_t const & e,
  boost::source_location const & loc );
  • 效果

    boost::throw_with_location( system_error( make_error_code( e ) ), loc ).

BOOST_NORETURN inline void throw_exception_from_error( std::errc const & e,
  boost::source_location const & loc );
  • 效果

    boost::throw_with_location( std::system_error( make_error_code( e ) ), loc ).

BOOST_NORETURN inline void throw_exception_from_error( std::exception_ptr & e,
  boost::source_location const & loc );
  • 效果
    • 如果 e 不为 null,则 std::rethrow_exception( e )

    • 否则,boost::throw_with_location( std::bad_exception(), loc )

result<T, E>

result<T, E> 存储类型为 T 的值或类型为 E 的错误。E 默认为 error_code。在典型用法中,可能失败的函数返回 result<T>

namespace boost {
namespace system {

template<class T, class E = error_code> class result
{
public:

    using value_type = T;
    using error_type = E;

    static constexpr in_place_value_t in_place_value{};
    static constexpr in_place_error_t in_place_error{};

    // constructors

    constexpr result();

    template<class... A>
      constexpr result( A&&... a );

    template<class... A>
      constexpr result( in_place_value_t, A&&... a );

    template<class... A>
      constexpr result( in_place_error_t, A&&... a );

    template<class T2, class E2>
      constexpr result( result<T2, E2> const& r2 );

    template<class T2, class E2>
      constexpr result( result<T2, E2>&& r2 );

    // queries

    constexpr bool has_value() const noexcept;
    constexpr bool has_error() const noexcept;
    constexpr explicit operator bool() const noexcept;

    // checked value access

    constexpr T& value( boost::source_location const & loc =
      BOOST_CURRENT_LOCATION ) & ;

    constexpr T const& value( boost::source_location const & loc =
      BOOST_CURRENT_LOCATION ) const& ;

    constexpr T&& value( boost::source_location const & loc =
      BOOST_CURRENT_LOCATION ) && ;

    constexpr T const&& value( boost::source_location const & loc =
      BOOST_CURRENT_LOCATION ) const&& ;

    // unchecked value access

    constexpr T* operator->() noexcept;
    constexpr T const* operator->() const noexcept;

    constexpr T& operator*() & noexcept;
    constexpr T const& operator*() const & noexcept;
    constexpr T&& operator*() && noexcept;
    constexpr T const&& operator*() const && noexcept;

    // error access

    constexpr E error() const &;
    constexpr E error() &&;

    // emplace

    template<class... A>
      constexpr T& emplace( A&&... a );

    // swap

    constexpr void swap( result& r );
    friend constexpr void swap( result & r1, result & r2 );

    // equality

    friend constexpr bool operator==( result const & r1, result const & r2 );
    friend constexpr bool operator!=( result const & r1, result const & r2 );
};

// stream insertion

template<class Ch, class Tr, class T, class E>
  std::basic_ostream<Ch, Tr>&
    operator<<( std::basic_ostream<Ch, Tr>& os, result<T, E> const & r );

} // namespace system
} // namespace boost
构造函数
constexpr result();
  • 确保

    *this 持有值 T()

    备注

    仅当 std::is_default_constructible<T>::valuetrue 时,此构造函数才启用。

template<class... A>
  constexpr result( A&&... a );
  • 效果
    • 如果 std::is_constructible<T, A…​>::value && !std::is_constructible<E, A…​>::value,则确保 *this 持有值 T( std::forward<A>(a)…​ )

    • 如果 std::is_constructible<E, A…​>::value && !std::is_constructible<T, A…​>::value,则确保 *this 持有错误 E( std::forward<A>(a)…​ )

    • 否则,此构造函数不参与重载解析。

    备注

    仅当 sizeof…​(A) > 0 时,此构造函数才启用。

template<class... A>
  constexpr result( in_place_value_t, A&&... a );
  • 确保

    *this 持有值 T( std::forward<A>(a)…​ )

    备注

    仅当 std::is_constructible<T, A…​>::valuetrue 时,此构造函数才启用。

template<class... A>
  constexpr result( in_place_error_t, A&&... a );
  • 确保

    *this 持有错误 E( std::forward<A>(a)…​ )

    备注

    仅当 std::is_constructible<E, A…​>::valuetrue 时,此构造函数才启用。

template<class T2, class E2>
  constexpr result( result<T2, E2> const& r2 );
  • 确保

    如果 r2.has_value()true,则 *this 持有值 T( *r2 ),否则 *this 持有错误 E( r2.error() )

    备注

    仅当 std::is_convertible<T2, T>::value && std::is_convertible<E2, E>::valuetrue 时,此构造函数才启用。

template<class T2, class E2>
  constexpr result( result<T2, E2>&& r2 );
  • 确保

    如果 r2.has_value()true,则 *this 持有值 T( std::move( *r2 ) ),否则 *this 持有错误 E( r2.error() )

    备注

    仅当 std::is_convertible<T2, T>::value && std::is_convertible<E2, E>::valuetrue 时,此构造函数才启用。

查询
constexpr bool has_value() const noexcept;
  • 返回值

    *this 持有值时为 true,否则为 false

constexpr bool has_error() const noexcept;
  • 返回值

    !has_value().

constexpr explicit operator bool() const noexcept;
  • 返回值

    has_value().

检查的值访问
constexpr T& value(
  boost::source_location const & loc = BOOST_CURRENT_LOCATION ) & ;

constexpr T const& value(
  boost::source_location const & loc = BOOST_CURRENT_LOCATION ) const& ;

constexpr T&& value(
  boost::source_location const & loc = BOOST_CURRENT_LOCATION ) && ;

constexpr T const&& value(
  boost::source_location const & loc = BOOST_CURRENT_LOCATION ) const&& ;
  • 效果

    如果 *this 持有值,则返回对它的引用。否则,调用 throw_exception_from_error,将对持有的错误和 loc 的引用传递给它。

未检查的值访问
constexpr T* operator->() noexcept;
constexpr T const* operator->() const noexcept;
  • 返回值

    如果 *this 持有值,则返回指向它的指针。否则,返回 nullptr

constexpr T& operator*() & noexcept;
constexpr T const& operator*() const & noexcept;
  • 要求

    *this 持有值。

    返回值

    *operator->().

constexpr T&& operator*() && noexcept;
constexpr T const&& operator*() const && noexcept;
  • 要求

    *this 持有值。

    返回值

    std::move( *operator->() ).

错误访问
constexpr E error() const &;
constexpr E error() &&;
  • 效果

    如果 *this 持有错误,则返回它。否则,返回 E()

emplace
template<class... A>
  constexpr T& emplace( A&&... a );
  • 确保

    *this 持有值 T( std::forward<A>(a)…​ )

    返回值

    对包含值的引用。

swap
constexpr void swap( result& r );
  • 效果

    交换 *thisr 的内容。

friend constexpr void swap( result & r1, result & r2 );
  • 效果

    交换 r1r2 的内容。

相等性
friend constexpr bool operator==( result const & r1, result const & r2 );
  • 效果
    • 如果 r1 持有值 t1r2 持有值 t2,则返回 t1 == t2

    • 如果 r1 持有错误 e1r2 持有错误 e2,则返回 e1 == e2

    • 否则,返回 false

friend constexpr bool operator!=( result const & r1, result const & r2 );
  • 返回值

    !( r1 == r2 ).

流插入
template<class Ch, class Tr, class T, class E>
  std::basic_ostream<Ch, Tr>&
    operator<<( std::basic_ostream<Ch, Tr>& os, result<T, E> const & r );
  • 效果
    • 如果 *this 持有值 t,则 os << "value:" << t

    • 如果 *this 持有错误 e,则 os << "error:" << e

    返回值

    os.

result<void, E>

namespace boost {
namespace system {

template<class E> class result<void, E>
{
public:

    using value_type = void;
    using error_type = E;

    static constexpr in_place_value_t in_place_value{};
    static constexpr in_place_error_t in_place_error{};

    // constructors

    constexpr result() noexcept;

    template<class... A>
      constexpr result( A&&... a );

    constexpr result( in_place_value_t ) noexcept;

    template<class... A>
      constexpr result( in_place_error_t, A&&... a );

    template<class E2>
      constexpr result( result<void, E2> const& r2 );

    // queries

    constexpr bool has_value() const noexcept;
    constexpr bool has_error() const noexcept;
    constexpr explicit operator bool() const noexcept;

    // checked value access

    constexpr void value( boost::source_location const & loc =
      BOOST_CURRENT_LOCATION ) const;

    // unchecked value access

    constexpr void* operator->() noexcept;
    constexpr void const* operator->() const noexcept;

    constexpr void operator*() const noexcept;

    // error access

    constexpr E error() const &;
    constexpr E error() &&;

    // emplace

    constexpr void emplace();

    // swap

    constexpr void swap( result& r );
    friend constexpr void swap( result & r1, result & r2 );

    // equality

    friend constexpr bool operator==( result const & r1, result const & r2 );
    friend constexpr bool operator!=( result const & r1, result const & r2 );
};

// stream insertion

template<class Ch, class Tr, class E>
  std::basic_ostream<Ch, Tr>&
    operator<<( std::basic_ostream<Ch, Tr>& os, result<void, E> const & r );

} // namespace system
} // namespace boost
构造函数
constexpr result() noexcept;
  • 确保

    *this 持有未指定的值。

template<class... A>
  constexpr result( A&&... a );
  • 效果
    • 如果 std::is_constructible<E, A…​>::value,则确保 *this 持有错误 E( std::forward<A>(a)…​ )

    • 否则,此构造函数不参与重载解析。

    备注

    仅当 sizeof…​(A) > 0 时,此构造函数才启用。

template<class... A>
  constexpr result( in_place_value_t ) noexcept;
  • 确保

    *this 持有未指定的值。

template<class... A>
  constexpr result( in_place_error_t, A&&... a );
  • 确保

    *this 持有错误 E( std::forward<A>(a)…​ )

    备注

    仅当 std::is_constructible<E, A…​>::valuetrue 时,此构造函数才启用。

template<class E2>
  constexpr result( result<void, E2> const& r2 );
  • 确保

    如果 r2.has_value()true,则 *this 持有未指定的值,否则 *this 持有错误 E( r2.error() )

    备注

    仅当 std::is_convertible<E2, E>::valuetrue 时,此构造函数才启用。

查询
constexpr bool has_value() const noexcept;
  • 返回值

    *this 持有值时为 true,否则为 false

constexpr bool has_error() const noexcept;
  • 返回值

    !has_value().

constexpr explicit operator bool() const noexcept;
  • 返回值

    has_value().

检查的值访问
constexpr void value(
  boost::source_location const & loc = BOOST_CURRENT_LOCATION ) const;
  • 效果

    如果 *this 不持有值,则调用 throw_exception_from_error,将对持有的错误和 loc 的引用传递给它。

未检查的值访问
constexpr void* operator->() noexcept;
constexpr void const* operator->() const noexcept;
  • 返回值

    如果 *this 持有值,则返回指向它的指针。否则,返回 nullptr

constexpr void operator*() const noexcept;
  • 要求

    *this 持有值。

    效果

    无。

错误访问
constexpr E error() const &;
constexpr E error() &&;
  • 效果

    如果 *this 持有错误,则返回它。否则,返回 E()

emplace
constexpr void emplace();
  • 确保

    *this 持有未指定的值。

swap
constexpr void swap( result& r );
  • 效果

    交换 *thisr 的内容。

friend constexpr void swap( result & r1, result & r2 );
  • 效果

    交换 r1r2 的内容。

相等性
friend constexpr bool operator==( result const & r1, result const & r2 );
  • 效果
    • 如果 r1r2 都持有值,则返回 true

    • 如果 r1 持有错误 e1r2 持有错误 e2,则返回 e1 == e2

    • 否则,返回 false

friend constexpr bool operator!=( result const & r1, result const & r2 );
  • 返回值

    !( r1 == r2 ).

流插入
template<class Ch, class Tr, class E>
  std::basic_ostream<Ch, Tr>&
    operator<<( std::basic_ostream<Ch, Tr>& os, result<void, E> const & r );
  • 效果
    • 如果 *this 持有值,则 os << "value:void"

    • 如果 *this 持有错误 e,则 os << "error:" << e

    返回值

    os.

result<U&, E>

namespace boost {
namespace system {

template<class U, class E> class result<U&, E>
{
public:

    using value_type = U&;
    using error_type = E;

    static constexpr in_place_value_t in_place_value{};
    static constexpr in_place_error_t in_place_error{};

    // constructors

    template<class A>
      constexpr result( A&& a ) noexcept;

    template<class... A>
      constexpr result( A&&... a );

    template<class A>
      constexpr result( in_place_value_t, A&& a ) noexcept;

    template<class... A>
      constexpr result( in_place_error_t, A&&... a );

    template<class U2, class E2>
      constexpr result( result<U2&, E2> const& r2 );

    // queries

    constexpr bool has_value() const noexcept;
    constexpr bool has_error() const noexcept;
    constexpr explicit operator bool() const noexcept;

    // checked value access

    constexpr U& value( boost::source_location const & loc =
      BOOST_CURRENT_LOCATION ) const;

    // unchecked value access

    constexpr U* operator->() const noexcept;
    constexpr U& operator*() const noexcept;

    // error access

    constexpr E error() const &;
    constexpr E error() &&;

    // emplace

    template<class A>
      constexpr U& emplace( A&& a ) noexcept;

    // swap

    constexpr void swap( result& r );
    friend constexpr void swap( result & r1, result & r2 );

    // equality

    friend constexpr bool operator==( result const & r1, result const & r2 );
    friend constexpr bool operator!=( result const & r1, result const & r2 );
};

} // namespace system
} // namespace boost
构造函数
template<class A>
  constexpr result( A&& a ) noexcept;
  • 确保

    *this 持有引用 static_cast<U&>( std::forward<A>(a) )

    备注

    仅当 AB&std::is_convertible<B*, U*>::valuetrue 时,此构造函数才启用。

template<class... A>
  constexpr result( A&&... a );
  • 效果
    • 如果 std::is_constructible<E, A…​>::value && !std::is_constructible<U&, A…​>::value,则确保 *this 持有错误 E( std::forward<A>(a)…​ )

    • 否则,此构造函数不参与重载解析。

    备注

    仅当 sizeof…​(A) > 0 时,此构造函数才启用。

template<class A>
  constexpr result( in_place_value_t, A&& a ) noexcept;
  • 确保

    *this 持有引用 static_cast<U&>( std::forward<A>(a) )

    备注

    仅当 AB&std::is_convertible<B*, U*>::valuetrue 时,此构造函数才启用。

template<class... A>
  constexpr result( in_place_error_t, A&&... a );
  • 确保

    *this 持有错误 E( std::forward<A>(a)…​ )

    备注

    仅当 std::is_constructible<E, A…​>::valuetrue 时,此构造函数才启用。

template<class U2, class E2>
  constexpr result( result<U2&, E2> const& r2 );
  • 确保

    如果 r2.has_value()true,则 *this 持有引用 static_cast<U&>( *r2 ),否则 *this 持有错误 E( r2.error() )

    备注

    仅当 std::is_convertible<U2*, U*>::value && std::is_convertible<E2, E>::valuetrue 时,此构造函数才启用。

查询
constexpr bool has_value() const noexcept;
  • 返回值

    *this 持有值时为 true,否则为 false

constexpr bool has_error() const noexcept;
  • 返回值

    !has_value().

constexpr explicit operator bool() const noexcept;
  • 返回值

    has_value().

检查的值访问
constexpr U& value(
  boost::source_location const & loc = BOOST_CURRENT_LOCATION ) const;
  • 效果

    如果 *this 持有引用,则返回它。否则,调用 throw_exception_from_error,将对持有的错误和 loc 的引用传递给它。

未检查的值访问
constexpr U* operator->() const noexcept;
  • 返回值

    如果 *this 持有引用,则返回指向其引用对象的指针。否则,返回 nullptr

constexpr U& operator*() const noexcept;
  • 要求

    *this 持有引用。

    返回值

    *operator->().

错误访问
constexpr E error() const &;
constexpr E error() &&;
  • 效果

    如果 *this 持有错误,则返回它。否则,返回 E()

emplace
template<class A>
  constexpr U& emplace( A&& a ) noexcept;
  • 确保

    *this 持有引用 static_cast<U&>( std::forward<A>(a)…​ )

    返回值

    包含的引用。

    备注

    仅当 AB&std::is_convertible<B*, U*>::valuetrue 时,此函数才启用。

swap
constexpr void swap( result& r );
  • 效果

    交换 *thisr 的内容。

friend constexpr void swap( result & r1, result & r2 );
  • 效果

    交换 r1r2 的内容。

相等性
friend constexpr bool operator==( result const & r1, result const & r2 );
  • 效果
    • 如果 r1 持有引用 t1r2 持有引用 t2,则返回 t1 == t2

    • 如果 r1 持有错误 e1r2 持有错误 e2,则返回 e1 == e2

    • 否则,返回 false

friend constexpr bool operator!=( result const & r1, result const & r2 );
  • 返回值

    !( r1 == r2 ).

链式调用

operator|
template<class T, class E, class U> T operator|( result<T, E> const& r, U&& u );
template<class T, class E, class U> T operator|( result<T, E>&& r, U&& u );
  • 返回 r 中的值,或者如果 r 包含错误,则返回默认值 u

    效果
    • 如果 r.has_value()true,则返回 *r

    • 否则,返回 u

    备注

    仅当 U 可转换为 T 时才启用。

    示例
    result<int> get_server_port(); // can fail
    
    int get_port()
    {
        return get_server_port() | 443;
    }
template<class T, class E, class F> T operator|( result<T, E> const& r, F&& f );
template<class T, class E, class F> T operator|( result<T, E>&& r, F&& f );
  • 返回 r 中的值,或者如果 r 包含错误,则返回通过调用函数 f 获得的默认值。

    效果
    • 如果 r.has_value()true,则返回 *r

    • 否则,返回 f()

    备注

    仅当 f() 可转换为 T 时才启用。

    示例
    result<int> get_server_port(); // can fail
    int get_default_port();
    
    int get_port()
    {
        return get_server_port() | get_default_port;
    }

    请注意,右手边是 get_default_port 而不是 get_default_port()。这很重要;以这种方式拼写,只有当 get_server_port_impl() 失败时,才会调用函数 get_default_port。如果是

        return get_server_port() | get_default_port();

    该函数将被无条件调用。

    另一种等效的方法是使用 lambda

        return get_server_port() | []{ return get_default_port(); };

    在这里是不必要的,但对于更复杂的表达式将是必需的。

template<class T, class E, class F, class R = ...> R operator|( result<T, E> const& r, F&& f );
template<class T, class E, class F, class R = ...> R operator|( result<T, E>&& r, F&& f );
template<class E, class F, class R = ...> R operator|( result<void, E> const& r, F&& f );
template<class E, class F, class R = ...> R operator|( result<void, E>&& r, F&& f );
  • 返回 r 中的值,或者如果 r 包含错误,则返回通过调用函数 f 获得的另一个 result

    Rf() 的类型。

    效果
    • 如果 r.has_value()true,则返回 *r

    • 否则,返回 f()

    备注

    仅当 Rresult 的实例且 T 可转换为 R::value_type 时才启用。

    示例
    result<int> get_server_port(); // can fail
    result<int> get_default_port(); // can fail
    
    int get_port()
    {
        return get_server_port() | get_default_port | 443;
    }
operator|=
template<class T, class E, class U> result<T, E>& operator|=( result<T, E>& r, U&& u );
  • 如果 r 包含错误,则为其赋值,从 u 构造。

    效果

    如果 r.has_value()false,则将 u 赋值给 r

    返回值

    r.

    备注

    仅当 U 可转换为 T 时才启用。

template<class T, class E, class F> result<T, E>& operator|=( result<T, E>& r, F&& f );
  • 如果 r 包含错误,则将 f() 赋值给它。

    效果

    如果 r.has_value()false,则将 f() 赋值给 r

    返回值

    r.

    备注

    仅当 f() 的类型为以下之一时才启用

    • 可转换为 T

    • 可转换为 result<T, E>result 的实例。

    示例
    result<response> query_impl( std::string_view server, request const& req );
    
    result<response> query( std::vector<std::string_view> const& servers, request const& req )
    {
        result<response> r = make_error_code( errc::invalid_argument );
    
        for( auto const& server: servers )
        {
            r |= std::bind( query_impl, server, std::ref( req ) );
        }
    
        return r;
    }
operator&
template<class T, class E, class F, class U = ...>
  result<U, E> operator&( result<T, E> const& r, F&& f );

template<class T, class E, class F, class U = ...>
  result<U, E> operator&( result<T, E>&& r, F&& f );
  • 返回 r 中的错误,或者如果 r 包含值,则通过在其上调用 f 来转换该值。

    Uf(*r) 的类型。

    效果
    • 如果 r.has_value()true,则返回 f(*r)

    • 否则,返回 r.error()

    备注

    仅当 U 不是 result 的实例时才启用。

    示例
    struct currency_type
    {
        char code_[ 4 ] = {};
    };
    
    result<double> get_exchange_rate( currency_type from, currency_type to );
    
    result<double> convert( double amount, currency_type from, currency_type to )
    {
        return get_exchange_rate( from, to ) & [&](double rate){ return rate * amount; };
    }
template<class E, class F, class U = ...>
  result<U, E> operator&( result<void, E> const& r, F&& f );
  • 返回 r 中的错误,或者如果 r 包含值,则将其替换为调用 f 的结果。

    Uf() 的类型。

    效果
    • 如果 r.has_value()true,则返回 f()

    • 否则,返回 r.error()

    备注

    仅当 U 不是 result 的实例时才启用。

template<class T, class E, class F, class R = ...> R operator&( result<T, E> const& r, F&& f );
template<class T, class E, class F, class R = ...> R operator&( result<T, E>&& r, F&& f );
  • 返回 r 中的错误,或者如果 r 包含值,则返回通过在其上调用函数 f 获得的另一个 result

    Rf(*r) 的类型。

    效果
    • 如果 r.has_value()true,则返回 f(*r)

    • 否则,返回 r.error()

    备注

    仅当 Rresult 的实例且 E 可转换为 R::error_type 时才启用。

    示例
    struct JsonValue
    {
        result<JsonValue const&> at( std::size_t i ) const noexcept;
        result<JsonValue const&> at( std::string_view key ) const noexcept;
        template<class T> result<T> to_number() const noexcept;
    };
    
    namespace helpers
    {
    
    inline auto at( std::size_t i ) {
        return [=](JsonValue const& jv){ return jv.at( i ); }; }
    
    inline auto at( std::string_view key ) {
        return [=](JsonValue const& jv){ return jv.at( key ); }; }
    
    template<class T> inline auto to_number() {
        return [](JsonValue const& jv){ return jv.to_number<T>(); }; }
    
    } // namespace helpers
    
    int get_port( JsonValue const& config, int def )
    {
        using namespace helpers;
        return config.at( "servers" ) & at( 0 ) & at( "port" ) & to_number<int>() | def;
    }
template<class E, class F, class R = ...> R operator&( result<void, E> const& r, F&& f );
  • 返回 r 中的错误,或者如果 r 包含值,则将其替换为调用函数 f 的结果。

    Rf() 的类型。

    效果
    • 如果 r.has_value()true,则返回 f()

    • 否则,返回 r.error()

    备注

    仅当 Rresult 的实例且 E 可转换为 R::error_type 时才启用。

operator&=
template<class T, class E, class F, class U = ...>
  result<T, E>& operator&=( result<T, E>& r, F&& f );
  • 如果 r 包含值,则将其替换为调用函数 fr 中的值上调用的结果。

    Uf(*r) 的类型。

    效果

    如果 r.has_value()true,则将 f(*std::move(r)) 赋值给 r

    返回值

    r.

    备注

    仅当 U 不是 result 的实例且可转换为 T 时才启用。

template<class T, class E, class F, class R = ...>
  result<T, E>& operator&=( result<T, E>& r, F&& f );
  • 如果 r 包含值,则将 r 替换为调用函数 fr 中的值上调用的结果。

    Rf(*r) 的类型。

    效果

    如果 r.has_value()true,则将 f(*std::move(r)) 赋值给 r

    返回值

    r.

    备注

    仅当 Rresult 的实例且可转换为 result<T, E> 时才启用。

    示例
    struct JsonValue
    {
        result<JsonValue const&> at( std::string_view key ) const noexcept;
    };
    
    namespace helpers
    {
    
    inline auto at( std::string_view key ) {
        return [=](JsonValue const& jv){ return jv.at( key ); }; }
    
    } // namespace helpers
    
    result<JsonValue const&> at_path( JsonValue const& jv,
        std::initializer_list<std::string_view> path )
    {
        result<JsonValue const&> r( jv );
    
        using namespace helpers;
    
        for( auto key: path )
        {
            r &= at( key );
        }
    
        return r;
    }
template<class E, class F, class R = ...>
  result<void, E>& operator&=( result<void, E>& r, F&& f );
  • 如果 r 包含值,则将 r 替换为调用函数 f 的结果。

    Rf() 的类型。

    效果

    如果 r.has_value()true,则将 f() 赋值给 r

    返回值

    r.

    备注

    仅当 Rresult<void, E2> 的实例且 E2 可转换为 E 时才启用。

<boost/system.hpp>

此便捷头文件包含之前描述的所有头文件。

历史

N1975,《TR2 文件系统库提案》,在柏林会议上被接受为库技术报告 2 (TR2),其中包含补充标准库诊断子句的其他组件。从那时起,这些错误报告组件受到了更广泛的公众审查,并且对设计进行了增强。增强版本已被 N2054,《TR2 网络库提案》使用,表明这些错误报告组件在原始文件系统库之外也很有用。

最初的提案将错误类别视为 errno(即 POSIX 样式)和本机操作系统错误代码之间的二元选择。现在,提议的组件允许实现或用户根据需要添加任意数量的其他错误类别。例如,在某些网络库实现中,由于它们构建在 POSIX getaddrinfo API 之上,该 API 使用的错误代码不是基于 errno,因此需要支持其他错误类别。

致谢

克里斯托弗·科尔霍夫和彼得·迪莫夫为设计做出了重要贡献。 Pavel Vozenilek、Gennaro Prota、Dave Abrahams、Jeff Garland、Iain Hanson、Oliver Kowalke 和 Oleg Abrosimov 也提出了意见和建议。 克里斯托弗·科尔霍夫对 N2066 论文提出了多项改进建议。 Johan Nilsson 的评论促成了 N2066 中的多项改进。

本文档是

  • 版权所有 2003-2017 Beman Dawes

  • 版权所有 2018-2022 Peter Dimov

并根据 Boost 软件许可协议 1.0 版 分发。