C++ Insights 是一个基于 Clang 的源码到源码转换工具,其声明的目标是让那些通常且有意发生在幕后的内容变得可见:用项目自己的话来说,就是“编译器为了让事情正常运行而为我们做的魔法”。与其生成汇编代码或抽象语法树 (AST) 转储,它将 C++ 代码片段重新改写为 C++ —— 即程序员编写的语言 —— 从而使编译器的内部降级过程在熟悉的语法中变得可读。

C++ Insights 项目徽标
C++ Insights 徽标,标识该基于 Clang 的源码到源码转换工具。github.com

第一段介绍了 C++ Insights 是一个基于 Clang 的转换工具,其目的是让编译器的幕后降级过程可见;徽标标识了文章描述的项目。

这种动机源于 C++ 通常被教授和调试方式中的空白。Clang 可以输出 AST 转储,像 Compiler Explorer 这样的工具可以显示编译器从代码片段生成的汇编代码。但这两类输出都不是用 C++ 编写的,项目的作者表示他发现这些输出并不熟悉。在教学生学习 C++ 时,展示一个 AST 并断言“所有内容都在其中”并不令人满意。C++ Insights 的编写正是为了填补这一空白。

作者 Andreas Fertig 于 2017 年在研究 C++11、C++14 和 C++17 的新特性(如 lambda、基于范围的 for 循环和结构化绑定)时开始了这项工作,并最初以包含发布幻灯片和视频的演讲形式展示。随后,他编写了一个基于 Clang 的工具,将基于范围的 for 循环转换为其编译器内部版本,然后对结构化绑定和 lambda 做了同样的处理。该项目的发展超出了最初的目标:它现在还能显示运算符被调用的位置以及编译器插入转换的位置,并且可以推断出 `auto` 或 `decltype` 背后的类型。

该工具的一个经典示例是一个小型类层次结构。给定:

class Base { }; class Derived : public Base { }; int main() { Derived d; Derived d2 = d; d2 = d; Base& b = d; }

C++ Insights 生成:

class Base { public: // inline constexpr Base() noexcept = default; // inline constexpr Base(const Base&) noexcept = default; // inline constexpr Base& operator=(const Base&) noexcept = default; }; class Derived : public Base { public: // inline constexpr Derived() noexcept = default; // inline constexpr Derived(const Derived&) noexcept = default; // inline constexpr Derived& operator=(const Derived&) noexcept = default; }; int main() { Derived d; Derived d2 = Derived(d); d2.operator=(d); Base& b = static_cast<Base&>(d); return 0; }

andreasfertig/cppinsights 的 GitHub 仓库页眉
项目的 GitHub 仓库页眉,带有其名称以及关于通过编译器的视角审视源代码的标语。github.com

可用性段落列出了命令行工具、cppinsights.io Web 服务、编辑器集成和 Homebrew 配方;仓库页眉将读者引导至项目在 GitHub 上的主页。

输出揭示了原始源代码隐藏的两件事。首先,编译器为 `Base` 和 `Derived` 都提供了特殊成员函数 —— 默认构造函数、拷贝构造函数和拷贝赋值运算符 —— 每个函数都标注了 `inline constexpr` 和 `noexcept`。其次,`main` 函数中的隐式操作被重写为显式操作:拷贝 `Derived d2 = d` 变成了显式构造 `Derived(d)`,赋值 `d2 = d` 变成了显式成员调用 `d2.operator=(d)`,而引用绑定 `Base& b = d` 则变成了显式的上行转换 `static_cast<Base&>(d)`。

同样的机制也适用于作者最初目标的功能。基于范围的 for 循环被重写为循环脱糖后的显式形式。Lambda 被重写以显示编译器生成的闭包类型及其 `operator()`。`auto` 或 `decltype` 推断出的类型直接显示在原位。贯穿始终的目标是生成可编译的代码,尽管项目指出并非所有地方都能做到这一点。

C++ Insights 可作为命令行工具、cppinsights.io 上的 Web 服务,以及 Vim、Neovim、Visual Studio Code 和 Homebrew(在 macOS 上)的集成插件提供。命令行调用接收一个源文件和一个标准标志,例如 `insights <YOUR_CPP_FILE> -- -std=c++17`。系统包含路径是从二进制文件的构建编译器中硬编码的,而辅助脚本 `scripts/getinclude.py` 则从编译器中收集这些路径;如果传递一个不在默认路径上的自定义 GCC 安装,则需要 `--gcc-toolchain` 参数指向 STL 位置。

项目自身的文档中列出了几点注意事项。作者“并不声称获取了所有正确的内容”。在文档编写时,对 C++20 等新标准特性的支持正在进行中。该工具基于 Clang 及其对 AST 的理解,因此其输出反映了该解析器对代码的模型。Web 服务紧密跟踪最新的 Clang 发布,尽管由于构建 Windows 版 Clang 的困难,其版本往往会延迟几个月。

由于提供的唯一实质性来源是项目的官方仓库页面,上述所有声明均可归因于项目的文档,而非独立验证。重要的未知因素包括 Web 服务当前运行的准确 Clang 版本、C++20 及后续功能支持的完整性,以及重写后的输出在文档未列举的边缘情况下编译的忠实程度。对于想要直接测试该工具的读者,cppinsights.io 上的 Web 服务不需要本地安装。