第20章:Freestanding

English

为什么这一章是回报

到目前为止每一章都假设你下面有一个操作系统:malloc 后面有一个堆、print 后面有一个控制台、一个 unwinder、一个加载器、一个 C 库。这一章把这些全部拿走,然后问:还剩什么。答案是:本书教给你的一切,减去那些从来没人认为是好设计的部分。

标准对这两类实现的定义很短,而且是这里一切的依据:cppreference — freestanding。继续往下读之前先看一遍那一页,因为本章剩下的内容讲的就是那一页和编译器实际提供的东西之间的差距。

类别它承诺什么你会在哪里遇到它
宿主实现(hosted) __STDC_HOSTED__ 为 1。程序有 main,可以有多个线程,并且拥有整个标准库。 桌面、服务器、手机、普通的应用程序构建
独立实现(freestanding) __STDC_HOSTED__ 为 0。程序是否有 main 由实现定义,线程由实现定义,标准库收缩为一份头文件清单 —— 而这份清单,C++ 让它空着空了三十年。 内核、引导程序、固件、嵌入式板子、WebAssembly 模块

独立实现不是"更小的 C++"。它是把操作系统拿走的 C++,而有趣的问题不是"编译器还能不能编译" —— 当然能 —— 而是"你被教导去依赖的那些东西里,哪些其实是操作系统,哪些才是语言"。

本章按顺序读什么

  1. 20.1 独立实现与宿主实现是什么 — 标准的定义、__STDC_HOSTED__,以及为什么在桌面上加 -ffreestanding 并不等于一个独立环境。
  2. 20.2 C 独立实现与 C++ 独立实现 — C 在 1989 年就有了可用的独立模式。C++ 只有一个承诺。
  3. 20.3 C++03 独立实现的谎言 — 一个要求异常、RTTI 和 new 的独立模式,甚至连 std::addressof 都没有。
  4. 20.4 Linus Torvalds 为何称之为"愚蠢透顶的主意" — 2004 年那封邮件,以及它的四条论断里哪几条成立。
  5. 20.5 辩护者从不做的一件事:自己构建交叉编译器 — 这些论证总是来自手里只有宿主工具链的人。
  6. 20.6 今天仍然缺什么 — 头文件清单、C++26 的窟窿,以及 std::array。
  7. 20.7 默认堆存储:new 与 delete — 一个全局堆不足以支撑内核,这里实测。
  8. 20.8 RTTI 与 dynamic_cast — 你链接不到的那些表和 vtable。
  9. 20.9 遗留异常在这里根本不存在 — 一个需要操作系统的机制。
  10. 20.10 赫布异常:这里唯一存在的错误处理 — 同一个机制,一个链接符号,以及 [domain](code)。
  11. 20.11 线程局部存储、浮点与程序启动 — 没人列出来的那三个特性。
  12. 20.12 std::move、std::addressof 与工具头文件 — 你绕不过去的缺失。
  13. 20.13 fast_io 是一个独立实现的 I/O 库 — 一个未定义符号为零的程序。
  14. 20.14 观看:Freestanding C++ 的过去、现在与未来 — Ben Saks,CppCon 2019。

一句话版本

C++ 是作为宿主语言设计的,并且花了三十年坚持认为独立模式只是一个边角情形 —— 而世界上部署量最大的那些代码,内核、固件、引导程序、GPU,如果不把语言自己的特性关掉、把一半库扔出去,就根本用不了它。这就是 Linus Torvalds 在 2004 年提出的批评,它是对的,而本章讲的是今天要站在正确的一边需要做什么:不要遗留异常、不要 RTTI、不要隐藏的全局堆、不要宿主库 —— 以及一个不假设这些东西存在的 I/O 库。