尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

error_code

error_code
📅 发布时间:2026/6/19 14:54:24

1. 为什么需要 error_category?

在 C++ 开发中,我们经常遇到从底层(操作系统、网络库、第三方库)返回的 int 类型的错误码。

核心问题:数字是有歧义的。

  • 错误码 1 在 Linux 系统 中可能表示 "Operation not permitted" (EPERM)。
  • 错误码 1 在 HTTP 协议 中可能完全不是这个意思。
  • 错误码 1 在 你的自定义库 中可能表示“初始化失败”。

如果只给你一个数字 1,你无法知道它到底是什么错误。std::error_category 的作用就是给这个数字赋予“上下文(Context)”。

2. 它是如何工作的?

C++ 使用 std::error_code 对象来表示一个具体的错误,它内部包含两部分:

  1. Value (int): 错误的数值(例如 1, 404, 500)。
  2. Category (const std::error_category\*): 一个指向分类对象的指针,标明这个数字属于哪个领域。

$$\text{std::error_code} = \text{整数值} + \text{错误类别指针}$$

判断两个错误是否相等时,不仅比较数值,还要比较类别。

3. 标准库自带的类别

C++ 标准库预定义了几个常见的 error_category(它们都是单例):

  1. std::generic_category(): 对应标准的 POSIX 错误码(如 errno)。
  2. std::system_category(): 对应操作系统的底层错误(在 Windows 上可能是 Win32 API 错误,在 Linux 上通常和 generic 一样)。
  3. std::iostream_category(): 用于 IO 流的错误。

4. 代码示例:同样的数字,不同的含义

这个例子展示了即使错误数值都是 1,因为类别不同,它们被视为不同的错误。

#include <iostream>
#include <system_error>
#include <string>int main() {// 创建两个错误码,数值都是 1,但类别不同std::error_code ec_system(1, std::system_category());std::error_code ec_generic(1, std::generic_category());std::cout << "错误 1 (System): " << ec_system.message() << std::endl;std::cout << "错误 1 (Generic): " << ec_generic.message() << std::endl;// 比较它们if (ec_system == ec_generic) {std::cout << "它们是同一个错误" << std::endl;} else {std::cout << "它们是不同的错误 (因为 category 不同)" << std::endl;}// 获取类别名称std::cout << "Category Name: " << ec_system.category().name() << std::endl;return 0;
}

5. 进阶:如何自定义 error_category?

这是 error_category 最强大的地方。如果您在写一个库(比如叫 MyLib),您不应该直接返回 int,也不应该借用系统的错误码。您应该定义自己的类别。

实现步骤如下:

  1. 继承 std::error_category。
  2. 实现 name() 方法(返回类别名称)。
  3. 实现 message(int) 方法(根据错误码返回对应的字符串解释)。
  4. 定义一个单例函数来获取这个类别。
// 简化的自定义类别示例
class MyLibCategory : public std::error_category {
public:const char* name() const noexcept override {return "MyLib";}std::string message(int ev) const override {switch (ev) {case 1: return "MyLib: 初始化失败";case 2: return "MyLib: 网络连接断开";default: return "MyLib: 未知错误";}}
};// 获取单例
const std::error_category& my_lib_category() {static MyLibCategory instance;return instance;
}int main() {// 创建一个属于我们自己库的错误std::error_code my_err(2, my_lib_category());std::cout << "错误信息: " << my_err.message() << std::endl;// 输出: 错误信息: MyLib: 网络连接断开return 0;
}

简单来说:

  1. error_category 负责定义错误的身份(是系统错误?还是网络库错误?)。
  2. std::system_error 是一个容器,它把 error_category + 错误码 包装成一个异常对象。
  3. exception_ptr 负责把这个异常对象在线程之间搬运。

6.联合使用的工作流

这种模式通常用于:底层(基于错误码的)API 在子线程报错,需要将具体的错误码和类别完整地传回主线程处理。

