现代化一个拥有 25 年历史的极简 C++ 单元测试框架(第二部分)
Modernizing a 25-year-old minimal C++ unit testing framework (Part 2)

原始链接: https://freshsources.com/code-capsules/test-part2/

本文探讨了一个简单的 C++ 单元测试框架存在的两个局限性:一是需要项目级的成功/失败计数器,二是基于头文件的代码重复带来的开销。 作者提出了两种现代解决方案: 1. **C++17 内联变量(Inline Variables):** 通过将测试计数器声明为 `inline`,它们可以在不同编译单元间共享同一个内存地址,且不会违反“单一定义规则”(ODR)。这在保持简单的头文件实现方式的同时,修复了计数错误。 2. **C++20 模块(Modules):** 为了实现更好的封装和编译效率,核心逻辑被迁移至模块中。由于宏无法从模块中导出,作者采用了混合方法:`test` 模块负责处理逻辑,而配套的头文件(`test_macros.h`)提供捕获表达式文本所需的宏。此外,利用 `std::source_location` 替代了手动报告文件和行号的方式。 尽管模块提供了更优的结构,但作者建议在一般用例、课程作业和跨平台需求中,继续使用基于头文件的方法。基于模块的方法被视为项目在拥有强大工具链支持后的长期目标,并承认在可预见的未来,“模块+头文件”的混合模式仍将是一种现实且实用的选择。

```Hacker News最新 | 过往 | 评论 | 提问 | 展示 | 招聘 | 提交登录现代化一个 25 年历史的极简 C++ 单元测试框架(第二部分)(freshsources.com)6 分,作者:chuckallison,2 小时前 | 隐藏 | 过往 | 收藏 | 讨论 帮助 指导原则 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索: ```
相关文章

原文
test-part2

C++ Code Capsules


In Part 1 of this two-part series I introduced a time-tested (i.e., old :-)) technique that handled automated unit testing in a remarkably simple way, including validating proper exception handling. The previous post left two problems on the table, however, and the journey to fix them turns out to be a nice tour of two key features of modern C++: inline variables and modules.

The simplicity of the test framework discussed in Part 1 follows from everything being contained in a small header file, test.h (include guards not shown):

  1. One Definition Rule (ODR).
  2. Dependencies on header files have long been recognized as a source of headaches in C++. The macros above call inline functions contained in the anonymous namespace, so each file under test gets it own copy of the code. Modules were introduced in C++20 to alleviate such issues.

It turns out that macros will still be needed here, so I will take a hybrid approach to move as much as possible into a module.


Inline Variables

C++17 introduced the notion of inline variables. Just as with functions, inline variables may be defined in multiple translation units, and the linker is required to collapse all those definitions into one. The rules mirror those for inline functions:

  • All definitions must be identical (same tokens, same types, same initializer).
  • The variable has external linkage by default at namespace scope.
  • It is guaranteed to be the same object across all translation units (same address everywhere).

The fix here is to choose a named namespace and declare nPass and nFail to be inline:

std::source_location introduced in C++20:

here. I would like to thank Bjarne Stroustrup for his helpful comments on a previous draft of this post.)

联系我们 contact @ memedata.com