Jackarain 的 blog

喜欢 c++ 语言,在这里记录一些所见和所思...

论 c++ 中 error_code 的设计

20 August 2025


这篇文章是我在知乎上回复关于 error_code 的提问整合而来的,error_code 这套东西是出自 Beman Dawes(boost 库发起者, 已经去世了) 与 Christoper Kohlhoff(asio 作者) 等一众大佬的设计。

error_code 的设计雏形之初是 Kohlhoff 为了解决 asio 中系统错误代码和自定义错误代码的统一所做的抽象设计。

试想,编写一个网络库,要如何将系统错误码,在不丢失信息的情况下(也就是保留系统错误代码,比如 windows 上 10061 代码表示 “由于目标计算机积极拒绝,无法连接。 ” 这2个信息都不能丢失)如实的汇报给调用者。

还有库特定的错误代码,也要能在不增加使用者负担的情况下,以同样的方式给调用者使用。

另外在不同操作系统上不同错误代码,还要可以根据语义上的统一,去使用 error_code / error_condition,比如 operation_aborted 它无论在 windows 还是 linuxmacos 中或其它系统,语义上都是相同的,无论实际的系统错误代码值是什么值,像这种抽象设计就可以在编程中根据语义来判定错误代码,从而去做相关的业务逻辑处理,而不需要使用者去区分什么操作系统啊、错误码是啥,等等这些麻烦事。

error_code 在进入 boost 之后,就被广泛使用,再后来都不需要 Kohlhoff 出手(实际是由 Beman 大佬等人推动的),就顺理成章的进入了 c++11 标准。

在我第一次看过它的设计之后我就喜欢上了, 并且一直在使用, 它有很多优点, 包括上面提到的,还有比如不必担心错误代码值被重复定义(可以通过自己定义的不同 error_category), 有更可读的错误信息(message)而不止是一个数值码。

error_code 的设计中,其中可能让人难以理解的就属 error_condition,具体而言,error_condition 就是为了实现可移植性的 error_code,如 no_such_file_or_directory 就是不同平台上的相同错误类型,但错误代码 value 并不一定相同,例如 std::filesystem::copy 函数的错误处理:

std::error_code ec;
std::filesystem::copy(from, to, ec);

if (ec == std::errc::no_such_file_or_directory) {
  // ...
}

在上面代码中

if (ec == std::errc::no_such_file_or_directory)

std::errc::no_such_file_or_directory 则是与平台无关的 error_condition,而 ec.value() 是平台相关的错误代码,也就是操作系统返回的错误值。

而如果写成:

if (ec.value() == ERROR_PATH_NOT_FOUND)

则不具可移植性,仅在 windows 下有效(ERROR_PATH_NOT_FOUNDwindows 上的具体错误代码定义)。

简而言之,error_code 是具体的错误值,通常包含的信息就是操作系统返回的,而 error_condition 是抽象的与平台无关的错误表示。

再举一个比较易懂示例就是,比如在一些平台中可能 EAGAIN 并不等于 EWOULDBLOCK,但是这2个错误值完全表达了一个意思,所以,无论 OS 返回的 ec.value()EAGAIN 或是 EWOULDBLOCK,都对应到 std::errc::resource_unavailable_try_again 这个 error_condition,你不需要关心 EAGAINEWOULDBLOCK 在某些系统上是否是同一个值,只需要判断

if (ec == std::errc::resource_unavailable_try_again)

这样就包含了 EAGAINEWOULDBLOCK 两种情况,完整示例代码:

#include <iostream>
#include <system_error>
#include <unistd.h> // 包含 EAGAIN 相关定义

int main()
{
    {
        // 这里模拟产生 EWOULDBLOCK 的系统错误
        std::error_code ec(EWOULDBLOCK, std::system_category());

        if (ec == std::errc::resource_unavailable_try_again) {
            std::cout << "resource_unavailable_try_again\n";
        } else {
            std::cerr << "其他错误: " << ec.message() << std::endl;
        }
    }

    {
        // 这里模拟产生 EAGAIN 的系统错误
        std::error_code ec(EAGAIN, std::system_category());

        if (ec == std::errc::resource_unavailable_try_again) {
            std::cout << "resource_unavailable_try_again\n";
        } else {
            std::cerr << "其他错误: " << ec.message() << std::endl;
        }
    }

    return 0;
}

当然,包含在 std 中的 error_code 并不完整,若 std 中没有定义的话,就得自己扩展,关于 error_code 扩展以后有空再写吧。。。




访问量 加载中...