簡介

來自作業系統或其他低階應用程式介面 (API) 的錯誤,通常會透過一個表示錯誤碼的整數來回報,無論是直接從函式回傳錯誤碼(例如 pthread_mutex_init),還是透過邊通道(side channel),例如 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 下從 OS API 回傳錯誤

假設我們正在實作一個可移植的 file 包裝器,以封裝 OS 檔案 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 函式會回傳傳輸的位元組數,並透過類型為 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 值來自作業系統(「系統」),我們會為其使用系統類別。那是因為系統類別值可能是泛型(平台獨立)值的平台特定超集。

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 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(),加上建構時提供的字首,也是 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(),無需檢查。在沒有值的情況下,這會自動拋出一個對應於 rerror_codesystem_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() );
    }
}

因此,程式碼將執行

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

而不是直接使用 ec.assign( r, zlib_category() )

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 );
    }
}

我們可以就此停止,因為這涵蓋了我們打算做的一切,但是我們可以採取額外的步驟,為我們的錯誤碼啟用來源位置。為此,我們需要將 assign_zlib_error 變更為接受 source_location

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

定義一個輔助巨集,以避免每次都定義 static constexpr 來源位置物件的樣板程式碼

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

支援與條件進行比較

然後使用巨集代替函式

為了編碼這種關係,我們需要在我們的類別中實作 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 現在會透過預設引數自動將 BOOST_CURRENT_LOCATION 提供給 throw_exception_from_error

  • 新增了一個接受來源位置的 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 現在是僅標頭程式庫。為了相容性,仍然會建立一個 Stub 程式庫,但不再需要連結它。

  • 更多函數已標記為 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 介面,並在使用 Boost.System 時,直接或透過 Boost.ASIO 等相依性,透過 std::error_code 報告錯誤。

設計原理

error_codeerror_condition 被設計為值類型,因此它們可以被複製而不會發生切割 (slicing),也不需要堆積 (heap) 配置,但仍然可以根據錯誤類別擁有多型行為。這是透過抽象基底類別 error_category 提供多型行為來實現的,而 error_codeerror_condition 則包含一個指向衍生自 error_category 的類型物件的指標。

許多詳細的設計決策都是受到以下要求的驅使:使用者能夠添加額外的錯誤類別,並且撰寫可移植程式碼不會比撰寫特定系統程式碼更困難。

針對 error_codeoperator<< 多載消除了像 cout << ec 這樣程式碼中誤導性的轉換為 bool 的情況,其中 ec 的類型為 error_code。它本身也很有用。

參考資料

巨集

當定義了 BOOST_SYSTEM_ENABLE_DEPRECATED 時,函式庫會提供已棄用的功能以保持相容性。這些功能最終將會消失。

當定義了 BOOST_SYSTEM_USE_UTF8 時,在 Windows 上,函式庫會使用程式碼頁 CP_UTF8 返回 UTF-8 訊息,而不是預設的 CP_ACP。這個巨集在 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;
  • 回傳

    一個對應於 *thisstd::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 會儲存 lochas_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 不為 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 包含值,則傳回透過對 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 取代為對 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 包含值,則將 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 樣式)和原生作業系統錯誤碼之間的二元選擇。現在提出的組件允許實作或使用者需要盡可能多的額外錯誤類別。例如,在某些網路函式庫實作中,需要支援額外的錯誤類別,因為它們是建立在基於非 errno 的錯誤碼的 POSIX getaddrinfo API 之上的。

致謝

Christopher Kohlhoff 和 Peter Dimov 對設計做出了重要的貢獻。Pavel Vozenilek、Gennaro Prota、Dave Abrahams、Jeff Garland、Iain Hanson、Oliver Kowalke 和 Oleg Abrosimov 也提供了評論和建議。Christopher Kohlhoff 對 N2066 文件提出了多項改進建議。Johan Nilsson 的評論導致了 N2066 中的多項改進。

此文件為

  • Copyright 2003-2017 Beman Dawes

  • Copyright 2018-2022 Peter Dimov