Boost C++ 库

...世界上最受推崇、设计最精良的 C++ 库项目之一。 Herb SutterAndrei AlexandrescuC++ 编码标准

Boost.System: 可扩展的错误报告 - Boost C++ 函数库

介绍

来自操作系统或其他低级应用程序编程接口 (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 下,系统类别表示 OS API 返回的 errno 值(通用类别的超集),而在 Windows 下,系统类别表示 GetLastError() 返回的错误值。

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

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

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 下从 OS API 返回错误

假设我们正在实现一个围绕 OS 文件 API 的可移植 file 包装器。其一般概要如下:

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 函数返回传输的字节数,并通过类型为 boost::system::error_code 的输出参数 ec 信号错误。

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 值来自 OS(“系统”),我们使用系统类别来表示它。这是因为系统类别值可能是通用(平台独立)值的平台特定超集。

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 下从 OS 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 实现存在一个问题;它接受 sizestd::size_t 值,但是当请求值不适合 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 constexpr,需要使用 static const。另一个选项是 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(),加上构造时提供的前缀,也是 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(因为错误可能由 OS API 返回,而不是函数本身,就像我们的 readwrite 实现中的情况一样)。

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

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

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

正确的方法不是将 ec 与特定的错误码进行比较,而是与 error_condition( EINVAL, generic_category() ) 进行比较。错误条件是一种独立于平台的方式,用于表示具体错误码的含义。在我们的例子中,所有表示 EINVAL 的错误码,无论是在 POSIX 还是 Windows 下,都将与 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 条件。例如,Z_STREAM_ERROR 在 POSIX 函数会返回 EINVAL 的情况下返回;Z_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++ 上)。

  • value_typeerror_typein_place_valuein_place_error 添加到 result<>

  • emplace 添加到 result<>

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,而不是来自系统类别。这在 POSIX 中发生,当输入 error_code 来自系统类别且不对应任何 errc_t 值时。

  • 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(其中 ecerror_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_conditionerrc::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() == 0category() == system_category()!has_location()

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

    value() == valcategory() == cat!has_location()

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

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

    确保

    value() == valcategory() == cathas_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() == 0category() == generic_category()

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

    value() == valcategory() == 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() == valcategory() == 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() == 0category() == 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 不为空,则为 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)…​ )

    返回

    对包含值的引用。

交换
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 存储一个未指定的值。

交换
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 时启用。

交换
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_default_port 仅在 get_server_port_impl() 失败时被调用。如果是

        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 包含一个值,则返回通过对 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 获得的另一个 result

    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 包含一个值,则将其替换为通过对 r 中的值调用函数 f 而获得的结果。

    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 中的值调用函数 f 而获得的结果。

    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 包含一个值,则将其替换为通过调用函数 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

致谢

Christopher Kohlhoff 和 Peter Dimov 对设计做出了重要贡献。我们还收到了 Pavel Vozenilek、Gennaro Prota、Dave Abrahams、Jeff Garland、Iain Hanson、Oliver Kowalke 和 Oleg Abrosimov 的评论和建议。Christopher Kohlhoff 建议对 N2066 论文进行多项改进。Johan Nilsson 的评论促成了 N2066 中的多项改进。

本文档

  • 版权所有 2003-2017 Beman Dawes

  • 版权所有 2018-2022 Peter Dimov