#961·doctest

增强现有匹配器系统

作者: mitchgrout创建于 2025年10月27日更新于 2026年7月13日

考虑以下代码片段:

cpp
struct event { std::string message; std::any data; };
auto events = std::vector<event> { ... };

auto found = std::find_if(events.begin(), events.end(), [](auto event) { return event.message == "task started"; });
CHECK(found);

虽然这样做可以解决问题,但我仍然有几个疑虑:

  1. 我们需要使用 <algorithm> 来实现 std::find_if,这会增加不小的包含成本(参见 [artificial-mind.net/projects/compile-health/](https://artificial-mind.net/projects/compile-health/))。
  2. auto found = ... 表达式的大部分部分是迭代性的代码片段。
  3. 如果断言失败,我们无法了解 events 的内容(假设对于 eventstd::vector 都可以提供字符串化功能)。
    • 当在 CI/CD 上发生失败时,这种情况尤其令人恼火,因为调试并不容易获得。
    • 这可以通过明确的 CAPTURE(events) 来解决,但这感觉像是一个附加的解决方案。 从 RSpec 中获得灵感,我们可以将其重写为以下假设形式:
cpp
using namespace doctest::matchers;
CHECK(events == Includes( Splat("task started", ignore) ));

Includes 接收一个 matcher<T> 对象,基本上执行 std::find_if 操作。另一方面,Splat 接收一个 Args... 序列,这些是 T 的成员,将 T 对象拆分为其部分,然后逐个比较每个部分(请注意,这只在 T 是 POD 的情况下有效),实际上执行与 std::find_if 关联的 lambda。doctest::ignore 是一个特殊的匹配器,总是为真。这将解决前三个问题:

  1. 它直接可用于 doctest,包含和编译成本应该很低。
  2. 断言现在几乎完全由相关组件组成。
  3. 如果断言失败,表达式分解器将请求显示 events,在调试时提供更多上下文。

内容来源: doctest/doctest