第19章:遗留异常和赫布异常
本章讲什么
你从 第3.16章 起就一直在抛和接,之后每一页都只用那两条语句。你还没见过的是错误值背后的那张表:它决定一个错误码意味着什么、打印出什么、对应哪一个 ::std::errc。本章讲那张表,也讲 C++ 的另一种异常机制 —— 你必须读得懂,但绝不能使用。
| 机制 | 它的代价 | 在本书里的地位 |
|---|---|---|
| 赫布异常 | 返回路径上的两个字加一个失败位 —— 和返回错误码是同一个形状 | 唯一使用的机制,处处如此 |
| C++ 遗留异常 | 每次抛出一次堆分配、两阶段栈遍历、每个函数都要有帧表、一个 personality routine、处理匹配需要 RTTI | 只读。通过编译选项从每个构建里移除(第19.7章) |
第二行的简要版本:遗留异常不只是慢(每次抛出约一微秒,在 第19.8章 中测量过)。在你大多数要发布到的目标上,它们根本不存在 —— freestanding、内核、引导程序、嵌入式板子、WebAssembly —— 因为它们依赖的 unwinder、加载器注册以及 __cxa_* 运行时在那些地方都没有。
本章怎么读
只有当你写的库拥有自己的错误码空间时才去读 19.1。但仍然要仔细读,因为它开篇就是那条最省事的建议:运行时里的错误域本来就是拿来复用的,其中一个几乎覆盖了一切。
先读 19.2:它决定了什么才算错误,本章其余内容都由它推出。然后把 19.3、19.4 和 19.5 当作识别训练来读:你将来要维护这种代码。再读 19.6,那是"某个依赖把 std::runtime_error 递到你手上"那天你需要的那座桥的两个方向。19.7 则是现在就该抄进构建里的五个选项。
-
第19.1章:定义与覆盖错误域 —
::std::error_domain_singleton的五个钩子、其中唯一必需的那个、error_domain<T>特化、一个完整的 CSV 错误域,以及环境需要时如何替换随运行时提供的错误域 -
第19.2章:
std::abort()、std::terminate()与__builtin_trap()— 为什么分配失败、尺寸溢出和不变量被破坏不是错误,结束进程的三种写法,以及 fast_io 在什么情况下崩溃 -
第19.3章:C++ 遗留异常 — 这套机制怎么工作、怎么读它:抛出拷贝了什么、处理块匹配到什么、展开析构了什么,以及
exception_ptr让什么活下去 -
第19.4章:
<stdexcept>—logic_error/runtime_error那棵树、what()实际返回什么,以及这个头文件招来的切片 bug -
第19.5章:
<system_error>—error_code、带 vtable 和 ABI 伤疤的error_category,以及这个设计为何没有统一反而碎片化 -
第19.6章:跨越边界 — 遗留异常到赫布异常是自动的,赫布异常到遗留异常要调用
throw_legacy_exception(),以及路上丢掉了什么 -
第19.7章:用构建选项禁止遗留异常 —
-fno-exceptions -fno-rtti -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-ident,以及每一个各删掉了什么 - 第19.8章:C++ 遗留异常的问题 — 实测的代价、它需要的运行时、没有它的那些环境、不健全的角落,以及两套 ABI
-
第19.9章:fast_io 的分配器选项 — 分配器就是策略:如何让 fast_io 容器上报
::std::errc::not_enough_memory而不是终止进程,以及标准分配器模型为何表达不了其中任何一点 - 第19.10章:De-fragmenting C++ — Herb Sutter 的 CppCon 2019 演讲,本章所教一切的发源