流程步骤:

  1. 产生错误:底层 API 返回一个 int 错误码。
  2. 打包:使用 std::error_code (包含 int 和 error_category) 创建一个 std::system_error 异常并抛出。
  3. 捕获与保存:在子线程捕获该异常,用 std::current_exception 存入 exception_ptr。
  4. 传递:将指针传给主线程(通过 std::promise 或共享变量)。
  5. 解包与处理:主线程 rethrow,捕获 std::system_error,然后从中取出原始的 error_category 进行精确判断。

代码示例

这个例子模拟了一个子线程调用底层 OS API(返回错误码),然后主线程精准识别该错误的场景。

#include <iostream>
#include <thread>
#include <future>
#include <system_error>
#include <fstream>// 模拟一个底层的、基于错误码的函数
// 比如它返回 errno 风格的错误码
int lowLevelOsOperation() {// 模拟错误:2 通常代表 ENOENT (No such file or directory)return 2; 
}void workerThread(std::promise<void> prom) {try {int err = lowLevelOsOperation();if (err != 0) {// 【关键点 1】将 错误码 + 类别 打包成 std::system_error 抛出// 这里我们明确指明这是 generic_category (POSIX 标准错误)throw std::system_error(std::error_code(err, std::generic_category()), "读取底层文件失败");}prom.set_value();} catch (...) {// 【关键点 2】捕获异常并转为 exception_ptr (存入 promise)prom.set_exception(std::current_exception());}
}int main() {std::promise<void> prom;std::future<void> fut = prom.get_future();std::thread t(workerThread, std::move(prom));try {// 等待结果,如果有异常会在这里重新抛出fut.get();} catch (const std::system_error& e) { // 【关键点 3】专门捕获 system_error// 【关键点 4】拆包:获取原始的错误码和类别const std::error_code& ec = e.code();std::cout << "--- 主线程捕获系统错误 ---" << std::endl;std::cout << "错误描述: " << e.what() << std::endl;std::cout << "错误数值: " << ec.value() << std::endl;std::cout << "错误类别: " << ec.category().name() << std::endl;// 精确判断:不仅判断数值,还判断类别if (ec.category() == std::generic_category() && ec.value() == 2) {std::cout << ">> 判定原因:找不到指定的文件。" << std::endl;}} catch (const std::exception& e) {std::cout << "捕获到普通异常: " << e.what() << std::endl;}t.join();return 0;
}

为什么要这么麻烦?(优势)

如果只用 std::runtime_error("error 2") 传递字符串,主线程就得解析字符串来猜错误原因,非常脆弱。

联合使用 exception_ptr + system_error (含 error_category) 的好处是:

  1. 类型安全:主线程可以拿到原始的 int 错误码,而不是文本。
  2. 上下文明确:主线程知道这个 2 是 generic_category 里的 2(文件不存在),而不是 iostream_category 里的 2。
  3. 无损传递:异常从子线程到主线程,就像在同一个线程里抛出一样,保留了所有的元数据。

这种模式是 C++ 开发高性能服务器(如基于 Asio 网络库)时的标准错误处理范式。

相关新闻

  • 好用的大型牛场水滴粉碎机技术强的
  • function bind
  • Item6--若不想使用编译器自动生成的函数,就该明确拒绝

最新新闻

  • 六安生日蛋糕推荐|不同场景怎么选?定制/多口味/高性价比全解 - 速递信息
  • OpenClaw 入门指南:轻量级 AI 技能运行时安装与首个 MySQL Skill 实战
  • NVIDIA Profile Inspector完全指南:解锁显卡隐藏功能的终极利器
  • 2026 常熟贵金属回收市场全攻略|黄金变现高价出手避坑指南 - 速递信息
  • 研究生必备9款免费AI论文神器半天生成12万字带真实文献引用 - 麟书学长
  • 基于Miniblink49构建轻量级UI自动化测试框架:从原理到实践

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号