From 91868302c7224f99913978219e024675913e3a6e Mon Sep 17 00:00:00 2001 From: Zhe Feng Date: Mon, 9 Nov 2026 12:28:23 +0000 Subject: [PATCH 1/9] docs(agents): design the module test hardening for 34 popular libraries --- ...2026-11-09-module-test-hardening-design.md | 517 ++++++++++++++++++ .agents/docs/README.md | 11 +- 2 files changed, 527 insertions(+), 1 deletion(-) create mode 100644 .agents/docs/2026-11-09-module-test-hardening-design.md diff --git a/.agents/docs/2026-11-09-module-test-hardening-design.md b/.agents/docs/2026-11-09-module-test-hardening-design.md new file mode 100644 index 00000000..3a88cdb6 --- /dev/null +++ b/.agents/docs/2026-11-09-module-test-hardening-design.md @@ -0,0 +1,517 @@ +--- +subject: tests +status: active +--- + +# 模块封装层测试加固:常用 34 个库的示例覆盖与自定义类型套件 + +> 日期:2026-11-09 · 状态:设计;§9 的问题均已答复 +> +> 输入:按 boost.org「most popular」排序选出的 34 个库;要求补全被测 API,至少覆盖官方 +> 使用示例;凡支持自定义类型的库,必须带自定义类型的测试套件;按 +> `tests//.cpp` 组织,必要时用 `tests//mcpp.toml` 建独立测试工程。 + +## 0. 结论 + +1. 34 个库的测试从 `tests/.cpp` 的单文件 smoke 改为目录 `tests//`,每个文件 + 仍是一个独立程序(`mcpp test` 的既有语义),测试名随之变为 `/`。 + 其余库保持平铺,不在本次范围内。 +2. 每个目录有固定的几类文件:`smoke.cpp`(原测试原样迁入)、`api_.cpp`(按主题 + 补齐的 API)、`examples_.cpp`(官方示例的移植)、`custom_types.cpp`(自定义 + 类型套件,凡有定制点的库必须有)。必要时再加 `include_.cpp`,放纯 include 形态的 + 宏 API。 +3. 测试框架用 doctest,从 mcpp index 的 `compat.doctest`(2.4.12)引入,放在根清单的 + `[dev-dependencies]` 中,只有 `mcpp test` 会解析它,发布出去的包不受影响。 + 不自写框架;也不用 Boost.Test,原因有二:它有两种互斥的形态,而且它本身就是被测对象(§4.1)。 +4. 「覆盖官方示例」以清单的方式落实:`tests//coverage.toml` 把 Boost 1.92.0 发布包中 + 该库的每个示例文件(以及文档里的示例片段)映射到一个用例,或者写明排除理由。 + `tools/ci/check_test_coverage.py` 检查这份清单。vendored 树已经裁掉了 `example/` 和 + `doc/`,所以清单是提交进仓库的事实,升级 Boost 时重新生成。 +5. 独立测试工程只用于根工程无法表达的场景:`dll`(需要构建共享库插件)与 `test` + (Boost.Test 的两种互斥形态)。根清单用 `[test] discover` 的 `!` 规则把这两个目录 + 排除出根测试,CI 再分别进入这两个目录运行测试。 +6. 先做 P0 验证(§8),再动手写测试,重点验证 gcc 16 下 include 与 import 混用的问题。 + 这个问题决定了 json、geometry、hash2、container_hash、describe 这些库的自定义类型套件 + 能否走 import 形态。 +7. conversion 的模块化放在本轮一起做(§5.3)。它没有模块,原因是生成器的头文件启发式漏掉了 + 它(§9 Q4),并非技术限制。修复先于 conversion 的测试,排在 P1。 + +## 1. 现状 + +### 1.1 现有测试 + +`tests/` 下每个 `.cpp` 是一个独立程序。`mcpp test` 默认发现 `tests/**/*.cpp`,测试名是 +文件相对 `tests/` 的路径去掉扩展名。目标库的测试几乎都是 20~60 行的 smoke:只调几个 +入口,以 `assert` 判定,没有自定义类型(`describe.cpp` 例外),也不对照官方示例。 + +| 库 | 现有测试 | 形态 | +|---|---|---| +| 31 个有模块的库 | `tests/.cpp` 各一 | `import mcpplibs.mwrap.boost.;` | +| describe | `tests/describe.cpp` | 纯 include(无模块,见 docs/usage.zh.md §2.3) | +| test | `tests/test_utf.cpp`、`tests/test_included.cpp` | 纯 include,两种形态由 `MCPP_FEATURE_UNIT_TEST_FRAMEWORK` 切换 | +| context、coroutine2 | 无 | 未声明(需要汇编工具链,usage §2.5) | +| conversion | 无 | 没有模块,也没有 feature;`polymorphic_cast` 等头文件在 vendored 树中 | + +### 1.2 官方示例的来源 + +`upstream/boost/` 是裁剪过的发布树,保留了 `libs//test/`,但没有 `example/` 和 +`doc/`。本次从 `upstream/boost/SOURCE.md` 记录的官方地址下载了 +`boost_1_92_0.tar.gz`(SHA-256 `c4a3b310…296ce`,与记录一致),解压到 `.tmp/boost-ref/` +(已被 gitignore),只作为参考,不进仓库。各库的示例规模: + +| 规模 | 库(`example/` 下 `.cpp`/`.hpp` 文件数) | +|---|---| +| 巨大 | asio 240、geometry 202、math 171、test 143、beast 70、decimal 46 | +| 中等 | callable_traits 37、context 27、bimap 27、dll 27、url 26、regex 22、container 20、describe 16 | +| 小 | json 12、cobalt 12、hash2 11、mqtt5 11、convert 7、container_hash 7、algorithm 5、circular_buffer 5、coroutine2 5、bloom 4、dynamic_bitset 3、unordered 3、crc 1 | +| 只在文档里 | uuid、variant2、any、compat、charconv、core、conversion(示例写在 `doc/*.adoc` / `*.qbk` 中) | + +巨大的几类大多是网络服务、命令行工具和基准:asio 的 `cpp11/http`(32 个)、beast 的 +websocket 与 http server/client,math 的大量数值演示,test 的 143 个框架演示。 +「逐一照搬」既不可行,也不是测试的目的,因此 §4.5 给出收录规则。 + +### 1.3 已验证的 mcpp 行为(mcpp 2026.11.5.1,在 `.tmp/probe` 的最小工程上实测) + +- `tests/foo/one.cpp` 能被发现,测试名为 `foo/one`;`mcpp test foo` 只跑 `foo/*`; + 同目录的 `.hpp` 不会被当成测试。 +- **子目录里放 `mcpp.toml` 并不能把它从根测试中排除**:根工程照样把 `tests/bar/x.cpp`、 + `tests/bar/src/main.cpp`、`tests/bar/tests/t.cpp` 都当成自己的测试。 +- 根清单的 `[test] discover = ["tests/**/*.cpp", "!tests/bar/**"]` 能正确排除。 + (`[test]` 支持的键:`discover`、`main`、`start`、`windows_code_page`、`windows_long_paths`。) +- 嵌套工程用 `{ path = "../.." }` 依赖根包,在它自己的目录里运行 `mcpp test` 可以通过; + 在根清单中声明 `[workspace] members = ["tests/bar"]` 后,`mcpp test --workspace` + 也能跑到它。 +- `[targets.] kind = "shared"` 受支持(从二进制的诊断信息确认,尚未实测), + dll 插件依赖这一点。 + +### 1.4 约束 + +- CI 有 5 台宿主,每台跑 A、B 两组;`mcpp test` 编译的是基础源文件的并集,因此**每个 + 测试程序在 10 条腿上各编译一次**,单腿超时 240 分钟。测试的数量和单个 TU 的重量直接 + 乘到 CI 时间上。 +- 具名模块不导出宏。凡是以宏注册定制点的 API(`BOOST_DESCRIBE_STRUCT`、 + `BOOST_GEOMETRY_REGISTER_POINT_2D`、`BOOST_FUSION_ADAPT_STRUCT` 等),用户 TU 都必须 + include 头文件。 +- gcc 16 在同一 TU 中 include 与 import 混用时,对部分实体族报 ODR 冲突,这是 + `describe.cpp`、`statechart.cpp`、`scope_exit.cpp` 的先例。`hof`、`units` 的 CI 证明 + 并非所有库都会出问题。而 `generated/exports/json.inc` 重新导出了 + `boost::describe::*`,所以 json 加 describe 恰好是高风险组合。 +- 本机只有 9 GB 内存,按 docs/architecture §10 的预算构建,不做全树并行。 + +## 2. 范围 + +### 2.1 库名规范化 + +| 输入 | 解释 | 备注 | +|---|---|---| +| `char_conv` | `charconv` | | +| `cicular_buffer` | `circular_buffer` | | +| `bitmap` | `bimap` | 已确认(§9 Q1) | +| `conversion` | Boost.Conversion(`polymorphic_cast`、`polymorphic_downcast`、`polymorphic_pointer_cast`、`polymorphic_pointer_downcast`、`implicit_cast`) | 与 `convert` 不是同一个库;本轮新增模块(§5.3) | +| `test` | Boost.Test | 两种形态,独立工程 | +| `context`、`coroutine2` | 未声明的库(coroutine2 依赖 context) | 已确认:只记为不支持,不在本次接入(§9 Q2) | + +### 2.2 不在范围 + +- 列表之外的库,以及这些库的平铺测试。 +- 修改生成器或导出策略。测试暴露出的导出缺口按既有流程(`tools/export_policy.toml`、 + 补丁)另行修复,测试中先以 `// GAP:` 注释加 `coverage.toml` 的 `gap` 状态记录, + 不在测试里绕开。 + +## 3. 目录与命名 + +```text +tests/ +├── test_assert.hpp 保留,平铺测试与迁入的 smoke.cpp 继续使用 +├── json/ +│ ├── smoke.cpp 原 tests/json.cpp,git mv 迁入 +│ ├── api_value.cpp value/object/array/string 的完整操作 +│ ├── api_parse_serialize.cpp parser/stream_parser/serializer/parse_options +│ ├── api_pointer.cpp JSON Pointer、at_pointer、set_at_pointer +│ ├── examples.cpp example/*.cpp + 文档片段 +│ ├── custom_types.cpp value_from/value_to/parse_into 的自定义类型(tag_invoke 形态) +│ ├── include_describe.cpp BOOST_DESCRIBE_STRUCT 驱动的转换(视 P0 结论,§4.4) +│ └── coverage.toml 示例 → 用例 / 排除理由 +├── dll/ 独立工程(§5) +│ ├── mcpp.toml +│ ├── plugin_sum.cpp … +│ └── coverage.toml +└── … +``` + +- 文件名只用小写字母、数字和 `_`。测试名形如 `json/api_value`,CI 日志和 + `mcpp test json/` 都能直接定位。 +- 一个文件对应一个主题,不对应一个示例。callable_traits 有 37 个示例,每个只有几行, + 逐个成文件会让编译开销翻 37 倍(每个 TU 都要加载一次 BMI)。示例以用例为单位收进 + `examples_.cpp`,用例名就是上游的示例名。 +- 文件头注释写明:被测模块、覆盖的上游示例路径、include 与 import 的形态及原因。 + 这沿用现有测试的注释风格。 + +## 4. 方案 + +### 4.1 测试框架:doctest(`compat.doctest`) + +根 `mcpp.toml` 在生成标记之外新增: + +```toml +[dev-dependencies] +"compat.doctest" = "2.4.12" +``` + +每个测试文件是一个独立程序,因此每个文件自己定义 `main`: + +```cpp +// tests/json/api_value.cpp +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.json; + +TEST_CASE("object: emplace / at / contains") { + boost::json::object o{{"a", 1}}; + CHECK(o.contains("a")); + CHECK_EQ(o.at("a").as_int64(), 1); + CHECK_THROWS_AS(o.at("missing"), std::out_of_range); +} +``` + +在 `.tmp/` 的最小工程上实测(mcpp 2026.11.5.1,gcc 16.1.0 与 llvm 22.1.8): + +- `#include `、`import std;` 和对一个具名模块的 import 写在同一个 TU 中, + 两个工具链都能编译通过。include 必须写在所有 import 之前。 +- 失败的用例使程序以 1 退出,`mcpp test` 判为 FAIL;表达式两侧的自定义类型通过 + `operator<<` 打印出值。 +- `mcpp test json/api_value -- -tc="object*"` 可以只跑部分用例。 +- `mcpp build` 不会解析 `[dev-dependencies]`,下游消费者不会引入 doctest。 + +约定: + +- `TEST_CASE` 名称即 `coverage.toml` 里的用例名。示例移植的用例以上游示例名命名,例如 + `TEST_CASE("example: pretty")`;文档片段以 section 命名,例如 `TEST_CASE("doc: tag_invoke")`。 +- 同一文件内按主题用 `TEST_SUITE` 分组,需要共享状态时用 `SUBCASE`,不用全局变量。 +- 自定义类型的值打印用 `operator<<` 或特化 `doctest::StringMaker`,写在 + `custom_types.cpp` 内,这本身也是对该类型可用性的检查。 +- 检查不依赖 `assert`,所以 `--profile release`(`NDEBUG`)下同样生效。 +- 迁入的 `smoke.cpp` 保持原样(`assert` 形态),不改写成 doctest,以免迁移提交混入行为变化。 +- doctest 头文件与 Boost 模块的组合会在 P0 中实测,以真实的重模块(asio、geometry)为准; + 上面的探针只覆盖了 `import std` 和一个小模块。 +- `tests/test/` 是 Boost.Test 自身的测试,不使用 doctest。两个子工程(§5)若需要 doctest, + 各自在 `[dev-dependencies]` 中声明。 + +### 4.2 各类文件的职责 + +| 文件 | 内容 | 必须 | +|---|---|---| +| `smoke.cpp` | 原测试原样迁入,只改 include 路径 | 是 | +| `api_.cpp` | 按官方参考文档的章节补齐公开 API:构造/赋值/比较/迭代/错误路径/异常/`constexpr` 可用性 | 是 | +| `examples[_].cpp` | 官方示例的移植,每个示例一个 `TEST_CASE`,`main()` 改为用例体,标准输出改为断言 | 是 | +| `custom_types.cpp` | 自定义类型套件(§4.3) | 库有定制点时必须 | +| `include_.cpp` | 纯 include 形态,用于宏注册的定制点(§4.4) | 视情况 | + +示例移植的原则:保留上游示例的逻辑与数据,把输出到 `std::cout` 的内容改写为对期望值的 +检查。上游示例自带期望输出的(注释或文档中给出),以它为准;没有给出的,在本地跑一次 +上游原版,取其输出作为期望值,并在用例注释中注明。 + +### 4.3 自定义类型套件 + +每个有定制点的库,`custom_types.cpp` 至少覆盖下表列出的定制点,每个定制点同时给出 +正向用例(自定义类型能用)和负向的编译期判定(未定制的类型被正确拒绝,用 `requires` +表达式或该库自己的 trait 做 `static_assert`,不写编译失败的测试)。 + +| 库 | 定制点(必须覆盖) | +|---|---| +| asio | 自定义 completion token(`async_result` 特化)、自定义 executor、`async_compose` 写的组合操作、自定义 `buffer` 序列类型、`associated_allocator`/`associated_executor` 特化 | +| beast | 自定义 `Body`(满足 `is_body`,带 reader/writer)、自定义 `Fields`、自定义 `DynamicBuffer` | +| bloom | 自定义元素类型配合自定义 hash、自定义 subfilter | +| cobalt | 自定义 awaitable(`await_ready/suspend/resume`)、`promise`/`generator`/`task` 承载用户类型、自定义 executor | +| container | 自定义 allocator(`allocator_traits` 路径)、不可拷贝/仅可移动类型、递归容器(`vector`)、`flat_map` 自定义比较器、`small_vector`/`static_vector` 的 options | +| decimal | 用户类型与 decimal 的混合运算边界(显式转换)、`std::formatter`/流输出、`numeric_limits` | +| geometry | 自定义 point/box/linestring/polygon:手写 `traits::tag/coordinate_type/coordinate_system/dimension/access` 特化;宏注册形态见 §4.4;自定义坐标类型 | +| describe | 结构体/类(含基类、私有成员)、枚举,以及基于 describe 的通用 `==`、`hash_value`、打印、`to_tuple`(全部 include 形态) | +| hash2 | 用户类型的 `tag_invoke(hash_append_tag, …)`、基于 describe 的类型、自定义哈希算法(满足 HashAlgorithm 概念)、自定义 flavor | +| json | `tag_invoke(value_from_tag/value_to_tag, …)`、`is_described_class` 路径(§4.4)、`parse_into` 到用户类型、自定义 `storage_ptr`/memory_resource | +| math | 自定义 policy、以用户实数类型(如包装 `double` 的 `strong_real`)实例化分布与特殊函数、自定义 tools(`newton_raphson_iterate` 用的函子) | +| mqtt5 | 自定义 stream 层(满足 next_layer 要求的内存流),无需真实 broker | +| regex | 自定义 `regex_traits`、自定义字符类型的 `basic_regex`(在上游支持范围内)、自定义迭代器类型上的 `regex_search` | +| test | `boost_test_print_type` 自定义打印、自定义 fixture、数据驱动用例的自定义数据集 | +| unordered | 自定义 hash 与 equal(含透明 `is_transparent`)、自定义 allocator、用户 key 的 `hash_value` 走 `boost::hash`、`concurrent_flat_map` 存放用户类型 | +| url | 自定义 grammar rule(满足 Rule 概念)、自定义 `string_token` | +| uuid | 自定义 UniformRandomNumberGenerator 喂给 `basic_random_generator`、name generator 的命名空间 | +| variant2 | 用户类型(含不可移动、抛异常的构造)作为备选项,`visit` 用户 visitor,`valueless` 语义(variant2 的 never-empty 保证) | +| algorithm | 用户类型配合自定义谓词/比较器(`clamp`、`is_palindrome`、`apply_permutation`、搜索器) | +| any | 用户类型(含大对象与小对象优化边界)、`basic_any` 自定义缓冲区大小、`unique_any` 仅可移动类型 | +| bimap | 用户 key/value 类型、tagged 视图、自定义集合类型(`unordered_set_of` 带自定义 hash) | +| callable_traits | 用户类的成员函数(各种 cv/ref/noexcept 限定)、函数对象、函数指针 | +| charconv | 无用户类型定制点;补齐各进制、格式、错误码(无需 `custom_types.cpp`) | +| circular_buffer | 用户元素类型(非平凡析构、仅可移动)、`space_optimized` 版本 | +| compat | `function_ref`/`move_only_function` 承载用户可调用对象,`bind_front/back`、`invoke` 作用于用户成员指针 | +| container_hash | 用户类型的 `hash_value` 友元、`boost::hash` 特化、基于 describe 的类型(§4.4)、范围类型与 tuple-like 类型的自动支持 | +| convert | 用户类型经流转换器(`operator<<`/`operator>>`)、自定义 converter | +| core | `empty_value` 承载用户类型、`span` 包装用户容器、`allocator_access` 作用于自定义 allocator、`ref`、`noinit_adaptor` | +| crc | 自定义参数的 `crc_optimal<…>`/`crc_basic<…>`(官方示例的形态) | +| dll | 用户类型跨插件边界(导出工厂函数返回用户接口的实现) | +| dynamic_bitset | 自定义 `Block` 类型(`unsigned char`/`unsigned long long`)、自定义 allocator | +| conversion | 用户类层次上的 `polymorphic_cast`/`polymorphic_downcast`,`polymorphic_pointer_cast`/`polymorphic_pointer_downcast` 作用于原始指针、`std::shared_ptr`、`boost::shared_ptr` 和用户自定义智能指针(经 `pointer_cast` 定制点),`implicit_cast` 作用于用户转换构造/转换运算符 | + +### 4.4 include 与 import 混用的处理 + +宏注册的定制点必须 include 头文件,而用户 TU 又要 import 模块。处理顺序如下,每个 +受影响的库在 P0 中实测后落定,结论写进对应文件的头注释: + +1. **混用可行**(clang 与 gcc 都通过):直接在 `custom_types.cpp` 中 include 宏头文件并 + import 模块。 +2. **混用在 gcc 上冲突**:`custom_types.cpp` 改用不依赖宏的等价形态,留在 import 侧, + 例如 geometry 手写 traits 特化(官方文档同时给出了这两种写法),json 用 `tag_invoke`。 + 宏形态单独放进 `include_.cpp`,整个 TU 纯 include。先例是 + `hof_include.cpp`、`units_include.cpp`。 +3. **只有 gcc 守卫可行**(`statechart.cpp` 的模式):这是最后手段,因为 gcc 腿实际没有测到 + 模块。只有在 1、2 都不可行时才采用,并在 `coverage.toml` 里把该用例标为 `gcc-header-only`。 + +describe 本身没有模块,始终走纯 include 形态。它与 json、hash2、container_hash 组合的 +用例,按上述顺序判定。 + +### 4.5 官方示例的收录规则 + +收录的示例:自包含、确定性、秒级完成,不需要外部资源。以下几类排除,排除理由写进 +`coverage.toml`: + +| 排除类别 | 例子 | 替代 | +|---|---|---| +| 访问外部网络 | beast `http/client/sync` 访问真实主机,mqtt5 的 `hello_world_over_tcp` 连 broker | 改写为 127.0.0.1 回环加端口 0 的同进程客户端/服务端对(asio echo、beast http、websocket 均可),超时由用例自己设置 | +| 依赖未 vendor 的库或 SDK | TLS/OpenSSL(beast ssl、mqtt5 tls)、ICU(regex `icu_example`)、MFC、Python(cobalt `python.cpp`)、fmtlib(describe 的 fmtlib 示例)、Qt/wxWidgets(geometry) | 不替代 | +| 长时间运行的服务或交互程序 | asio 的 `chat`/`http` server 主循环、读 stdin 的程序 | 抽取其中的核心组件做用例,例如 http request parser | +| 基准与大数据 | json `parse_into_canada`/`citm_catalog`、math 的精度表生成 | 用小数据替代,或者排除 | +| 依赖不支持的库 | context、coroutine2(§9 Q2,记为 `unsupported`)、asio 的 `spawn`(依赖 context) | 不替代 | +| 平台专属 | asio `windows/*`、`local`(Unix 域套接字)在 Windows 上 | 在用例内按平台跳过,计入 `skip-on` | + +文件系统只能在临时目录下使用。多线程示例(asio thread pool、cobalt thread、unordered +concurrent)允许,但必须带超时,避免 CI 卡死。 + +对 asio、geometry、math、test、beast、decimal 这几个巨大的库,「完整」的判定标准是: +每个示例要么有对应的用例,要么有一条排除记录,不能遗漏不记。数量上不设下限。 + +### 4.6 覆盖清单 `coverage.toml` 与检查 + +```toml +# tests/json/coverage.toml — generated by tools/ci/gen_test_coverage.py from boost_1_92_0.tar.gz, then edited +upstream = "1.92.0" + +[[example]] +path = "libs/json/example/pretty.cpp" +case = "json/examples:example: pretty" # : + +[[example]] +path = "libs/json/example/parse_into_canada.cpp" +status = "excluded" +reason = "benchmark over a 2 MB fixture not vendored" + +[[snippet]] # 文档片段:以 qbk/adoc 中的 [section] 为单位 +doc = "libs/json/doc/qbk/conversion/custom.qbk#tag_invoke" +case = "json/custom_types:doc: tag_invoke" +``` + +- `tools/ci/gen_test_coverage.py `:从发布包列出示例文件与文档片段的 + section,生成骨架(全部标 `todo`),已有条目保留不动。它只在初始化和升级 Boost 时运行, + 归档包放在 `.tmp/`,校验 `SOURCE.md` 记录的 SHA-256。 +- `tools/ci/check_test_coverage.py`:不需要归档包,只检查仓库内的一致性: + `todo` 数为零;`case` 指向的测试文件存在,且文件里有同名的 `TEST_CASE("…")`; + `excluded`/`gap` 必须带 `reason`;有定制点的库(§4.3 的表固化在脚本里)必须有 + `custom_types.cpp`。 +- CI:在 `tests.yml` 中作为一个很轻的步骤加到 `examples` job 之前,或者单独起一个 job + (只依赖 Python,几秒钟即可完成)。 + +文档片段以 section 为粒度,不以代码块为粒度,以免清单过细、无法维护。 + +### 4.7 迁移现有测试 + +- 用 `git mv tests/.cpp tests//smoke.cpp` 迁移,内容只把 + `#include "test_assert.hpp"` 改为 `#include "../test_assert.hpp"`。 + `test_utf.cpp`、`test_included.cpp` 进入 `tests/test/`(§5.2)。 +- `container_hash.cpp` 原本和 `container` 同为前缀匹配,迁移后各在各的目录,不再混淆。 +- 其他文件和文档中引用旧路径的地方(`hof.cpp`、`units.cpp`、`openmethod.cpp`、 + `scope_exit.cpp`、`statechart.cpp` 的「describe.cpp 先例」注释,`docs/usage*.md`) + 一并改为新路径。 +- 测试名改变:`json` 变为 `json/smoke`。CI 没有按测试名过滤,所以不受影响。 + +## 5. 独立测试工程 + +### 5.1 机制 + +根 `mcpp.toml` 在生成标记之外新增: + +```toml +[test] +discover = ["tests/**/*.cpp", "!tests/dll/**", "!tests/test/**"] +``` + +子工程 `tests//mcpp.toml`: + +```toml +[package] +name = "boost-m-tests-dll" +version = "0.0.0" + +[dependencies] +"mcpplibs.mwrap.boost" = { path = "../..", default-features = false, features = ["dll", "filesystem"] } + +[test] +discover = ["*.cpp"] # 源文件仍在 tests// 下(P0 验证相对 glob 的语义) +``` + +CI 在 `Test libraries` 之后加一步,遍历 `tests/*/mcpp.toml`,进入每个目录运行 +`mcpp test --toolchain …`。**不采用**根清单的 `[workspace] members`:根包会被发布和打包 +(`package-source.sh`、`mcpp-index`),在已发布的清单中引入 workspace 的影响没有验证过, +而显式遍历的成本只是 CI 里的几行。 + +子工程只激活自己需要的 feature,避免重复编译整棵 Boost;全局 BMI 缓存 +(`--cache global`)能否在根工程与子工程之间复用,在 P0 中测量。 + +### 5.2 用到独立工程的库 + +| 库 | 原因 | 内容 | +|---|---|---| +| dll | 官方教程 1~9 的核心是构建插件共享库并在运行时加载 | `[targets.] kind = "shared"` 构建插件;测试程序通过 `boost::dll::import_symbol` 等加载;`load_self`/`static_plugin` 不需要插件 | +| test | `unit-test-framework` 的编译形态和 `included` 头文件形态不能链接在同一程序中;根工程测试不激活该 feature | 两个子工程:`tests/test/utf/`(`features = ["unit-test-framework"]`,迁入 `test_utf.cpp`,加上官方示例)、`tests/test/included/`(迁入 `test_included.cpp`),去掉现有的 `MCPP_FEATURE_*` 切换 | + +其余库(含 asio、beast、cobalt、mqtt5)都留在根工程:它们需要的只是 import 与回环 +网络,不需要另外的 feature 或 define。 + +### 5.3 conversion 模块化(本轮新增的封装层改动) + +原因见 §9 Q4。改动如下: + +| 位置 | 改动 | +|---|---| +| `tools/boost_common.py` | `LIBS_T1A` 加入 `conversion`;新增一张显式映射 `EXTRA_TOP_HEADERS = {"conversion": ["implicit_cast.hpp", "polymorphic_cast.hpp", "polymorphic_pointer_cast.hpp"]}`,由 `resolve_headers_heuristic`(头文件集合)、`build_file_to_lib`(归属)与 `_lib_of_include`(依赖边)三处共同读取,三者必须一致,否则排序和归属会彼此矛盾 | +| `generated/meta/libs.json` | `gen_exports.py --scan` 重新生成,新增 `conversion` 一行 | +| `src/conversion.cppm` | `gen_exports.py --emit-cppm` 生成草稿后手工维护,与其他门面同形;GMF 只 include 这三个头文件 | +| `generated/exports/*` | 全量重新生成。先到先得的归属取决于处理顺序,不能只生成 conversion。预期:`conversion.inc` 导出五个函数模板;`range.inc` 去掉 `polymorphic_cast`、`polymorphic_downcast`,`graph.inc` 去掉 `implicit_cast`,两者的 `.deps` 增加到 conversion 的边。`import mcpplibs.mwrap.boost.range;` 的使用者经 `export import` 仍能看到这两个名字,API 不变 | +| `mcpp.toml` | `gen_features.py` 重新生成 `[features.conversion]` 与基础源文件列表 | +| `.github/workflows/tests.yml` | `BOOST_GROUP_A` 加入 `conversion`,否则「Validate feature-group coverage」会拒绝运行 | +| 文档 | `docs/architecture*.md` 与 `docs/usage*.md` 的模块计数(116 → 117)及库清单;`README*` 若列出计数也一并修改 | +| 测试 | `tests/conversion/`(§6);`tests/range/`、`tests/graph/` 之外的既有测试不受影响 | + +不纳入的部分:`boost/cast.hpp` 已被弃用,并且会引入 numeric_conversion。上游自带的 +`boost_conversion.cppm` 也不包含它。 + +验证:`gen_exports.py --check`、`gen_features.py --check`、审计脚本以及 +`tools/ci/verify_generated.py` 都通过;range、graph 的 smoke 测试保持通过。生成器使用 +`.xlings.json` 固定的 llvm 23.1.3,并遵守 docs/architecture §10 的资源预算(单线程 AST 生成)。 + +## 6. 各库计划 + +「示例」一列是 1.92.0 发布包中 `example/` 下的文件数,以及示例所在的文档。 + +| 库 | 示例 | 计划文件(除 `smoke.cpp`、`coverage.toml` 外) | 备注 | +|---|---|---|---| +| asio | 240 | `api_io_context`、`api_timers`、`api_buffers`、`api_strand_executors`、`api_sockets_loopback`、`api_coroutines`(C++20 `co_spawn`/`awaitable`、`deferred`、`parallel_group`、`experimental::awaitable_operators`)、`examples_tutorial`、`examples_echo`、`examples_operations`、`custom_types` | `spawn` 依赖 context,排除;http server 只抽取 request parser | +| beast | 70 | `api_http_message`、`api_http_parser_serializer`、`api_websocket_loopback`、`api_buffers`、`examples_http`、`examples_websocket`、`custom_types` | ssl 全部排除;client 示例改为回环 | +| bloom | 4 | `api_filter`、`examples`、`custom_types` | serialization 示例走 std 流 | +| cobalt | 12 | `api_promise_task_generator`、`api_channel`、`api_race_join`、`examples`、`custom_types` | `python.cpp`、`http.cpp`(ssl)排除;`echo_server` 改回环 | +| container | 20 | `api_vectors`(vector/devector/small/static/stable)、`api_flat_tree`、`api_deque_list_slist`、`api_string`、`api_pmr`、`examples`、`custom_types` | `doc_custom_*` 的 options 示例全部收录 | +| decimal | 46 | `api_arith_cmp`、`api_cmath`、`api_charconv_format`、`api_literals_limits`、`examples`、`custom_types` | 示例中以基准为主的文件排除 | +| geometry | 202 | `api_core_geometries`、`api_algorithms_measure`(area/length/distance/perimeter)、`api_algorithms_relate`(within/intersects/relate)、`api_algorithms_setops`、`api_io_wkt`、`api_rtree`、`api_srs_strategies`、`examples_core`、`examples_algorithms`、`examples_rtree`、`custom_types`、`include_register` | `with_external_libs/*` 排除;`geometries/register` 与 `adapted` 走 §4.4 | +| describe | 16 | `include_members_bases`、`include_enums`、`include_examples`、`include_custom_types`(全部纯 include) | fmtlib 两例排除;json/serialization 示例与 json、serialization 交叉,按 §4.4 | +| hash2 | 11 | `api_algorithms`(fnv/siphash/xxhash/md5/sha1/sha2/ripemd/hmac)、`api_hash_append`、`examples`、`custom_types`、`include_describe` | `md5sum`/`hash2sum` 改为对内存数据计算 | +| json | 12 | `api_value`、`api_parse_serialize`、`api_pointer`、`api_conversion`、`examples`、`custom_types`、`include_describe` | 见 §3 示例 | +| math | 171 | `api_special_functions`、`api_distributions`、`api_tools_roots_minima`、`api_quadrature`、`api_interpolators`、`api_constants`、`api_ccmath`、`examples_*`(按主题 3~5 个)、`custom_types` | 收录规则对 math 最严格:精度表、`dot_net_example` 排除 | +| mqtt5 | 11 | `api_client_config`、`api_properties_codes`、`custom_types`(内存流驱动 connect/publish/subscribe) | 所有 hello_world 均需要 broker,按 `custom_types` 的内存流改写其流程 | +| regex | 22 | `api_match_search_replace`、`api_iterators`、`api_partial`、`api_syntax_flags`、`examples`、`custom_types` | icu、mfc 排除 | +| test | 143 | 子工程,§5.2 | 示例按 suite/fixture/data-driven/decorator/floating-point 分组 | +| unordered | 3 | `api_node_based`、`api_flat`(flat/node map/set)、`api_concurrent`、`api_erase_if_visit`、`examples`、`custom_types` | 示例少,覆盖以参考文档章节为主 | +| url | 26 | `api_parse_views`、`api_modify`、`api_encoding`、`api_grammar`、`api_segments_params`、`examples`、`custom_types` | `router`、`file_router` 收录核心逻辑 | +| uuid | 文档 | `api_generators`(random/name/string/nil/time-based v1/v6/v7)、`api_io_hash`、`examples_doc`、`custom_types` | | +| variant2 | 文档 | `api_variant`、`api_visit`、`examples_doc`、`custom_types` | | +| algorithm | 5 | `api_cxx11_17`(all_of…、`apply_permutation`)、`api_searching`(BM/BMH/KMP)、`api_string_algo`、`api_misc`(clamp/hex/is_palindrome/gather/minmax)、`examples`、`custom_types` | string_algo 是 algorithm 模块的一部分,已有测试已涉及 | +| any | 文档 | `api_any`、`api_basic_unique_any`、`examples_doc`、`custom_types` | | +| bimap | 27 | `api_views`、`api_collection_types`、`examples`、`custom_types` | `bimap_and_boost/*` 跨库示例按库是否支持来判定 | +| callable_traits | 37 | `api_queries`、`api_transforms`、`examples`、`custom_types` | 全部为编译期检查,37 例全收 | +| charconv | 文档 | `api_from_chars`、`api_to_chars`、`examples_doc` | 无定制点 | +| circular_buffer | 5 | `api_buffer`、`api_space_optimized`、`examples`、`custom_types` | `bounded_buffer_comparison` 是基准,只收录正确性部分 | +| compat | 文档 | `api_function_ref_move_only_function`、`api_bind_invoke_misc`、`examples_doc`、`custom_types` | | +| container_hash | 7 | `api_hash_value`、`examples`、`custom_types`、`include_describe` | | +| convert | 7 | `api_converters`(stream/lexical_cast/strtol/spirit/charconv)、`examples`、`custom_types` | `default_converter_fail` 是编译失败示例,排除 | +| core | 文档 | 按 qbk 章节分为 3~4 个 `api_*`、`custom_types` | 约 40 个组件,以参考文档的章节为清单 | +| crc | 1 | `api_crc`、`examples`、`custom_types` | | +| dll | 27 | 子工程,§5.2 | mangled(`import_class`/`smart_library`)是实验性功能,视平台排除 | +| dynamic_bitset | 3 | `api_bitset`、`examples`、`custom_types` | | +| conversion | 文档(`doc/conversion.qbk`)、`libs/conversion/test/*.cpp` | `api_casts`、`examples_doc`、`custom_types` | 先完成 §5.3 的模块化;import 形态 | +| context / coroutine2 | 27 / 5 | 只有 `coverage.toml`,全部记为 `unsupported` | 不支持(Q2) | + +## 7. 成本与风险 + +| 项 | 风险 | 对策 | +|---|---|---| +| CI 时长 | 34 个库由 34 个程序变为约 170~200 个,每个都在 10 条腿上编译;asio/beast/geometry/math 的 TU 很重 | P0 测量单个重 TU 的编译时间与内存,按结果控制每个库的文件数(宁可单个文件大一些);超出预算时,深度测试只在一组中运行(§9 Q3) | +| gcc 的 include 与 import 混用 | 自定义类型套件可能被迫降级 | §4.4 的三级处理,P0 先行 | +| 导出缺口 | 新 API 测试暴露模块漏导出的实体 | 记录为 `gap`,另开修复,不在测试里绕开 | +| 回环网络 | CI 宿主(尤其 Windows/macOS)的防火墙与端口 | 只绑定 127.0.0.1 的端口 0,每个用例都设超时;不稳定的用例以 `skip-on` 记录 | +| 示例期望值 | 上游示例只打印结果,不做断言 | 期望值来源写在用例注释里(§4.2) | +| 浮点结果(math、geometry、decimal) | 跨平台的 ulp 差异 | 一律用容差比较,不做精确比较 | + +## 8. 实施顺序 + +| 阶段 | 内容 | 退出条件 | +|---|---|---| +| P0 验证 | ① doctest 头文件加重模块(asio、geometry)在 gcc 16 / clang 22 上的组合;gcc 16 / clang 22:json+describe、geometry 注册宏、hash2+describe、container_hash+describe 的 include 与 import 混用;② 子工程 `[test] discover = ["*.cpp"]` 的相对 glob 语义;③ `kind = "shared"` 插件加 dll 加载;④ 单个重 TU(asio/geometry)的编译时间与峰值内存;⑤ 子工程与根工程的 BMI 缓存复用 | 结论写进一篇 `-analysis` 记录 | +| P1 基础 | conversion 模块化(§5.3);根清单的 `[dev-dependencies]`(doctest)与 `[test] discover`;迁移 34 个 smoke;`gen/check_test_coverage.py` 与全部 `coverage.toml` 骨架 | `--check` 类检查全部通过,根 `mcpp test` 全绿,测试名为新形式 | +| P2 小型库 | algorithm any bimap callable_traits charconv circular_buffer compat container_hash convert core crc dynamic_bitset uuid variant2 bloom describe conversion | 这些库的 `coverage.toml` 中 `todo` 为零 | +| P3 中型库 | container decimal hash2 json regex unordered url | 同上 | +| P4 重型库 | asio beast cobalt mqtt5 geometry math | 同上,CI 时长在预算内 | +| P5 子工程 | dll、test | 子工程在 CI 中全绿 | +| P6 收尾 | CI 接入覆盖检查;更新 `docs/architecture*.md` 的 `tests/` 描述 | 本记录标为 `landed` | + +每个阶段一个或几个 PR,按库拆分,便于 review。 + +## 9. 问题与答复 + +已确认(2026-11-09,用户答复): + +- **Q1** `bitmap` 是笔误,指 `bimap`。 +- **Q2** `context`、`coroutine2` 只在 `coverage.toml` 中记为 `unsupported`,不在本次接入。 +- **Q3** CI 时长先在 P0 测量再决定。若超出预算,深度测试只在一组中运行,具体机制 + (例如由 CI 传入 `discover` 的排除规则或环境变量)在 P0 的 analysis 记录中给出。 + 预算内则保持现状,两组都跑全部测试。 +- **Q5** 接受 §4.5 的覆盖规则:每个示例要么映射到一个用例,要么记录排除理由,不设数量下限; + 网络示例改为回环。 +- **Q4** `conversion` 没有模块。调查结论(2026-11-09):这不是技术限制,而是生成器的 + 头文件启发式漏掉了它。 + - `tools/boost_common.py` 的 `resolve_headers_heuristic` 只收 `boost//**.hpp`(排除 + `detail/`)和 `boost/.hpp`。conversion 的公开头文件是顶层的 + `boost/implicit_cast.hpp`、`boost/polymorphic_cast.hpp`、`boost/polymorphic_pointer_cast.hpp`, + `boost/conversion/` 下只有 `detail/config.hpp`,所以得到空集,被 + [2026-09-08 汇总](2026-09-08-consolidated-design.md) §2.2 归为「无头 stub」。 + - 这几个头文件没有面向用户的宏,只有函数模板。上游 1.92 自带 + `libs/conversion/modules/boost_conversion.cppm`,导出的正是这三个头文件;上游的模块开关 + 只在定义 `BOOST_USE_MODULES` 时生效,封装层不定义它,因此不冲突。 + - `build_file_to_lib` 也按路径前缀判定归属,这三个文件因此被记为 `shared`,由先到先得的 + 规则归给了别的库:`range.inc` 导出了 `polymorphic_cast`、`polymorphic_downcast`, + `graph.inc` 导出了 `implicit_cast`;`polymorphic_pointer_cast`、 + `polymorphic_pointer_downcast` 没有任何模块导出。 + - 在 `upstream/boost/libs/` 中,除了刻意排除的库之外,被这条启发式漏掉的只有 conversion + 一个(describe、openmethod、scope_exit 是 C1 的有意降级)。 + - 修复方向:在 `libs.json` 登记 conversion 的三个头文件,加入 `LIBS_T1A`,并让 + `build_file_to_lib` / `_lib_of_include` 认得这几个顶层文件(一个显式的「库 → 额外顶层 + 头文件」映射)。之后重新生成:range、graph 不再导出这三个名字,改为通过 `.deps` + 中的 `export import …conversion` 边提供,使用者看到的 API 不变。`boost/cast.hpp` + 已弃用且引入 numeric_conversion,按上游模块的做法不纳入。这是封装层改动,需要走 + `gen_exports.py`、`gen_features.py`、CI 分组和文档计数的完整流程,应另写一篇记录; + 做完之后 conversion 的测试改为 import 形态。 + - **答复**:本轮一起做,方案见 §5.3。 + +- **Q6**(用户追加)测试框架:不自写,直接用 mcpp index 收录的 doctest,见 §4.1。 + +## 10. 否决的方案 + +- **每个官方示例一个测试文件。** 编译开销与 BMI 加载次数乘以示例数,callable_traits + 一个库就要 37 个程序,而 CI 要在 10 条腿上各编译一次。 +- **自写一个测试框架(原方案中的 `tests/common/mtest.hpp`)。** 用户否决:mcpp index + 已收录 doctest,它是单头文件,没有需要编译的 main 库,能直接满足多用例、失败不中断、 + 值打印、用例过滤的需求,自写只会多一份需要维护的代码。 +- **用 Boost.Test 作为测试框架。** 它有两种互斥的形态,必须二选一并通过 feature 控制。 + 每个 TU 都要 include Boost 头文件,又要 import 模块,正好落入 gcc 的 ODR 冲突区, + 而被测的 Boost.Test 本身也成了所有测试的前提。 +- **每个库都建子工程。** 每个子工程都要重新解析依赖并编译它的 feature 闭包。根工程的 + 基础源文件列表已经覆盖了所有库,子工程只在根工程无法表达时才有必要。 +- **把官方示例目录 vendor 进仓库。** 这会违背 `upstream/` 只放裁剪后上游树的约定, + 并使仓库增大;`coverage.toml` 只记路径,归档按需下载并校验。 diff --git a/.agents/docs/README.md b/.agents/docs/README.md index 21cb4876..cc1613fe 100644 --- a/.agents/docs/README.md +++ b/.agents/docs/README.md @@ -27,7 +27,11 @@ - `todos/` 放尚未完成的工作项,它们不是决定的记录。 - 移动或改名用 `tools/doc-tools/move_record.py`,它同时改写仓库中对该文件的引用。 -共 21 篇记录(其中 0 篇已归档),0 项待办。 +共 22 篇记录(其中 0 篇已归档),0 项待办。 + +## 进行中 + +- [模块封装层测试加固:常用 34 个库的示例覆盖与自定义类型套件](2026-11-09-module-test-hardening-design.md) — active ## 按主题 @@ -37,10 +41,15 @@ - [设计记录的管理:只增不删、按年归档、生成的索引与 `tools/doc-tools/`](2026-11-08-design-records-management-design.md) — landed +### tests + +- [模块封装层测试加固:常用 34 个库的示例覆盖与自定义类型套件](2026-11-09-module-test-hardening-design.md) — active + ## 按日期 ### 2026-11 +- [模块封装层测试加固:常用 34 个库的示例覆盖与自定义类型套件](2026-11-09-module-test-hardening-design.md) — active - [设计记录的管理:只增不删、按年归档、生成的索引与 `tools/doc-tools/`](2026-11-08-design-records-management-design.md) — landed - [封装层生成原理改造:clang JSON AST + 声明式策略 + `--check`](2026-11-05-json-ast-generation.md) From 1a110857bc8c15f6233de0a6e1f78c4610f39b89 Mon Sep 17 00:00:00 2001 From: Zhe Feng Date: Mon, 9 Nov 2026 13:30:11 +0000 Subject: [PATCH 2/9] Modularize Boost.Conversion; export boost::core::string_view; fix the transform_reduce include guard The prefix heuristic saw no headers for conversion (its headers sit at the top of boost/), so the library had no module and its casts were exported by range and graph. EXTRA_HEADERS gives it its three headers, ownership and include edges; pointer_cast.hpp is smart_ptr's so conversion does not claim smart_ptr's names. range, graph and statechart now import the conversion module instead of re-exporting the casts. core's header set adds boost/core/detail/string_view.hpp, the only header of the documented boost::core::string_view; no module exported the alias before (json exported only basic_string_view). patches/algorithm.patch: transform_inclusive_scan.hpp reused the include guard of transform_reduce.hpp, so transform_reduce was missing from the algorithm module and from any TU that includes both headers. mcpp.toml also gains [dev-dependencies] compat.doctest and the [test] discover globs of the test hardening. --- .github/workflows/tests.yml | 12 +++-- README.md | 4 +- README.zh.hant.md | 4 +- README.zh.md | 4 +- docs/architecture.md | 4 +- docs/architecture.zh.md | 4 +- docs/usage.md | 2 +- docs/usage.zh.md | 2 +- generated/exports/algorithm.inc | 3 +- generated/exports/conversion.deps | 3 ++ generated/exports/conversion.inc | 14 ++++++ generated/exports/core.inc | 15 +++++- generated/exports/graph.deps | 1 + generated/exports/graph.inc | 4 +- generated/exports/json.inc | 6 +-- generated/exports/range.deps | 1 + generated/exports/range.inc | 4 +- generated/exports/statechart.deps | 2 +- generated/meta/api-surface.json | 28 ++++++++--- generated/meta/features.lst | 1 + generated/meta/libs.json | 6 +++ generated/meta/modules.lst | 1 + mcpp.toml | 25 ++++++++-- patches/README.md | 7 +++ patches/algorithm.patch | 26 ++++++++++ src/conversion.cppm | 14 ++++++ src/core.cppm | 1 + src/graph.cppm | 1 + src/range.cppm | 1 + src/statechart.cppm | 1 + tools/apply_patches.py | 2 +- tools/boost_common.py | 50 +++++++++++++++++-- .../cxx17/transform_inclusive_scan.hpp | 11 ++-- 33 files changed, 215 insertions(+), 49 deletions(-) create mode 100644 generated/exports/conversion.deps create mode 100644 generated/exports/conversion.inc create mode 100644 patches/algorithm.patch create mode 100644 src/conversion.cppm diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1a5088a1..30c82305 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,9 +13,10 @@ # Every host is built twice, once per feature group, because a single clang # aggregate closure of the whole package can exhaust the source-location space: # -# A — the 105 library features of the original group A: 103 module-bearing -# libraries plus the two compiled include-only features (`log`, -# `unit-test-framework`); +# A — the 105 library features of the original group A (103 module-bearing +# libraries plus the two compiled include-only features `log` and +# `unit-test-framework`), plus `conversion` (six function templates, +# added by the module test hardening); # B — the twelve heavy libraries (asio, beast, geometry, ...) plus the two # wrapper-only modules the mwrap migration added (`version`, `macros`), # so group A's aggregate closure stays at the module count whose limit was @@ -89,10 +90,11 @@ env: # Leave the runners room: the vendored tree is large and every leg compiles # Boost from the same checkout. MCPP_JOBS: '4' - # M12 grouping: group A is the original wrapper's 105-feature set; group B is + # M12 grouping: group A is the original wrapper's 105-feature set plus + # `conversion`; group B is # its twelve heavy libraries plus the two wrapper-only modules added by the # mwrap migration. Both are kebab-case features declared in mcpp.toml. - BOOST_GROUP_A: "optional,variant,variant2,any,core,container-hash,mp11,static-string,scope,type-traits,algorithm,iterator,range,io,rational,endian,tuple,system,filesystem,regex,thread,chrono,program-options,stacktrace,json,url,align,array,assert,assign,bimap,bind,bloom,callable-traits,circular-buffer,compat,concept-check,config,convert,crc,decimal,dll,dynamic-bitset,flyweight,format,function,functional,hash2,heap,histogram,hof,icl,integer,intrusive,lambda,lambda2,leaf,lexical-cast,lockfree,logic,move,multi-array,multi-index,outcome,parser,pfr,poly-collection,pool,property-map,property-tree,ptr-container,ratio,safe-numerics,signals2,smart-ptr,sort,statechart,stl-interfaces,throw-exception,tokenizer,type-index,units,unordered,utility,uuid,winapi,yap,atomic,charconv,cobalt,container,contract,date-time,graph,iostreams,log,math,nowide,process,random,serialization,unit-test-framework,timer,type-erasure,wave" + BOOST_GROUP_A: "optional,variant,variant2,any,core,container-hash,mp11,static-string,scope,type-traits,algorithm,iterator,range,io,rational,endian,tuple,system,filesystem,regex,thread,chrono,program-options,stacktrace,json,url,align,array,assert,assign,bimap,bind,bloom,callable-traits,circular-buffer,compat,concept-check,config,conversion,convert,crc,decimal,dll,dynamic-bitset,flyweight,format,function,functional,hash2,heap,histogram,hof,icl,integer,intrusive,lambda,lambda2,leaf,lexical-cast,lockfree,logic,move,multi-array,multi-index,outcome,parser,pfr,poly-collection,pool,property-map,property-tree,ptr-container,ratio,safe-numerics,signals2,smart-ptr,sort,statechart,stl-interfaces,throw-exception,tokenizer,type-index,units,unordered,utility,uuid,winapi,yap,atomic,charconv,cobalt,container,contract,date-time,graph,iostreams,log,math,nowide,process,random,serialization,unit-test-framework,timer,type-erasure,wave" BOOST_GROUP_B: "accumulators,asio,beast,geometry,gil,hana,interprocess,mqtt5,multiprecision,numeric,polygon,qvm,version,macros" jobs: diff --git a/README.md b/README.md index b8dd1eef..fb9f52ef 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![mcpp-community](https://img.shields.io/badge/mcpp--community-mwrap-blue)](https://github.com/mcpp-community) [![tests](https://github.com/mcpplibs/boost-m/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mcpplibs/boost-m/actions/workflows/tests.yml) -116 Boost libraries are reachable as named modules: the wrapper runs +117 Boost libraries are reachable as named modules: the wrapper runs `#include` inside a module's global module fragment and re-exports the upstream entities with `export using`, so types, overloads, ADL and spelling stay Boost's. The header-only, compiled and macro-only libraries keep their own @@ -60,7 +60,7 @@ grouping spelling (`type-traits` enables `mcpplibs.mwrap.boost.type_traits`). | Feature | Purpose | |---|---| | `default` | The 50-library baseline closure (header-only core + filesystem/regex/thread/chrono/json/serialization) | -| `all-modules` | Every module interface (117 entries); `log` and `unit-test-framework` stay explicit | +| `all-modules` | Every module interface (118 entries); `log` and `unit-test-framework` stay explicit | | `macros` | The mapped macro constants module; `version` re-exports it | | `log`, `unit-test-framework` | Compiled libraries with no module: include the upstream headers | | `backend-log-avx2`, `backend-log-ssse3` | Build-time Boost.Log dump implementation (x86_64 only, mutually exclusive) | diff --git a/README.zh.hant.md b/README.zh.hant.md index a2403476..999dfe46 100644 --- a/README.zh.hant.md +++ b/README.zh.hant.md @@ -9,7 +9,7 @@ [![mcpp-community](https://img.shields.io/badge/mcpp--community-mwrap-blue)](https://github.com/mcpp-community) [![tests](https://github.com/mcpplibs/boost-m/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mcpplibs/boost-m/actions/workflows/tests.yml) -116 個 Boost 庫可以按具名模組使用:封裝在模組的全域模組片段中 `#include` 上游標頭檔, +117 個 Boost 庫可以按具名模組使用:封裝在模組的全域模組片段中 `#include` 上游標頭檔, 再用 `export using` 重新匯出上游實體,因此型別、多載、ADL 與拼寫都仍是 Boost 的。 純標頭庫、編譯庫與純巨集庫各自保留既有消費方式;程式裡只有 vendored Boost 這一實作, 不會重複連結第二份。 @@ -58,7 +58,7 @@ feature 名一律小寫 kebab-case;模組後綴保留 Boost 自己的分組拼 | Feature | 作用 | |---|---| | `default` | 50 庫基線閉包(純標頭核心 + filesystem/regex/thread/chrono/json/serialization) | -| `all-modules` | 全部模組介面(117 個);`log`、`unit-test-framework` 仍需明確啟用 | +| `all-modules` | 全部模組介面(118 個);`log`、`unit-test-framework` 仍需明確啟用 | | `macros` | 巨集常數映射模組;`version` 會重新匯出它 | | `log`、`unit-test-framework` | 無模組的編譯庫:直接 include 上游標頭檔 | | `backend-log-avx2`、`backend-log-ssse3` | 建置期 Boost.Log dump 實作(僅 x86_64,同軸互斥) | diff --git a/README.zh.md b/README.zh.md index 8e7c3695..31c30ab7 100644 --- a/README.zh.md +++ b/README.zh.md @@ -9,7 +9,7 @@ [![mcpp-community](https://img.shields.io/badge/mcpp--community-mwrap-blue)](https://github.com/mcpp-community) [![tests](https://github.com/mcpplibs/boost-m/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mcpplibs/boost-m/actions/workflows/tests.yml) -116 个 Boost 库可以按命名模块使用:封装在模块的全局模块片段中 `#include` 上游头文件, +117 个 Boost 库可以按命名模块使用:封装在模块的全局模块片段中 `#include` 上游头文件, 再用 `export using` 重新导出上游实体,因此类型、重载、ADL 和拼写都仍是 Boost 的。 纯头库、编译库与纯宏库各自保留既有消费方式;程序里只有 vendored Boost 这一份实现, 不会重复链接第二份。 @@ -58,7 +58,7 @@ feature 名一律小写 kebab-case;模块后缀保留 Boost 自己的分组拼 | Feature | 作用 | |---|---| | `default` | 50 库基线闭包(纯头核心 + filesystem/regex/thread/chrono/json/serialization) | -| `all-modules` | 全部模块接口(117 个);`log`、`unit-test-framework` 仍需显式启用 | +| `all-modules` | 全部模块接口(118 个);`log`、`unit-test-framework` 仍需显式启用 | | `macros` | 宏常量映射模块;`version` 会重新导出它 | | `log`、`unit-test-framework` | 无模块的编译库:直接 include 上游头文件 | | `backend-log-avx2`、`backend-log-ssse3` | 构建期 Boost.Log dump 实现(仅 x86_64,同轴互斥) | diff --git a/docs/architecture.md b/docs/architecture.md index 0708c992..536eb0d8 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -10,7 +10,7 @@ C++23 module layer makes them importable while preserving the upstream API. The wrapper does not re-implement Boost, does not relocate its entities into a new namespace, and does not link a second Boost implementation. -- 116 library module interfaces + the root module + the macro mapping module. +- 117 library module interfaces + the root module + the macro mapping module. - 22 pure include-only libraries and 2 compiled include-only features (`log`, `unit-test-framework`): consumed by header, listed in [usage.md](usage.md). - 11 libraries need external toolchains/SDKs and are not declared. @@ -220,7 +220,7 @@ generator change, not as an unexplained API change. byte- and mtime-idempotent. Feature names are kebab-case; `implies` lists feature names (never internal keys), derived from each interface's `export import` lines plus the generated body edges. `modules.lst` is the - feature → module map `build.mcpp` reads; `all-modules` expands to the 117 module + feature → module map `build.mcpp` reads; `all-modules` expands to the 118 module entries. - `gen_audit.py` reports internal-linkage functions, anonymous-namespace entities and explicit specializations per library from the same AST; its diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index ac05aa3d..99bb7193 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -9,7 +9,7 @@ 模块让其可被 import,同时保留上游 API。包装层不重新实现 Boost,不把它的实体 搬进新的命名空间,也不链接第二份 Boost 实现。 -- 116 个库模块接口 + 根模块 + 宏映射模块。 +- 117 个库模块接口 + 根模块 + 宏映射模块。 - 22 个纯 include-only 库,以及 2 个需要编译的 include-only feature(`log`、 `unit-test-framework`):按头文件方式消费,列于 [usage.zh.md](usage.zh.md)。 - 11 个库需要外部工具链/SDK,未做声明。 @@ -195,7 +195,7 @@ clang++ -std=c++23 --target=x86_64-w64-mingw32 --sysroot= \ mtime 上都是幂等的。feature 名使用 kebab-case;`implies` 列出的是 feature 名(绝不是内部键),由各接口的 `export import` 行加上生成的函数体边推导而来。 `modules.lst` 是 `build.mcpp` 读取的 feature → 模块映射;`all-modules` 展开为 - 117 个模块条目。 + 118 个模块条目。 - `gen_audit.py` 从同一份 AST 报告每个库的内部链接函数、匿名命名空间实体和显式 特化;其报告要结合已提交的代码树审阅,不能当成“不属于公共 API”。 diff --git a/docs/usage.md b/docs/usage.md index d0bbc60f..e097ee8e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -161,7 +161,7 @@ spelling, so feature `type-traits` enables `mcpplibs.mwrap.boost.type_traits`. | Feature | Effect | |---|---| | `default` | 50-library closure (see below) | -| `all-modules` | All 117 module entries (116 library interfaces + `macros`); not `log`, not `unit-test-framework` | +| `all-modules` | All 118 module entries (117 library interfaces + `macros`); not `log`, not `unit-test-framework` | | `macros`, `version` | The mapping module and its thin re-export | | `backend-log-avx2`, `backend-log-ssse3` | Build-time Boost.Log dump implementation, x86_64 only, one per axis | diff --git a/docs/usage.zh.md b/docs/usage.zh.md index 06464f11..f27c8910 100644 --- a/docs/usage.zh.md +++ b/docs/usage.zh.md @@ -128,7 +128,7 @@ static_assert(mcpplibs::mwrap::boost::macros::BOOST_VERSION == raw_version); | 特征 | 作用 | |---|---| | `default` | 50 个库构成的闭包(见下文) | -| `all-modules` | 全部 117 个模块条目(116 个库接口 + `macros`);不含 `log`,不含 `unit-test-framework` | +| `all-modules` | 全部 118 个模块条目(117 个库接口 + `macros`);不含 `log`,不含 `unit-test-framework` | | `macros`、`version` | 映射模块及其薄重导出 | | `backend-log-avx2`、`backend-log-ssse3` | 构建期的 Boost.Log dump 实现,仅限 x86_64,每条轴各择其一 | diff --git a/generated/exports/algorithm.inc b/generated/exports/algorithm.inc index ce91e99e..235cb52d 100644 --- a/generated/exports/algorithm.inc +++ b/generated/exports/algorithm.inc @@ -1,5 +1,5 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=algorithm boost=1.92.0 target=x86_64-w64-mingw32 entities=477 +// lib=algorithm boost=1.92.0 target=x86_64-w64-mingw32 entities=478 export namespace boost { using boost::all; @@ -361,6 +361,7 @@ export namespace boost { namespace algorithm { using boost::algorithm::token_finder; using boost::algorithm::transform_exclusive_scan; using boost::algorithm::transform_inclusive_scan; + using boost::algorithm::transform_reduce; using boost::algorithm::trim; using boost::algorithm::trim_all; using boost::algorithm::trim_all_copy; diff --git a/generated/exports/conversion.deps b/generated/exports/conversion.deps new file mode 100644 index 00000000..364af3ad --- /dev/null +++ b/generated/exports/conversion.deps @@ -0,0 +1,3 @@ +mcpplibs.mwrap.boost.assert +mcpplibs.mwrap.boost.smart_ptr +mcpplibs.mwrap.boost.throw_exception diff --git a/generated/exports/conversion.inc b/generated/exports/conversion.inc new file mode 100644 index 00000000..d3988eb6 --- /dev/null +++ b/generated/exports/conversion.inc @@ -0,0 +1,14 @@ +// GENERATED by tools/gen_exports.py - DO NOT EDIT +// lib=conversion boost=1.92.0 target=x86_64-w64-mingw32 entities=6 + +export namespace boost { + using boost::implicit_cast; + using boost::polymorphic_cast; + using boost::polymorphic_downcast; + using boost::polymorphic_pointer_cast; + using boost::polymorphic_pointer_downcast; +} + +export namespace boost { namespace detail { + using boost::detail::icast_identity; +}} diff --git a/generated/exports/core.inc b/generated/exports/core.inc index c5fb9e25..60b99123 100644 --- a/generated/exports/core.inc +++ b/generated/exports/core.inc @@ -1,5 +1,5 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=core boost=1.92.0 target=x86_64-w64-mingw32 entities=270 +// lib=core boost=1.92.0 target=x86_64-w64-mingw32 entities=283 export namespace boost { using boost::addressof; @@ -42,6 +42,7 @@ export namespace boost { using boost::allocator_void_pointer_t; using boost::as_bytes; using boost::as_writable_bytes; + using boost::basic_string_view; using boost::checked_array_delete; using boost::checked_array_deleter; using boost::checked_delete; @@ -103,6 +104,7 @@ export namespace boost { namespace checked_deleters { }} export namespace boost { namespace core { + using boost::core::basic_string_view; using boost::core::bit_cast; using boost::core::bit_ceil; using boost::core::bit_floor; @@ -129,6 +131,7 @@ export namespace boost { namespace core { using boost::core::max_align_t; using boost::core::memory_resource; using boost::core::operator!=; + using boost::core::operator<<; using boost::core::operator==; using boost::core::popcount; using boost::core::rotl; @@ -141,13 +144,18 @@ export namespace boost { namespace core { using boost::core::sp_thread_yield; using boost::core::split_free; using boost::core::split_member; + using boost::core::string_view; using boost::core::swprintf; using boost::core::type_name; using boost::core::typeinfo; + using boost::core::u16string_view; + using boost::core::u32string_view; + using boost::core::u8string_view; using boost::core::uncaught_exceptions; using boost::core::verbose_terminate_handler; using boost::core::vsnprintf; using boost::core::vswprintf; + using boost::core::wstring_view; using std::fpclassify; using std::isfinite; using std::isinf; @@ -170,6 +178,10 @@ export namespace boost { namespace core { namespace detail { #endif using boost::core::detail::countl_impl; using boost::core::detail::countr_impl; + using boost::core::detail::find_first_not_of; + using boost::core::detail::find_first_of; + using boost::core::detail::find_last_not_of; + using boost::core::detail::find_last_of; using boost::core::detail::fix_typeid_name; using boost::core::detail::function_type_name; using boost::core::detail::is_same; @@ -191,6 +203,7 @@ export namespace boost { namespace core { namespace detail { using boost::core::sp_thread_sleep; using boost::core::sp_thread_yield; #endif + using boost::core::detail::sv_to_uchar; using boost::core::detail::tn_add_each; using boost::core::detail::tn_add_each_impl; using boost::core::detail::tn_holder; diff --git a/generated/exports/graph.deps b/generated/exports/graph.deps index 4226bf27..7ed600c2 100644 --- a/generated/exports/graph.deps +++ b/generated/exports/graph.deps @@ -6,6 +6,7 @@ mcpplibs.mwrap.boost.bimap mcpplibs.mwrap.boost.concept_check mcpplibs.mwrap.boost.config mcpplibs.mwrap.boost.container_hash +mcpplibs.mwrap.boost.conversion mcpplibs.mwrap.boost.core mcpplibs.mwrap.boost.function mcpplibs.mwrap.boost.integer diff --git a/generated/exports/graph.inc b/generated/exports/graph.inc index 70791db0..6c8733fc 100644 --- a/generated/exports/graph.inc +++ b/generated/exports/graph.inc @@ -1,5 +1,5 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=graph boost=1.92.0 target=x86_64-w64-mingw32 entities=3072 +// lib=graph boost=1.92.0 target=x86_64-w64-mingw32 entities=3070 export namespace boost { using boost::AStarHeuristicConcept; @@ -486,7 +486,6 @@ export namespace boost { using boost::hawick_unique_circuits; using boost::heart_topology; using boost::hypercube_topology; - using boost::implicit_cast; using boost::in_degree; using boost::in_edge_list; using boost::in_edge_set; @@ -1396,7 +1395,6 @@ export namespace boost { namespace detail { using boost::detail::has_vertex_iterator; using boost::detail::has_vertex_property_type; using boost::detail::has_vertices_size_type; - using boost::detail::icast_identity; using boost::detail::inL_edge_status; using boost::detail::in_edge_iter; using boost::detail::in_edge_predicate; diff --git a/generated/exports/json.inc b/generated/exports/json.inc index 88084674..7559f046 100644 --- a/generated/exports/json.inc +++ b/generated/exports/json.inc @@ -1,14 +1,10 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=json boost=1.92.0 target=x86_64-w64-mingw32 entities=306 +// lib=json boost=1.92.0 target=x86_64-w64-mingw32 entities=305 export namespace boost { namespace container_hash { using boost::container_hash::is_unordered_range; }} -export namespace boost { namespace core { - using boost::core::basic_string_view; -}} - export namespace boost { namespace describe { using boost::describe::describe_enumerators; using boost::describe::enum_from_string; diff --git a/generated/exports/range.deps b/generated/exports/range.deps index f3cdfcb6..f1757d93 100644 --- a/generated/exports/range.deps +++ b/generated/exports/range.deps @@ -2,6 +2,7 @@ mcpplibs.mwrap.boost.array mcpplibs.mwrap.boost.assert mcpplibs.mwrap.boost.concept_check mcpplibs.mwrap.boost.config +mcpplibs.mwrap.boost.conversion mcpplibs.mwrap.boost.core mcpplibs.mwrap.boost.functional mcpplibs.mwrap.boost.iterator diff --git a/generated/exports/range.inc b/generated/exports/range.inc index 4b4eb9ca..7fed0945 100644 --- a/generated/exports/range.inc +++ b/generated/exports/range.inc @@ -1,5 +1,5 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=range boost=1.92.0 target=x86_64-w64-mingw32 entities=482 +// lib=range boost=1.92.0 target=x86_64-w64-mingw32 entities=480 export namespace boost { using boost::BidirectionalRangeConcept; @@ -92,8 +92,6 @@ export namespace boost { using boost::partial_sort_copy; using boost::partial_sum; using boost::partition; - using boost::polymorphic_cast; - using boost::polymorphic_downcast; using boost::pop_heap; using boost::prev_permutation; using boost::prior; diff --git a/generated/exports/statechart.deps b/generated/exports/statechart.deps index 25a83967..d69bd6a9 100644 --- a/generated/exports/statechart.deps +++ b/generated/exports/statechart.deps @@ -1,8 +1,8 @@ mcpplibs.mwrap.boost.assert mcpplibs.mwrap.boost.config +mcpplibs.mwrap.boost.conversion mcpplibs.mwrap.boost.core mcpplibs.mwrap.boost.function mcpplibs.mwrap.boost.smart_ptr mcpplibs.mwrap.boost.thread -mcpplibs.mwrap.boost.throw_exception mcpplibs.mwrap.boost.type_traits diff --git a/generated/meta/api-surface.json b/generated/meta/api-surface.json index 5e076526..17991c74 100644 --- a/generated/meta/api-surface.json +++ b/generated/meta/api-surface.json @@ -47,7 +47,7 @@ "tuple", "type_traits" ], - "exported": 477, + "exported": 478, "gmf": [ "boost/algorithm/algorithm.hpp", "boost/algorithm/apply_permutation.hpp", @@ -509,6 +509,19 @@ "boost/contract.hpp" ] }, + "conversion": { + "deps": [ + "assert", + "smart_ptr", + "throw_exception" + ], + "exported": 6, + "gmf": [ + "boost/implicit_cast.hpp", + "boost/polymorphic_cast.hpp", + "boost/polymorphic_pointer_cast.hpp" + ] + }, "convert": { "deps": [ "assert", @@ -527,7 +540,7 @@ "config", "throw_exception" ], - "exported": 270, + "exported": 283, "gmf": [ "boost/core/alloc_construct.hpp", "boost/core/allocator_traits.hpp", @@ -535,6 +548,7 @@ "boost/core/checked_delete.hpp", "boost/core/cmath.hpp", "boost/core/default_allocator.hpp", + "boost/core/detail/string_view.hpp", "boost/core/empty_value.hpp", "boost/core/exchange.hpp", "boost/core/explicit_operator_bool.hpp", @@ -898,6 +912,7 @@ "concept_check", "config", "container_hash", + "conversion", "core", "function", "integer", @@ -923,7 +938,7 @@ "utility", "version" ], - "exported": 3072, + "exported": 3070, "gmf": [ "boost/graph/adj_list_serialize.hpp", "boost/graph/adjacency_list_io.hpp", @@ -1323,7 +1338,7 @@ "system", "variant2" ], - "exported": 306, + "exported": 305, "gmf": [ "boost/json/debug_printers.hpp", "boost/json.hpp" @@ -1986,6 +2001,7 @@ "assert", "concept_check", "config", + "conversion", "core", "functional", "iterator", @@ -1994,7 +2010,7 @@ "tuple", "type_traits" ], - "exported": 482, + "exported": 480, "gmf": [ "boost/range.hpp", "boost/range/adaptor/define_adaptor.hpp", @@ -2268,11 +2284,11 @@ "deps": [ "assert", "config", + "conversion", "core", "function", "smart_ptr", "thread", - "throw_exception", "type_traits" ], "exported": 73, diff --git a/generated/meta/features.lst b/generated/meta/features.lst index 81c8d1fc..8d417aa7 100644 --- a/generated/meta/features.lst +++ b/generated/meta/features.lst @@ -36,6 +36,7 @@ circular-buffer compat concept-check config +conversion convert crc decimal diff --git a/generated/meta/libs.json b/generated/meta/libs.json index 2f4de732..5a4500b4 100644 --- a/generated/meta/libs.json +++ b/generated/meta/libs.json @@ -955,6 +955,11 @@ "boost/contract/override.hpp", "boost/contract/public_function.hpp" ], + "conversion": [ + "boost/implicit_cast.hpp", + "boost/polymorphic_cast.hpp", + "boost/polymorphic_pointer_cast.hpp" + ], "convert": [ "boost/convert.hpp" ], @@ -970,6 +975,7 @@ "boost/core/data.hpp", "boost/core/default_allocator.hpp", "boost/core/demangle.hpp", + "boost/core/detail/string_view.hpp", "boost/core/empty_value.hpp", "boost/core/enable_if.hpp", "boost/core/exchange.hpp", diff --git a/generated/meta/modules.lst b/generated/meta/modules.lst index 6dc52dc6..92fbffd1 100644 --- a/generated/meta/modules.lst +++ b/generated/meta/modules.lst @@ -36,6 +36,7 @@ circular-buffer circular_buffer compat compat concept-check concept_check config config +conversion conversion convert convert crc crc decimal decimal diff --git a/mcpp.toml b/mcpp.toml index 9c7a27c0..7981abc8 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -91,6 +91,7 @@ sources = [ "src/compat.cppm", "src/concept_check.cppm", "src/config.cppm", + "src/conversion.cppm", "src/convert.cppm", "src/crc.cppm", "src/decimal.cppm", @@ -306,8 +307,8 @@ cxxflags = ["-Wno-invalid-specialization"] # -- generated by tools/gen_features.py - do not edit -- [features] -default = ["optional", "variant", "variant2", "any", "core", "container-hash", "mp11", "type-traits", "algorithm", "iterator", "range", "io", "endian", "tuple", "system", "filesystem", "regex", "thread", "chrono", "json", "array", "assert", "bind", "compat", "concept-check", "config", "dynamic-bitset", "function", "functional", "integer", "intrusive", "lambda", "lexical-cast", "move", "multi-array", "ratio", "smart-ptr", "throw-exception", "tokenizer", "type-index", "units", "utility", "winapi", "atomic", "container", "date-time", "math", "random", "serialization", "numeric", "version", "macros"] -all-modules = { implies = ["optional", "variant", "variant2", "any", "core", "container-hash", "mp11", "static-string", "scope", "type-traits", "algorithm", "iterator", "range", "io", "rational", "endian", "tuple", "system", "filesystem", "regex", "thread", "chrono", "program-options", "stacktrace", "json", "url", "align", "array", "assert", "assign", "bimap", "bind", "bloom", "callable-traits", "circular-buffer", "compat", "concept-check", "config", "convert", "crc", "decimal", "dll", "dynamic-bitset", "flyweight", "format", "function", "functional", "hash2", "heap", "histogram", "hof", "icl", "integer", "intrusive", "lambda", "lambda2", "leaf", "lexical-cast", "lockfree", "logic", "move", "multi-array", "multi-index", "outcome", "parser", "pfr", "poly-collection", "pool", "property-map", "property-tree", "ptr-container", "ratio", "safe-numerics", "signals2", "smart-ptr", "sort", "statechart", "stl-interfaces", "throw-exception", "tokenizer", "type-index", "units", "unordered", "utility", "uuid", "winapi", "yap", "atomic", "charconv", "cobalt", "container", "contract", "date-time", "graph", "iostreams", "math", "nowide", "process", "random", "serialization", "timer", "type-erasure", "wave", "accumulators", "asio", "beast", "geometry", "gil", "hana", "interprocess", "mqtt5", "multiprecision", "numeric", "polygon", "qvm", "version", "macros"] } +default = ["optional", "variant", "variant2", "any", "core", "container-hash", "mp11", "type-traits", "algorithm", "iterator", "range", "io", "endian", "tuple", "system", "filesystem", "regex", "thread", "chrono", "json", "array", "assert", "bind", "compat", "concept-check", "config", "conversion", "dynamic-bitset", "function", "functional", "integer", "intrusive", "lambda", "lexical-cast", "move", "multi-array", "ratio", "smart-ptr", "throw-exception", "tokenizer", "type-index", "units", "utility", "winapi", "atomic", "container", "date-time", "math", "random", "serialization", "numeric", "version", "macros"] +all-modules = { implies = ["optional", "variant", "variant2", "any", "core", "container-hash", "mp11", "static-string", "scope", "type-traits", "algorithm", "iterator", "range", "io", "rational", "endian", "tuple", "system", "filesystem", "regex", "thread", "chrono", "program-options", "stacktrace", "json", "url", "align", "array", "assert", "assign", "bimap", "bind", "bloom", "callable-traits", "circular-buffer", "compat", "concept-check", "config", "conversion", "convert", "crc", "decimal", "dll", "dynamic-bitset", "flyweight", "format", "function", "functional", "hash2", "heap", "histogram", "hof", "icl", "integer", "intrusive", "lambda", "lambda2", "leaf", "lexical-cast", "lockfree", "logic", "move", "multi-array", "multi-index", "outcome", "parser", "pfr", "poly-collection", "pool", "property-map", "property-tree", "ptr-container", "ratio", "safe-numerics", "signals2", "smart-ptr", "sort", "statechart", "stl-interfaces", "throw-exception", "tokenizer", "type-index", "units", "unordered", "utility", "uuid", "winapi", "yap", "atomic", "charconv", "cobalt", "container", "contract", "date-time", "graph", "iostreams", "math", "nowide", "process", "random", "serialization", "timer", "type-erasure", "wave", "accumulators", "asio", "beast", "geometry", "gil", "hana", "interprocess", "mqtt5", "multiprecision", "numeric", "polygon", "qvm", "version", "macros"] } [features.optional] sources = ["src/optional.cppm"] implies = ["assert", "config", "core", "type-traits"] @@ -345,7 +346,7 @@ all-modules = { implies = ["optional", "variant", "variant2", "any", "core", "co implies = ["assert", "concept-check", "config", "core", "mp11", "optional", "tuple", "type-traits", "utility"] [features.range] sources = ["src/range.cppm"] - implies = ["array", "assert", "concept-check", "config", "core", "functional", "iterator", "optional", "regex", "tuple", "type-traits"] + implies = ["array", "assert", "concept-check", "config", "conversion", "core", "functional", "iterator", "optional", "regex", "tuple", "type-traits"] [features.io] sources = ["src/io.cppm"] implies = ["config"] @@ -418,6 +419,9 @@ all-modules = { implies = ["optional", "variant", "variant2", "any", "core", "co implies = ["config", "type-traits"] [features.config] sources = ["src/config.cppm"] +[features.conversion] + sources = ["src/conversion.cppm"] + implies = ["assert", "smart-ptr", "throw-exception"] [features.convert] sources = ["src/convert.cppm"] implies = ["assert", "core", "optional", "type-traits"] @@ -527,7 +531,7 @@ all-modules = { implies = ["optional", "variant", "variant2", "any", "core", "co implies = ["core", "range", "type-traits"] [features.statechart] sources = ["src/statechart.cppm"] - implies = ["assert", "config", "core", "function", "smart-ptr", "thread", "throw-exception", "type-traits"] + implies = ["assert", "config", "conversion", "core", "function", "smart-ptr", "thread", "throw-exception", "type-traits"] [features.stl-interfaces] sources = ["src/stl_interfaces.cppm"] implies = ["assert", "config"] @@ -578,7 +582,7 @@ all-modules = { implies = ["optional", "variant", "variant2", "any", "core", "co implies = ["algorithm", "assert", "core", "io", "iterator", "numeric", "smart-ptr", "throw-exception", "tokenizer", "type-traits"] [features.graph] sources = ["src/graph.cppm", "upstream/boost/libs/graph/src/*.cpp"] - implies = ["algorithm", "any", "array", "assert", "bimap", "concept-check", "config", "container-hash", "core", "function", "integer", "iterator", "lexical-cast", "math", "move", "mp11", "multi-index", "multiprecision", "numeric", "optional", "property-map", "property-tree", "random", "range", "serialization", "smart-ptr", "throw-exception", "tuple", "type-traits", "unordered", "utility", "version"] + implies = ["algorithm", "any", "array", "assert", "bimap", "concept-check", "config", "container-hash", "conversion", "core", "function", "integer", "iterator", "lexical-cast", "math", "move", "mp11", "multi-index", "multiprecision", "numeric", "optional", "property-map", "property-tree", "random", "range", "serialization", "smart-ptr", "throw-exception", "tuple", "type-traits", "unordered", "utility", "version"] [features.iostreams] sources = ["src/iostreams.cppm", "upstream/boost/libs/iostreams/src/file_descriptor.cpp", "upstream/boost/libs/iostreams/src/mapped_file.cpp"] implies = ["assert", "concept-check", "config", "core", "iterator", "random", "range", "smart-ptr", "throw-exception", "type-traits", "utility"] @@ -728,3 +732,14 @@ path = "src/boost.cppm" [targets.boost] kind = "lib" + +# Test-only: `mcpp test` resolves it, `mcpp build` and consumers never see it. +# The test programs under tests// are doctest programs, one per file. +[dev-dependencies] +"compat.doctest" = "2.4.12" + +# tests/dll/ and tests/test/ are projects of their own (shared-library plugins; +# the two exclusive Boost.Test forms); CI runs `mcpp test` inside each of them. +# A nested mcpp.toml does not hide a directory from this glob, hence the `!`. +[test] +discover = ["tests/**/*.cpp", "!tests/dll/**", "!tests/test/**"] diff --git a/patches/README.md b/patches/README.md index 16976d97..4189cafa 100644 --- a/patches/README.md +++ b/patches/README.md @@ -27,6 +27,13 @@ numeric module needs the swap. The exported `same_impl_ex` follows the same header condition through the `numeric` guard in `tools/export_policy.toml`, so the release module neither exports a name the header hides nor fails on it. +`algorithm.patch` fixes an upstream bug rather than adapting to modules: +`boost/algorithm/cxx17/transform_inclusive_scan.hpp` reuses the include guard +of `transform_reduce.hpp`, so a translation unit that includes both loses the +second one. The algorithm module's GMF includes them in that order, and +`boost::algorithm::transform_reduce` was missing from its export list; plain +`#include` consumers hit the same bug. The patch gives the header its own guard. + Every patch is reviewed against the 1.92.0 tag it was written for; `upstream/boost/SOURCE.md` records the archive and checksum the tree comes from. A patch that neither applies nor is already applied fails the replay. diff --git a/patches/algorithm.patch b/patches/algorithm.patch new file mode 100644 index 00000000..a19ebeaa --- /dev/null +++ b/patches/algorithm.patch @@ -0,0 +1,26 @@ +diff --git a/upstream/boost/boost/algorithm/cxx17/transform_inclusive_scan.hpp b/upstream/boost/boost/algorithm/cxx17/transform_inclusive_scan.hpp +index 9d877c02..2e198c8e 100644 +--- a/upstream/boost/boost/algorithm/cxx17/transform_inclusive_scan.hpp ++++ b/upstream/boost/boost/algorithm/cxx17/transform_inclusive_scan.hpp +@@ -9,8 +9,13 @@ + /// \brief Combine the (transformed) elements of a sequence (or two) into a single value. + /// \author Marshall Clow + +-#ifndef BOOST_ALGORITHM_TRANSFORM_REDUCE_HPP +-#define BOOST_ALGORITHM_TRANSFORM_REDUCE_HPP ++// boost-m vendored edit: upstream reuses transform_reduce.hpp's include guard ++// here, so whichever of the two headers comes second is skipped and its ++// algorithm silently vanishes from any TU that includes both (the module GMF ++// lists them alphabetically, so boost::algorithm::transform_reduce was ++// never exported). Replay after re-running import_boost. ++#ifndef BOOST_ALGORITHM_TRANSFORM_INCLUSIVE_SCAN_HPP ++#define BOOST_ALGORITHM_TRANSFORM_INCLUSIVE_SCAN_HPP + + #include // for std::plus + #include // for std::iterator_traits +@@ -86,4 +91,4 @@ OutputIterator transform_inclusive_scan(InputIterator first, InputIterator last, + + }} // namespace boost and algorithm + +-#endif // BOOST_ALGORITHM_TRANSFORM_REDUCE_HPP ++#endif // BOOST_ALGORITHM_TRANSFORM_INCLUSIVE_SCAN_HPP diff --git a/src/conversion.cppm b/src/conversion.cppm new file mode 100644 index 00000000..2806a3b5 --- /dev/null +++ b/src/conversion.cppm @@ -0,0 +1,14 @@ +// mcpplibs.mwrap.boost.conversion — module facade over generated/exports/conversion.inc +// (hand-maintained; tools/gen_exports.py --check verifies its GMF include set) +module; +#include +#include +#include + +export module mcpplibs.mwrap.boost.conversion; + +export import mcpplibs.mwrap.boost.assert; +export import mcpplibs.mwrap.boost.smart_ptr; +export import mcpplibs.mwrap.boost.throw_exception; + +#include "../generated/exports/conversion.inc" diff --git a/src/core.cppm b/src/core.cppm index 597a93dc..d2531b55 100644 --- a/src/core.cppm +++ b/src/core.cppm @@ -18,6 +18,7 @@ module; #include #include #include +#include #include #include #include diff --git a/src/graph.cppm b/src/graph.cppm index 4869e270..6affb181 100644 --- a/src/graph.cppm +++ b/src/graph.cppm @@ -101,6 +101,7 @@ export import mcpplibs.mwrap.boost.assert; export import mcpplibs.mwrap.boost.bimap; export import mcpplibs.mwrap.boost.concept_check; export import mcpplibs.mwrap.boost.config; +export import mcpplibs.mwrap.boost.conversion; export import mcpplibs.mwrap.boost.container_hash; export import mcpplibs.mwrap.boost.core; export import mcpplibs.mwrap.boost.function; diff --git a/src/range.cppm b/src/range.cppm index 593dba62..c1d12c35 100644 --- a/src/range.cppm +++ b/src/range.cppm @@ -28,6 +28,7 @@ export import mcpplibs.mwrap.boost.array; export import mcpplibs.mwrap.boost.assert; export import mcpplibs.mwrap.boost.concept_check; export import mcpplibs.mwrap.boost.config; +export import mcpplibs.mwrap.boost.conversion; export import mcpplibs.mwrap.boost.core; export import mcpplibs.mwrap.boost.functional; export import mcpplibs.mwrap.boost.iterator; diff --git a/src/statechart.cppm b/src/statechart.cppm index 73c564ae..32fb50a2 100644 --- a/src/statechart.cppm +++ b/src/statechart.cppm @@ -15,6 +15,7 @@ export module mcpplibs.mwrap.boost.statechart; export import mcpplibs.mwrap.boost.assert; export import mcpplibs.mwrap.boost.config; +export import mcpplibs.mwrap.boost.conversion; export import mcpplibs.mwrap.boost.core; export import mcpplibs.mwrap.boost.function; export import mcpplibs.mwrap.boost.smart_ptr; diff --git a/tools/apply_patches.py b/tools/apply_patches.py index d48faa20..484e3977 100644 --- a/tools/apply_patches.py +++ b/tools/apply_patches.py @@ -28,7 +28,7 @@ # One entry per patches/.patch; see patches/README.md for each reason. PATCHES = [ - "accumulators", "asio", "beast", "config_platform", "config_user", "hof", + "accumulators", "algorithm", "asio", "beast", "config_platform", "config_user", "hof", "io", "lambda", "mqtt5", "multi_array", "parameter", "range_adaptors", "numeric", "regex", "safe_numerics", "serialization", "system", "test", "ublas", "units", diff --git a/tools/boost_common.py b/tools/boost_common.py index ce63a72f..b4f56204 100644 --- a/tools/boost_common.py +++ b/tools/boost_common.py @@ -92,10 +92,15 @@ def _boost_version(): # global `using namespace boost::placeholders;` cannot be reproduced by a # module — consumers of the module face write their own using-directive # (the upstream-recommended spelling anyway). +# Conversion (2026-11-09, .agents/docs/2026-11-09-module-test-hardening-design.md +# §5.3): its public headers sit at the top of boost/ (implicit_cast.hpp, +# polymorphic_cast.hpp, polymorphic_pointer_cast.hpp) and boost/conversion/ +# holds only detail/, so the prefix heuristic saw an empty library and the +# 2026-09-08 inventory filed it as a "no-header stub". See EXTRA_HEADERS. LIBS_T1A = [ "align", "array", "assert", "assign", "bimap", "bind", "bloom", "callable_traits", "circular_buffer", "compat", "concept_check", - "config", "convert", "crc", "decimal", + "config", "conversion", "convert", "crc", "decimal", "dll", "dynamic_bitset", "flyweight", "format", "function", "functional", "hash2", "heap", "histogram", "hof", "icl", "integer", "intrusive", "lambda", "lambda2", "leaf", "lexical_cast", "lockfree", @@ -174,6 +179,37 @@ def _boost_version(): LIBS_SPECIAL = ["version", "macros"] TARGET_LIBS = LIBS_M3 + LIBS_M4 + LIBS_T1A + LIBS_T2 + LIBS_T1B + LIBS_SPECIAL +# Public headers the prefix rules (boost//** minus detail/, boost/.hpp) +# do not see. Paths are relative to boost/. +# +# EXTRA_HEADERS: added to the library's public header set (its GMF) and owned +# by it. Header resolution, file ownership and include-edge detection all +# read it, so the three cannot disagree. +# - conversion: its headers sit at the top of boost/ under other names. +# - core: boost::core::string_view is a documented Core component +# (libs/core/doc/string_view.qbk) whose only header is +# boost/core/detail/string_view.hpp; without it no module exported the +# string_view alias, which charconv's from_chars(string_view) overloads +# take (only json exported basic_string_view, reached through its own GMF). +# TOP_LEVEL_OWNERS: ownership (and include edges) only, for top-level files; +# the file stays out of the owner's header set, so the owner's GMF and +# export list do not change. boost/pointer_cast.hpp is smart_ptr's +# (upstream: Boost::conversion links Boost::smart_ptr for it); without the +# entry conversion, which reaches it first in TARGET_LIBS order, would claim +# smart_ptr's *_pointer_cast names. +EXTRA_HEADERS = { + "conversion": ["implicit_cast.hpp", "polymorphic_cast.hpp", + "polymorphic_pointer_cast.hpp"], + "core": ["core/detail/string_view.hpp"], +} +TOP_LEVEL_OWNERS = { + "pointer_cast.hpp": "smart_ptr", +} +for _lib, _names in EXTRA_HEADERS.items(): + for _name in _names: + if "/" not in _name: + TOP_LEVEL_OWNERS[_name] = _lib + # M9 downgrades to include-only (plan §2 note + 2026-08-17 M9 doc §1): pure # macro libs (predef: .h only; static_assert: module name is a keyword) and # internal-linkage constexpr-object APIs (hof, units) — same consumer rule @@ -321,6 +357,8 @@ def resolve_headers_heuristic(lib: str): single = BOOST_ROOT / (lib + ".hpp") if single.is_file(): headers.add(single) + headers.update(BOOST_ROOT / name for name in EXTRA_HEADERS.get(lib, ()) + if (BOOST_ROOT / name).is_file()) def key(p: Path): rel = p.relative_to(BOOST_ROOT).as_posix() @@ -482,7 +520,8 @@ def build_file_to_lib(): Prefix-based: every file under boost// (including detail/ subtrees, which are enumerated but not GMF-included) plus the boost/.hpp - aggregate belongs to the library.""" + aggregate belongs to the library; TOP_LEVEL_OWNERS adds the top-level + files whose name is not a library's.""" mapping = {} for lib in TARGET_LIBS: d = BOOST_ROOT / lib @@ -492,6 +531,8 @@ def build_file_to_lib(): single = BOOST_ROOT / (lib + ".hpp") if single.is_file(): mapping[str(single.resolve()).lower()] = lib + for name, lib in TOP_LEVEL_OWNERS.items(): + mapping[str((BOOST_ROOT / name).resolve()).lower()] = lib return mapping @@ -514,8 +555,11 @@ def _lib_of_include(rel: str): """boost/... -> target lib x ('' when not a target lib root). Handles both directory roots (boost//...) and single-header - aggregates (boost/.hpp, e.g. boost/tokenizer.hpp / boost/crc.hpp).""" + aggregates (boost/.hpp, e.g. boost/tokenizer.hpp / boost/crc.hpp), + and the TOP_LEVEL_OWNERS files (boost/polymorphic_cast.hpp -> conversion).""" top = rel[len("boost/"):].split("/")[0] + if top in TOP_LEVEL_OWNERS: + return TOP_LEVEL_OWNERS[top] if "." in top: # single-header aggregate include (boost/.hpp) stem = top.rsplit(".", 1)[0] diff --git a/upstream/boost/boost/algorithm/cxx17/transform_inclusive_scan.hpp b/upstream/boost/boost/algorithm/cxx17/transform_inclusive_scan.hpp index 9d877c02..2e198c8e 100644 --- a/upstream/boost/boost/algorithm/cxx17/transform_inclusive_scan.hpp +++ b/upstream/boost/boost/algorithm/cxx17/transform_inclusive_scan.hpp @@ -9,8 +9,13 @@ /// \brief Combine the (transformed) elements of a sequence (or two) into a single value. /// \author Marshall Clow -#ifndef BOOST_ALGORITHM_TRANSFORM_REDUCE_HPP -#define BOOST_ALGORITHM_TRANSFORM_REDUCE_HPP +// boost-m vendored edit: upstream reuses transform_reduce.hpp's include guard +// here, so whichever of the two headers comes second is skipped and its +// algorithm silently vanishes from any TU that includes both (the module GMF +// lists them alphabetically, so boost::algorithm::transform_reduce was +// never exported). Replay after re-running import_boost. +#ifndef BOOST_ALGORITHM_TRANSFORM_INCLUSIVE_SCAN_HPP +#define BOOST_ALGORITHM_TRANSFORM_INCLUSIVE_SCAN_HPP #include // for std::plus #include // for std::iterator_traits @@ -86,4 +91,4 @@ OutputIterator transform_inclusive_scan(InputIterator first, InputIterator last, }} // namespace boost and algorithm -#endif // BOOST_ALGORITHM_TRANSFORM_REDUCE_HPP +#endif // BOOST_ALGORITHM_TRANSFORM_INCLUSIVE_SCAN_HPP From 2a982ae96bacdbacd05f8593710bd0bb6c126faa Mon Sep 17 00:00:00 2001 From: Zhe Feng Date: Mon, 9 Nov 2026 13:42:30 +0000 Subject: [PATCH 3/9] tests: move the smoke tests of 34 libraries under tests//, add doctest suites Each library of the hardening gets tests// (smoke.cpp is the former tests/.cpp); examples*.cpp port the official examples, api*.cpp the documented components, custom_types.cpp the user-type suite, and include_*.cpp the macro API as plain #include TUs. coverage.toml maps every example and documentation snippet of the 1.92.0 release to a test case or a reason (tools/ci/test_coverage.py scan/link/check). First libraries: algorithm, any, callable_traits, charconv, circular_buffer, compat, conversion, core, crc, dynamic_bitset, variant2. tests/dll/ and tests/test/utf/ are test projects of their own (plugins; the compiled Boost.Test form), excluded from the root [test] discover. --- mcpp.toml | 10 +- tests/algorithm/api_sequences.cpp | 296 +++ tests/algorithm/api_string.cpp | 116 + tests/algorithm/coverage.toml | 171 ++ tests/algorithm/custom_types.cpp | 132 ++ tests/algorithm/examples.cpp | 357 +++ tests/algorithm/include_string_regex.cpp | 36 + tests/{algorithm.cpp => algorithm/smoke.cpp} | 2 +- tests/any/api.cpp | 91 + tests/any/coverage.toml | 16 + tests/any/custom_types.cpp | 102 + tests/any/examples.cpp | 103 + tests/{any.cpp => any/smoke.cpp} | 2 +- tests/asio/coverage.toml | 1042 +++++++++ tests/{asio.cpp => asio/smoke.cpp} | 2 +- tests/beast/coverage.toml | 318 +++ tests/{beast.cpp => beast/smoke.cpp} | 2 +- tests/bimap/coverage.toml | 179 ++ tests/{bimap.cpp => bimap/smoke.cpp} | 2 +- tests/bloom/coverage.toml | 22 + tests/{bloom.cpp => bloom/smoke.cpp} | 2 +- tests/callable_traits/coverage.toml | 174 ++ tests/callable_traits/custom_types.cpp | 116 + tests/callable_traits/examples.cpp | 952 ++++++++ .../smoke.cpp} | 2 +- tests/charconv/api.cpp | 236 ++ tests/charconv/coverage.toml | 102 + tests/{charconv.cpp => charconv/smoke.cpp} | 2 +- tests/circular_buffer/api.cpp | 131 ++ tests/circular_buffer/coverage.toml | 26 + tests/circular_buffer/custom_types.cpp | 99 + tests/circular_buffer/examples.cpp | 215 ++ .../smoke.cpp} | 2 +- tests/cobalt/coverage.toml | 224 ++ tests/{cobalt.cpp => cobalt/smoke.cpp} | 2 +- tests/compat/api.cpp | 525 +++++ tests/compat/coverage.toml | 381 ++++ tests/compat/custom_types.cpp | 114 + tests/{compat.cpp => compat/smoke.cpp} | 2 +- tests/container/coverage.toml | 136 ++ tests/{container.cpp => container/smoke.cpp} | 2 +- tests/container_hash/coverage.toml | 36 + .../smoke.cpp} | 2 +- tests/context/coverage.toml | 213 ++ tests/conversion/api.cpp | 92 + tests/conversion/coverage.toml | 16 + tests/conversion/custom_types.cpp | 95 + tests/conversion/examples.cpp | 49 + tests/convert/coverage.toml | 64 + tests/{convert.cpp => convert/smoke.cpp} | 2 +- tests/core/api.cpp | 553 +++++ tests/core/coverage.toml | 341 +++ tests/core/custom_types.cpp | 166 ++ tests/core/include_macros.cpp | 199 ++ tests/{core.cpp => core/smoke.cpp} | 2 +- tests/coroutine2/coverage.toml | 97 + tests/crc/api.cpp | 85 + tests/crc/coverage.toml | 39 + tests/crc/custom_types.cpp | 75 + tests/crc/examples.cpp | 100 + tests/{crc.cpp => crc/smoke.cpp} | 2 +- tests/decimal/coverage.toml | 832 +++++++ tests/{decimal.cpp => decimal/smoke.cpp} | 2 +- tests/describe/coverage.toml | 175 ++ tests/{describe.cpp => describe/smoke.cpp} | 2 +- tests/dll/coverage.toml | 125 ++ tests/dll/mcpp.toml | 18 + tests/{dll.cpp => dll/smoke.cpp} | 2 +- tests/dynamic_bitset/api.cpp | 148 ++ tests/dynamic_bitset/coverage.toml | 18 + tests/dynamic_bitset/custom_types.cpp | 79 + tests/dynamic_bitset/examples.cpp | 55 + .../smoke.cpp} | 2 +- tests/geometry/coverage.toml | 1392 ++++++++++++ tests/{geometry.cpp => geometry/smoke.cpp} | 2 +- tests/hash2/coverage.toml | 145 ++ tests/{hash2.cpp => hash2/smoke.cpp} | 2 +- tests/hof.cpp | 2 +- tests/json/coverage.toml | 212 ++ tests/{json.cpp => json/smoke.cpp} | 2 +- tests/math/coverage.toml | 1910 +++++++++++++++++ tests/{math.cpp => math/smoke.cpp} | 2 +- tests/mqtt5/coverage.toml | 85 + tests/{mqtt5.cpp => mqtt5/smoke.cpp} | 2 +- tests/openmethod.cpp | 2 +- tests/regex/coverage.toml | 219 ++ tests/{regex.cpp => regex/smoke.cpp} | 2 +- tests/scope_exit.cpp | 2 +- tests/statechart.cpp | 2 +- tests/test/coverage.toml | 1019 +++++++++ .../included/smoke.cpp} | 4 +- tests/test/utf/mcpp.toml | 17 + tests/test/utf/smoke.cpp | 30 + tests/test_utf.cpp | 44 - tests/units.cpp | 2 +- tests/unordered/coverage.toml | 75 + tests/{unordered.cpp => unordered/smoke.cpp} | 2 +- tests/url/coverage.toml | 221 ++ tests/{url.cpp => url/smoke.cpp} | 2 +- tests/uuid/coverage.toml | 221 ++ tests/{uuid.cpp => uuid/smoke.cpp} | 2 +- tests/variant2/api.cpp | 95 + tests/variant2/coverage.toml | 31 + tests/variant2/custom_types.cpp | 144 ++ tests/variant2/examples.cpp | 145 ++ tests/{variant2.cpp => variant2/smoke.cpp} | 2 +- tools/boost_common.py | 9 +- tools/ci/test_coverage.py | 473 ++++ tools/gen_features.py | 6 +- 109 files changed, 16567 insertions(+), 92 deletions(-) create mode 100644 tests/algorithm/api_sequences.cpp create mode 100644 tests/algorithm/api_string.cpp create mode 100644 tests/algorithm/coverage.toml create mode 100644 tests/algorithm/custom_types.cpp create mode 100644 tests/algorithm/examples.cpp create mode 100644 tests/algorithm/include_string_regex.cpp rename tests/{algorithm.cpp => algorithm/smoke.cpp} (98%) create mode 100644 tests/any/api.cpp create mode 100644 tests/any/coverage.toml create mode 100644 tests/any/custom_types.cpp create mode 100644 tests/any/examples.cpp rename tests/{any.cpp => any/smoke.cpp} (96%) create mode 100644 tests/asio/coverage.toml rename tests/{asio.cpp => asio/smoke.cpp} (95%) create mode 100644 tests/beast/coverage.toml rename tests/{beast.cpp => beast/smoke.cpp} (96%) create mode 100644 tests/bimap/coverage.toml rename tests/{bimap.cpp => bimap/smoke.cpp} (95%) create mode 100644 tests/bloom/coverage.toml rename tests/{bloom.cpp => bloom/smoke.cpp} (93%) create mode 100644 tests/callable_traits/coverage.toml create mode 100644 tests/callable_traits/custom_types.cpp create mode 100644 tests/callable_traits/examples.cpp rename tests/{callable_traits.cpp => callable_traits/smoke.cpp} (97%) create mode 100644 tests/charconv/api.cpp create mode 100644 tests/charconv/coverage.toml rename tests/{charconv.cpp => charconv/smoke.cpp} (96%) create mode 100644 tests/circular_buffer/api.cpp create mode 100644 tests/circular_buffer/coverage.toml create mode 100644 tests/circular_buffer/custom_types.cpp create mode 100644 tests/circular_buffer/examples.cpp rename tests/{circular_buffer.cpp => circular_buffer/smoke.cpp} (95%) create mode 100644 tests/cobalt/coverage.toml rename tests/{cobalt.cpp => cobalt/smoke.cpp} (96%) create mode 100644 tests/compat/api.cpp create mode 100644 tests/compat/coverage.toml create mode 100644 tests/compat/custom_types.cpp rename tests/{compat.cpp => compat/smoke.cpp} (95%) create mode 100644 tests/container/coverage.toml rename tests/{container.cpp => container/smoke.cpp} (97%) create mode 100644 tests/container_hash/coverage.toml rename tests/{container_hash.cpp => container_hash/smoke.cpp} (95%) create mode 100644 tests/context/coverage.toml create mode 100644 tests/conversion/api.cpp create mode 100644 tests/conversion/coverage.toml create mode 100644 tests/conversion/custom_types.cpp create mode 100644 tests/conversion/examples.cpp create mode 100644 tests/convert/coverage.toml rename tests/{convert.cpp => convert/smoke.cpp} (96%) create mode 100644 tests/core/api.cpp create mode 100644 tests/core/coverage.toml create mode 100644 tests/core/custom_types.cpp create mode 100644 tests/core/include_macros.cpp rename tests/{core.cpp => core/smoke.cpp} (95%) create mode 100644 tests/coroutine2/coverage.toml create mode 100644 tests/crc/api.cpp create mode 100644 tests/crc/coverage.toml create mode 100644 tests/crc/custom_types.cpp create mode 100644 tests/crc/examples.cpp rename tests/{crc.cpp => crc/smoke.cpp} (95%) create mode 100644 tests/decimal/coverage.toml rename tests/{decimal.cpp => decimal/smoke.cpp} (96%) create mode 100644 tests/describe/coverage.toml rename tests/{describe.cpp => describe/smoke.cpp} (97%) create mode 100644 tests/dll/coverage.toml create mode 100644 tests/dll/mcpp.toml rename tests/{dll.cpp => dll/smoke.cpp} (91%) create mode 100644 tests/dynamic_bitset/api.cpp create mode 100644 tests/dynamic_bitset/coverage.toml create mode 100644 tests/dynamic_bitset/custom_types.cpp create mode 100644 tests/dynamic_bitset/examples.cpp rename tests/{dynamic_bitset.cpp => dynamic_bitset/smoke.cpp} (95%) create mode 100644 tests/geometry/coverage.toml rename tests/{geometry.cpp => geometry/smoke.cpp} (96%) create mode 100644 tests/hash2/coverage.toml rename tests/{hash2.cpp => hash2/smoke.cpp} (97%) create mode 100644 tests/json/coverage.toml rename tests/{json.cpp => json/smoke.cpp} (97%) create mode 100644 tests/math/coverage.toml rename tests/{math.cpp => math/smoke.cpp} (95%) create mode 100644 tests/mqtt5/coverage.toml rename tests/{mqtt5.cpp => mqtt5/smoke.cpp} (98%) create mode 100644 tests/regex/coverage.toml rename tests/{regex.cpp => regex/smoke.cpp} (97%) create mode 100644 tests/test/coverage.toml rename tests/{test_included.cpp => test/included/smoke.cpp} (88%) create mode 100644 tests/test/utf/mcpp.toml create mode 100644 tests/test/utf/smoke.cpp delete mode 100644 tests/test_utf.cpp create mode 100644 tests/unordered/coverage.toml rename tests/{unordered.cpp => unordered/smoke.cpp} (96%) create mode 100644 tests/url/coverage.toml rename tests/{url.cpp => url/smoke.cpp} (98%) create mode 100644 tests/uuid/coverage.toml rename tests/{uuid.cpp => uuid/smoke.cpp} (95%) create mode 100644 tests/variant2/api.cpp create mode 100644 tests/variant2/coverage.toml create mode 100644 tests/variant2/custom_types.cpp create mode 100644 tests/variant2/examples.cpp rename tests/{variant2.cpp => variant2/smoke.cpp} (97%) create mode 100644 tools/ci/test_coverage.py diff --git a/mcpp.toml b/mcpp.toml index 7981abc8..52204ab6 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -738,8 +738,10 @@ kind = "lib" [dev-dependencies] "compat.doctest" = "2.4.12" -# tests/dll/ and tests/test/ are projects of their own (shared-library plugins; -# the two exclusive Boost.Test forms); CI runs `mcpp test` inside each of them. -# A nested mcpp.toml does not hide a directory from this glob, hence the `!`. +# tests/dll/ (shared-library plugins) and tests/test/utf/ (the compiled +# Boost.Test form, which needs the unit-test-framework feature on) are projects +# of their own; CI runs `mcpp test` inside each. The header-only Boost.Test form +# (tests/test/included/) stays here. A nested mcpp.toml does not hide a +# directory from this glob, hence the `!`. [test] -discover = ["tests/**/*.cpp", "!tests/dll/**", "!tests/test/**"] +discover = ["tests/**/*.cpp", "!tests/dll/**", "!tests/test/utf/**"] diff --git a/tests/algorithm/api_sequences.cpp b/tests/algorithm/api_sequences.cpp new file mode 100644 index 00000000..65e3c21f --- /dev/null +++ b/tests/algorithm/api_sequences.cpp @@ -0,0 +1,296 @@ +// mcpplibs.mwrap.boost.algorithm — the sequence algorithms (cxx11/14/17, +// searching, hex, gather, clamp, ordering, permutation) through the module. +// Cases named "doc:
" carry the Examples of libs/algorithm/doc/.qbk. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.algorithm; + +namespace ba = boost::algorithm; + +namespace { +bool isOdd(int i) { return i % 2 == 1; } +bool lessThan10(int i) { return i < 10; } +const std::vector c{0, 1, 2, 3, 14, 15}; +} // namespace + +TEST_CASE("doc: all_of") { + CHECK_FALSE(ba::all_of(c, isOdd)); + CHECK_FALSE(ba::all_of(c.begin(), c.end(), lessThan10)); + CHECK(ba::all_of(c.begin(), c.begin() + 3, lessThan10)); + CHECK(ba::all_of(c.end(), c.end(), isOdd)); + CHECK_FALSE(ba::all_of_equal(c, 3)); + CHECK(ba::all_of_equal(c.begin() + 3, c.begin() + 4, 3)); + CHECK(ba::all_of_equal(c.begin(), c.begin(), 99)); +} + +TEST_CASE("doc: any_of") { + CHECK(ba::any_of(c, isOdd)); + CHECK(ba::any_of(c.begin(), c.end(), lessThan10)); + CHECK_FALSE(ba::any_of(c.begin() + 4, c.end(), lessThan10)); + CHECK_FALSE(ba::any_of(c.end(), c.end(), isOdd)); + CHECK(ba::any_of_equal(c, 3)); + CHECK_FALSE(ba::any_of_equal(c.begin(), c.begin() + 3, 3)); + CHECK_FALSE(ba::any_of_equal(c.begin(), c.begin(), 99)); +} + +TEST_CASE("doc: none_of") { + CHECK_FALSE(ba::none_of(c, isOdd)); + CHECK_FALSE(ba::none_of(c.begin(), c.end(), lessThan10)); + CHECK(ba::none_of(c.begin() + 4, c.end(), lessThan10)); + CHECK(ba::none_of(c.end(), c.end(), isOdd)); + CHECK_FALSE(ba::none_of_equal(c, 3)); + CHECK(ba::none_of_equal(c.begin(), c.begin() + 3, 3)); + CHECK(ba::none_of_equal(c.begin(), c.begin(), 99)); +} + +TEST_CASE("doc: one_of") { + CHECK_FALSE(ba::one_of(c, isOdd)); + CHECK_FALSE(ba::one_of(c.begin(), c.end(), lessThan10)); + CHECK(ba::one_of(c.begin() + 3, c.end(), lessThan10)); + CHECK_FALSE(ba::one_of(c.end(), c.end(), isOdd)); + CHECK(ba::one_of_equal(c, 3)); + CHECK_FALSE(ba::one_of_equal(c.begin(), c.begin() + 3, 3)); + CHECK_FALSE(ba::one_of_equal(c.begin(), c.begin(), 99)); +} + +TEST_CASE("doc: apply_permutation") { + std::vector v{'a', 'b', 'c', 'd'}; + std::vector order{2, 3, 1, 0}; + ba::apply_permutation(v, order); + CHECK(v == std::vector{'c', 'd', 'b', 'a'}); + std::vector w{'a', 'b', 'c', 'd'}; + std::vector order2{2, 3, 1, 0}; + ba::apply_reverse_permutation(w.begin(), w.end(), order2.begin(), order2.end()); + CHECK(w == std::vector{'d', 'c', 'a', 'b'}); +} + +TEST_CASE("doc: clamp") { + CHECK(ba::clamp(1, 1, 10) == 1); + CHECK(ba::clamp(0, 1, 10) == 1); + CHECK(ba::clamp(11, 1, 10) == 10); + CHECK(ba::clamp(3, 10, 1, std::greater()) == 3); + std::vector in{-5, 0, 5, 50}; + std::vector out; + ba::clamp_range(in, std::back_inserter(out), 0, 10); + CHECK(out == std::vector{0, 0, 5, 10}); + CHECK(ba::is_clamped(5, 0, 10)); + CHECK_FALSE(ba::is_clamped(11, 0, 10)); +} + +TEST_CASE("doc: equal") { + std::vector a{0, 1, 2}; + std::vector b{0, 1, 2, 3}; + CHECK_FALSE(ba::equal(a.begin(), a.end(), b.begin(), b.end())); + CHECK(ba::equal(a.begin(), a.end(), b.begin(), b.begin() + 3)); + CHECK(ba::equal(a.begin(), a.begin(), b.begin(), b.begin())); + CHECK(ba::equal(a.begin(), a.end(), b.begin(), b.begin() + 3, std::equal_to())); +} + +TEST_CASE("doc: mismatch") { + std::vector c1{0, 1, 2}; + std::vector c2{0, 1, 2, 3}; + auto r = ba::mismatch(c1.begin(), c1.end(), c2.begin(), c2.end()); + CHECK(r.first == c1.end()); + CHECK(r.second == c2.begin() + 3); + auto r2 = ba::mismatch(c1.begin(), c1.end(), c2.begin(), c2.begin()); + CHECK(r2.first == c1.begin()); + CHECK(r2.second == c2.begin()); +} + +TEST_CASE("doc: find_backward") { + std::vector v{1, 2, 3, 2, 1}; + CHECK(ba::find_backward(v.begin(), v.end(), 2) - v.begin() == 3); + CHECK(ba::find_backward(v, 9) == v.end()); + CHECK(ba::find_not_backward(v, 1) - v.begin() == 3); + CHECK(ba::find_if_backward(v, [](int i) { return i > 2; }) - v.begin() == 2); + CHECK(ba::find_if_not_backward(v, [](int i) { return i < 3; }) - v.begin() == 2); +} + +TEST_CASE("doc: find_not") { + std::vector vec{1, 1, 2}; + CHECK(ba::find_not(vec.begin(), vec.end(), 1) == vec.begin() + 2); + std::vector c1{1, 0, 0}; + CHECK(ba::find_not(c1.begin(), c1.end(), 1) == std::next(c1.begin())); + CHECK(ba::find_not(c1.begin(), c1.end(), 0) == c1.begin()); + CHECK(ba::find_not(c1, 2) == c1.begin()); + CHECK(ba::find_if_not(c1, [](int i) { return i == 1; }) == std::next(c1.begin())); +} + +TEST_CASE("doc: gather") { + // gather moves the selected elements next to the pivot, stably. + std::vector v{1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto pivot = v.begin() + 4; + auto r = ba::gather(v.begin(), v.end(), pivot, [](int i) { return i % 2 == 0; }); + CHECK(r.second - r.first == 4); + CHECK(std::vector(r.first, r.second) == std::vector{2, 4, 6, 8}); + CHECK(std::is_permutation(v.begin(), v.end(), + std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9}.begin())); +} + +TEST_CASE("doc: hex") { + std::string out; + ba::hex("abcdef", std::back_inserter(out)); + CHECK(out == "616263646566"); + CHECK(ba::hex(std::string("32")) == "3332"); + CHECK(ba::hex_lower(std::string("\x7f\xfe")) == "7ffe"); + CHECK(ba::unhex(std::string("616263646566")) == "abcdef"); + CHECK(ba::unhex(std::string("3332")) == "32"); + std::u16string wout; + ba::unhex(std::string("3332"), std::back_inserter(wout)); + REQUIRE(wout.size() == 1); + CHECK(wout[0] == 0x3332); + CHECK_THROWS_AS(ba::unhex(std::string("3")), ba::not_enough_input); + CHECK_THROWS_AS(ba::unhex(std::string("ACEG")), ba::non_hex_input); + CHECK_THROWS_AS(ba::unhex(std::string("ACEG")), ba::hex_decode_error); +} + +TEST_CASE("doc: is_palindrome") { + CHECK(ba::is_palindrome(std::vector{1, 2, 1})); + CHECK_FALSE(ba::is_palindrome(std::vector{1, 2, 3})); + CHECK(ba::is_palindrome(std::vector{})); + CHECK(ba::is_palindrome("a")); + CHECK(ba::is_palindrome(std::string("AbA"), [](char a, char b) { + return std::tolower(a) == std::tolower(b); + })); +} + +TEST_CASE("doc: is_partitioned") { + std::vector v{0, 1, 2, 3, 14, 15}; + CHECK_FALSE(ba::is_partitioned(v, isOdd)); + CHECK(ba::is_partitioned(v, lessThan10)); + CHECK(ba::is_partitioned(v.begin(), v.end(), lessThan10)); + CHECK(ba::is_partitioned(v.begin(), v.begin() + 3, lessThan10)); + CHECK(ba::is_partitioned(v.end(), v.end(), isOdd)); +} + +TEST_CASE("doc: is_partitioned_until") { + std::vector v{0, 1, 2, 3, 14, 15}; + CHECK(ba::is_partitioned_until(v, isOdd) == v.begin() + 1); + CHECK(ba::is_partitioned_until(v, lessThan10) == v.end()); + CHECK(ba::is_partitioned_until(v.end(), v.end(), isOdd) == v.end()); +} + +TEST_CASE("doc: is_permutation") { + std::vector c1{0, 1, 2, 3, 14, 15}; + std::vector c2{15, 14, 3, 1, 2}; + CHECK_FALSE(ba::is_permutation(c1.begin(), c1.end(), c2.begin(), c2.end())); + CHECK(ba::is_permutation(c1.begin() + 1, c1.end(), c2.begin(), c2.end())); + CHECK(ba::is_permutation(c1.end(), c1.end(), c2.end(), c2.end())); + CHECK(ba::is_permutation(c2, c1.begin() + 1)); +} + +TEST_CASE("doc: is_sorted") { + std::vector v{1, 2, 2, 3}; + CHECK(ba::is_sorted(v)); + CHECK(ba::is_sorted_until(v.begin(), v.end()) == v.end()); + CHECK(ba::is_increasing(v)); + CHECK_FALSE(ba::is_strictly_increasing(v)); + CHECK_FALSE(ba::is_decreasing(v)); + std::vector d{3, 2, 1}; + CHECK(ba::is_strictly_decreasing(d)); + CHECK(ba::is_sorted(d, std::greater())); + std::vector u{1, 3, 2}; + CHECK(ba::is_sorted_until(u) == u.begin() + 2); +} + +TEST_CASE("doc: partition_point") { + std::vector v{1, 3, 5, 2, 4}; + CHECK(ba::partition_point(v, isOdd) == v.begin() + 3); + CHECK(ba::partition_point(v.begin(), v.end(), isOdd) == v.begin() + 3); +} + +TEST_CASE("doc: BoyerMoore") { + const std::string corpus("abracadabra"); + const std::string pattern("cad"); + ba::boyer_moore bm(pattern.begin(), pattern.end()); + auto r = bm(corpus.begin(), corpus.end()); + CHECK(r.first - corpus.begin() == 4); + CHECK(r.second - corpus.begin() == 7); + auto r2 = ba::boyer_moore_search(corpus, pattern); + CHECK(r2.first - corpus.begin() == 4); + auto searcher = ba::make_boyer_moore(pattern); + CHECK(searcher(corpus).first - corpus.begin() == 4); +} + +TEST_CASE("doc: BoyerMooreHorspool") { + const std::string corpus("abracadabra"); + const std::string pattern("abra"); + ba::boyer_moore_horspool bmh(pattern.begin(), pattern.end()); + CHECK(bmh(corpus.begin(), corpus.end()).first == corpus.begin()); + auto r = ba::boyer_moore_horspool_search(corpus.begin() + 1, corpus.end(), + pattern.begin(), pattern.end()); + CHECK(r.first - corpus.begin() == 7); + CHECK(ba::make_boyer_moore_horspool(pattern)(corpus).first == corpus.begin()); +} + +TEST_CASE("doc: KnuthMorrisPratt") { + const std::string corpus("abracadabra"); + const std::string pattern("dab"); + ba::knuth_morris_pratt kmp(pattern.begin(), pattern.end()); + CHECK(kmp(corpus.begin(), corpus.end()).first - corpus.begin() == 6); + const std::string missing("xyz"); + auto r = ba::knuth_morris_pratt_search(corpus, missing); + CHECK(r.first == corpus.end()); + CHECK(r.second == corpus.end()); + CHECK(ba::make_knuth_morris_pratt(pattern)(corpus).first - corpus.begin() == 6); +} + +TEST_CASE("cxx11: copy_if / copy_while / copy_until / copy_n / iota / partition_copy") { + std::vector v{1, 2, 3, 4, 5}; + std::vector out; + ba::copy_if(v, std::back_inserter(out), isOdd); + CHECK(out == std::vector{1, 3, 5}); + out.clear(); + ba::copy_while(v, std::back_inserter(out), [](int i) { return i < 3; }); + CHECK(out == std::vector{1, 2}); + out.clear(); + ba::copy_until(v, std::back_inserter(out), [](int i) { return i == 4; }); + CHECK(out == std::vector{1, 2, 3}); + out.clear(); + ba::copy_n(v.begin(), 2, std::back_inserter(out)); + CHECK(out == std::vector{1, 2}); + std::vector seq(4); + ba::iota(seq, 10); + CHECK(seq == std::vector{10, 11, 12, 13}); + std::vector odd, even; + ba::partition_copy(v, std::back_inserter(odd), std::back_inserter(even), isOdd); + CHECK(odd == std::vector{1, 3, 5}); + CHECK(even == std::vector{2, 4}); +} + +TEST_CASE("cxx17: reduce / scans / for_each_n / transform_reduce") { + std::vector v{1, 2, 3, 4}; + CHECK(ba::reduce(v.begin(), v.end()) == 10); + CHECK(ba::reduce(v, 5) == 15); + CHECK(ba::reduce(v.begin(), v.end(), 1, std::multiplies()) == 24); + std::vector out; + ba::inclusive_scan(v.begin(), v.end(), std::back_inserter(out)); + CHECK(out == std::vector{1, 3, 6, 10}); + out.clear(); + ba::exclusive_scan(v.begin(), v.end(), std::back_inserter(out), 0); + CHECK(out == std::vector{0, 1, 3, 6}); + out.clear(); + ba::transform_inclusive_scan(v.begin(), v.end(), std::back_inserter(out), + std::plus(), [](int i) { return i * i; }); + CHECK(out == std::vector{1, 5, 14, 30}); + out.clear(); + ba::transform_exclusive_scan(v.begin(), v.end(), std::back_inserter(out), 0, + std::plus(), [](int i) { return i * 2; }); + CHECK(out == std::vector{0, 2, 6, 12}); + CHECK(ba::transform_reduce(v.begin(), v.end(), v.begin(), 0) == 30); + int sum = 0; + ba::for_each_n(v.begin(), 3, [&](int i) { sum += i; }); + CHECK(sum == 6); +} + +TEST_CASE("sort_subrange / partition_subrange") { + std::vector v{9, 1, 8, 2, 7, 3, 6, 4, 5}; + ba::sort_subrange(v.begin(), v.end(), v.begin() + 2, v.begin() + 5); + CHECK(std::vector(v.begin() + 2, v.begin() + 5) == std::vector{3, 4, 5}); + std::vector w{9, 1, 8, 2, 7, 3, 6, 4, 5}; + ba::partition_subrange(w.begin(), w.end(), w.begin() + 2, w.begin() + 5); + std::vector mid(w.begin() + 2, w.begin() + 5); + std::sort(mid.begin(), mid.end()); + CHECK(mid == std::vector{3, 4, 5}); +} diff --git a/tests/algorithm/api_string.cpp b/tests/algorithm/api_string.cpp new file mode 100644 index 00000000..35bcf961 --- /dev/null +++ b/tests/algorithm/api_string.cpp @@ -0,0 +1,116 @@ +// mcpplibs.mwrap.boost.algorithm — the string algorithms (boost/algorithm/ +// string.hpp, trim_all) through the module: case conversion, trimming, +// predicates, find/replace/erase families, split/join, finders and formatters. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.algorithm; + +TEST_CASE("case conversion") { + std::string s("MiXeD"); + CHECK(boost::to_upper_copy(s) == "MIXED"); + CHECK(boost::to_lower_copy(s) == "mixed"); + boost::to_upper(s); + CHECK(s == "MIXED"); + CHECK(boost::to_lower_copy(std::string("ÄB"), std::locale::classic()).back() == 'b'); +} + +TEST_CASE("trim family") { + CHECK(boost::trim_copy(std::string(" a b ")) == "a b"); + CHECK(boost::trim_left_copy_if(std::string("xxab"), boost::is_any_of("x")) == "ab"); + CHECK(boost::trim_right_copy_if(std::string("ab12"), boost::is_digit()) == "ab"); + std::string s(" a b "); + boost::trim_all(s); + CHECK(s == "a b"); + std::string f("--a--b--"); + boost::trim_fill(f, "+"); + CHECK(f == "--a--b--"); + std::string g(" a b "); + boost::trim_fill(g, "_"); + CHECK(g == "a_b"); +} + +// boost::find and boost::lexicographical_compare name both the range and the +// string algorithm (each library injects its own into boost::), as with plain +// #include; the string forms are spelled boost::algorithm:: here. +TEST_CASE("predicates and classification") { + CHECK(boost::istarts_with("Hello", "he")); + CHECK(boost::iends_with("Hello", "LO")); + CHECK(boost::icontains("Hello", "ELL")); + CHECK(boost::iequals("abc", "ABC")); + CHECK(boost::algorithm::lexicographical_compare("abc", "abd")); + CHECK(boost::ilexicographical_compare("ABC", "abd")); + CHECK(boost::all("12345", boost::is_digit())); + CHECK(boost::all("abc", boost::is_lower())); + CHECK(boost::all("ABC", boost::is_upper())); + CHECK(boost::all("a1", boost::is_alnum())); + CHECK(boost::all(" \t", boost::is_space())); + CHECK(boost::all("09af", boost::is_xdigit())); + CHECK(boost::all("cdef", boost::is_from_range('c', 'f'))); + CHECK(boost::all("ab", boost::is_classified(std::ctype_base::alpha))); + CHECK_FALSE(boost::all("a b", boost::is_alpha() || boost::is_digit())); +} + +TEST_CASE("find family") { + std::string s("one two one two"); + CHECK(boost::find_first(s, "two").begin() - s.begin() == 4); + CHECK(boost::find_last(s, "two").begin() - s.begin() == 12); + CHECK(boost::find_nth(s, "one", 1).begin() - s.begin() == 8); + CHECK(boost::ifind_first(s, "TWO").begin() - s.begin() == 4); + CHECK(boost::find_head(s, 3) == boost::as_literal("one")); + CHECK(boost::find_tail(s, 3) == boost::as_literal("two")); + auto tok = boost::find_token(s, boost::is_space(), boost::token_compress_on); + CHECK(tok.begin() - s.begin() == 3); + CHECK(boost::algorithm::find(s, boost::first_finder("one")).begin() == s.begin()); +} + +TEST_CASE("replace and erase family") { + std::string s("aaa bbb aaa"); + CHECK(boost::replace_last_copy(s, "aaa", "c") == "aaa bbb c"); + CHECK(boost::ireplace_all_copy(s, "AAA", "x") == "x bbb x"); + CHECK(boost::replace_head_copy(s, 3, "z") == "z bbb aaa"); + CHECK(boost::replace_tail_copy(s, 3, "z") == "aaa bbb z"); + CHECK(boost::erase_first_copy(s, "aaa ") == "bbb aaa"); + CHECK(boost::erase_nth_copy(s, "a", 1) == "aa bbb aaa"); + CHECK(boost::erase_head_copy(s, 4) == "bbb aaa"); + CHECK(boost::erase_tail_copy(s, 4) == "aaa bbb"); + boost::ierase_all(s, "B"); + CHECK(s == "aaa aaa"); +} + +TEST_CASE("split / join / iter_split / find_all") { + std::vector parts; + boost::split(parts, "a;b;;c", boost::is_any_of(";")); + CHECK(parts == std::vector{"a", "b", "", "c"}); + CHECK(boost::algorithm::join(parts, "+") == "a+b++c"); + CHECK(boost::algorithm::join_if(parts, "+", [](const std::string& x) { return !x.empty(); }) == + "a+b+c"); + std::vector pieces; + boost::iter_split(pieces, std::string("a--b--c"), boost::first_finder("--")); + CHECK(pieces == std::vector{"a", "b", "c"}); + std::vector> hits; + const std::string text("ab-ab-ab"); + boost::find_all(hits, text, "ab"); + CHECK(hits.size() == 3); + using split_it = boost::split_iterator; + std::vector viaIt; + for (split_it it = boost::make_split_iterator(text, boost::first_finder("-")); + it != split_it(); ++it) + viaIt.push_back(boost::copy_range(*it)); + CHECK(viaIt == std::vector{"ab", "ab", "ab"}); +} + +TEST_CASE("finders and formatters") { + std::string s("key=value"); + auto r = boost::algorithm::find(s, boost::token_finder(boost::is_any_of("="))); + CHECK(r.begin() - s.begin() == 3); + CHECK(boost::algorithm::find(s, boost::range_finder(boost::make_iterator_range(s.begin(), s.begin() + 3))) == + boost::as_literal("key")); + CHECK(boost::find_format_copy(s, boost::first_finder("value"), boost::const_formatter("v")) == + "key=v"); + CHECK(boost::find_format_all_copy(std::string("a.b.c"), boost::first_finder("."), + boost::empty_formatter(std::string())) == "abc"); + CHECK(boost::algorithm::find(s, boost::tail_finder(5)) == boost::as_literal("value")); + CHECK(boost::algorithm::find(s, boost::last_finder("e")).begin() - s.begin() == 8); + CHECK(boost::algorithm::find(s, boost::nth_finder("e", 1)).begin() - s.begin() == 8); +} diff --git a/tests/algorithm/coverage.toml b/tests/algorithm/coverage.toml new file mode 100644 index 00000000..bae5f257 --- /dev/null +++ b/tests/algorithm/coverage.toml @@ -0,0 +1,171 @@ +# tests/algorithm/coverage.toml — official examples and documentation snippets of +# Boost.algorithm 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "algorithm" + +[[example]] +path = "libs/algorithm/example/apply_permutation_example.cpp" +case = "algorithm/examples:example: apply_permutation_example" + +[[example]] +path = "libs/algorithm/example/clamp_example.cpp" +case = "algorithm/examples:example: clamp_example" + +[[example]] +path = "libs/algorithm/example/is_palindrome_example.cpp" +case = "algorithm/examples:example: is_palindrome_example" + +[[example]] +path = "libs/algorithm/example/is_partitioned_until_example.cpp" +case = "algorithm/examples:example: is_partitioned_until_example" + +[[example]] +path = "libs/algorithm/example/search_example.cpp" +case = "algorithm/examples:example: search_example" + +[[example]] +path = "libs/algorithm/minmax/example/minmax_ex.cpp" +case = "algorithm/examples:example: minmax_ex" + +[[example]] +path = "libs/algorithm/minmax/example/minmax_timer.cpp" +case = "algorithm/examples:example: minmax_timer" + +[[example]] +path = "libs/algorithm/string/example/conv_example.cpp" +case = "algorithm/examples:example: conv_example" + +[[example]] +path = "libs/algorithm/string/example/find_example.cpp" +case = "algorithm/examples:example: find_example" + +[[example]] +path = "libs/algorithm/string/example/predicate_example.cpp" +case = "algorithm/examples:example: predicate_example" + +[[example]] +path = "libs/algorithm/string/example/regex_example.cpp" +case = "algorithm/include_string_regex:example: regex_example" + +[[example]] +path = "libs/algorithm/string/example/replace_example.cpp" +case = "algorithm/examples:example: replace_example" + +[[example]] +path = "libs/algorithm/string/example/rle_example.cpp" +case = "algorithm/examples:example: rle_example" + +[[example]] +path = "libs/algorithm/string/example/split_example.cpp" +case = "algorithm/examples:example: split_example" + +[[example]] +path = "libs/algorithm/string/example/trim_example.cpp" +case = "algorithm/examples:example: trim_example" + +[[snippet]] +doc = "libs/algorithm/doc/all_of.qbk#all_of" +title = "all_of" +case = "algorithm/api_sequences:doc: all_of" + +[[snippet]] +doc = "libs/algorithm/doc/any_of.qbk#any_of" +title = "any_of" +case = "algorithm/api_sequences:doc: any_of" + +[[snippet]] +doc = "libs/algorithm/doc/apply_permutation.qbk#apply_permutation" +title = "apply_permutation" +case = "algorithm/api_sequences:doc: apply_permutation" + +[[snippet]] +doc = "libs/algorithm/doc/boyer_moore.qbk#BoyerMoore" +title = "Boyer-Moore Search" +case = "algorithm/api_sequences:doc: BoyerMoore" + +[[snippet]] +doc = "libs/algorithm/doc/boyer_moore_horspool.qbk#BoyerMooreHorspool" +title = "Boyer-Moore-Horspool Search" +case = "algorithm/api_sequences:doc: BoyerMooreHorspool" + +[[snippet]] +doc = "libs/algorithm/doc/clamp-hpp.qbk#clamp" +title = "clamp" +case = "algorithm/api_sequences:doc: clamp" + +[[snippet]] +doc = "libs/algorithm/doc/equal.qbk#equal" +title = "equal" +case = "algorithm/api_sequences:doc: equal" + +[[snippet]] +doc = "libs/algorithm/doc/find_backward.qbk#find_backward" +title = "find_backward" +case = "algorithm/api_sequences:doc: find_backward" + +[[snippet]] +doc = "libs/algorithm/doc/find_not.qbk#find_not" +title = "find_not" +case = "algorithm/api_sequences:doc: find_not" + +[[snippet]] +doc = "libs/algorithm/doc/gather.qbk#gather" +title = "gather" +case = "algorithm/api_sequences:doc: gather" + +[[snippet]] +doc = "libs/algorithm/doc/hex.qbk#hex" +title = "hex" +case = "algorithm/api_sequences:doc: hex" + +[[snippet]] +doc = "libs/algorithm/doc/is_palindrome.qbk#is_palindrome" +title = "is_palindrome" +case = "algorithm/api_sequences:doc: is_palindrome" + +[[snippet]] +doc = "libs/algorithm/doc/is_partitioned.qbk#is_partitioned" +title = "is_partitioned" +case = "algorithm/api_sequences:doc: is_partitioned" + +[[snippet]] +doc = "libs/algorithm/doc/is_partitioned_until.qbk#is_partitioned_until" +title = "is_partitioned_until" +case = "algorithm/api_sequences:doc: is_partitioned_until" + +[[snippet]] +doc = "libs/algorithm/doc/is_permutation.qbk#is_permutation" +title = "is_permutation" +case = "algorithm/api_sequences:doc: is_permutation" + +[[snippet]] +doc = "libs/algorithm/doc/knuth_morris_pratt.qbk#KnuthMorrisPratt" +title = "Knuth-Morris-Pratt Search" +case = "algorithm/api_sequences:doc: KnuthMorrisPratt" + +[[snippet]] +doc = "libs/algorithm/doc/mismatch.qbk#mismatch" +title = "mismatch" +case = "algorithm/api_sequences:doc: mismatch" + +[[snippet]] +doc = "libs/algorithm/doc/none_of.qbk#none_of" +title = "none_of" +case = "algorithm/api_sequences:doc: none_of" + +[[snippet]] +doc = "libs/algorithm/doc/one_of.qbk#one_of" +title = "one_of" +case = "algorithm/api_sequences:doc: one_of" + +[[snippet]] +doc = "libs/algorithm/doc/ordered-hpp.qbk#is_sorted" +title = "is_sorted" +case = "algorithm/api_sequences:doc: is_sorted" + +[[snippet]] +doc = "libs/algorithm/doc/partition_point.qbk#partition_point" +title = "partition_point" +case = "algorithm/api_sequences:doc: partition_point" diff --git a/tests/algorithm/custom_types.cpp b/tests/algorithm/custom_types.cpp new file mode 100644 index 00000000..b4b114b4 --- /dev/null +++ b/tests/algorithm/custom_types.cpp @@ -0,0 +1,132 @@ +// mcpplibs.mwrap.boost.algorithm — user-defined types through the module: +// user value types with their own ordering and equality, user predicates and +// comparators (function objects, function pointers, lambdas), user iterators +// (a bidirectional list-like container), and string algorithms over a user +// character sequence. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.algorithm; + +namespace ba = boost::algorithm; + +namespace user { +struct version { + int major = 0, minor = 0; + // is_increasing / is_decreasing compare with <= and >=. + friend auto operator<=>(const version&, const version&) = default; + friend std::ostream& operator<<(std::ostream& os, const version& v) { + return os << v.major << '.' << v.minor; + } +}; + +struct by_minor { + bool operator()(const version& a, const version& b) const { return a.minor < b.minor; } +}; + +bool same_major(const version& a, const version& b) { return a.major == b.major; } + +// A minimal bidirectional container with its own iterator type. +class ring { +public: + explicit ring(std::initializer_list v) : data_(v) {} + class iterator { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = int; + using difference_type = std::ptrdiff_t; + using pointer = const int*; + using reference = const int&; + iterator() = default; + iterator(const std::vector* d, std::size_t i) : d_(d), i_(i) {} + reference operator*() const { return (*d_)[i_]; } + iterator& operator++() { ++i_; return *this; } + iterator operator++(int) { auto t = *this; ++i_; return t; } + iterator& operator--() { --i_; return *this; } + iterator operator--(int) { auto t = *this; --i_; return t; } + friend bool operator==(const iterator& a, const iterator& b) { return a.i_ == b.i_; } + private: + const std::vector* d_ = nullptr; + std::size_t i_ = 0; + }; + using const_iterator = iterator; + iterator begin() const { return {&data_, 0}; } + iterator end() const { return {&data_, data_.size()}; } +private: + std::vector data_; +}; +} // namespace user + +TEST_CASE("clamp: user type, operator< and a user comparator") { + const user::version lo{1, 0}, hi{2, 5}; + CHECK(ba::clamp(user::version{0, 9}, lo, hi) == lo); + CHECK(ba::clamp(user::version{3, 0}, lo, hi) == hi); + CHECK(ba::clamp(user::version{1, 7}, lo, hi) == user::version{1, 7}); + // by_minor orders by the minor number only: 9 is above hi.minor (5). + CHECK(ba::clamp(user::version{0, 9}, lo, hi, user::by_minor()) == hi); + CHECK(ba::is_clamped(user::version{1, 3}, lo, hi)); +} + +TEST_CASE("ordering and minmax: user type") { + std::vector v{{1, 0}, {1, 2}, {2, 0}}; + CHECK(ba::is_sorted(v)); + CHECK(ba::is_strictly_increasing(v)); + CHECK_FALSE(ba::is_sorted(v, user::by_minor())); + auto mm = boost::minmax_element(v.begin(), v.end()); + CHECK(*mm.first == user::version{1, 0}); + CHECK(*mm.second == user::version{2, 0}); + auto mm2 = boost::minmax_element(v.begin(), v.end(), user::by_minor()); + CHECK(mm2.second->minor == 2); +} + +TEST_CASE("predicates: function pointer, functor, lambda over user types") { + std::vector v{{1, 0}, {1, 2}, {1, 9}}; + CHECK(ba::all_of(v, [](const user::version& x) { return x.major == 1; })); + CHECK(ba::one_of_equal(v, user::version{1, 2})); + CHECK(ba::none_of_equal(v, user::version{3, 0})); + CHECK(ba::is_palindrome(v, user::same_major)); + CHECK(ba::find_not(v, user::version{1, 0}) == v.begin() + 1); + std::vector w{{1, 9}, {1, 0}, {1, 2}}; + CHECK(ba::is_permutation(v.begin(), v.end(), w.begin(), w.end())); + CHECK(ba::equal(v.begin(), v.end(), w.begin(), w.end(), user::same_major)); +} + +TEST_CASE("user iterators: bidirectional only") { + user::ring r{1, 2, 3, 2, 1}; + CHECK(ba::is_palindrome(r.begin(), r.end())); + CHECK(ba::is_palindrome(r)); + auto it = ba::find_backward(r.begin(), r.end(), 2); + CHECK(*it == 2); + CHECK(std::distance(r.begin(), it) == 3); + CHECK(ba::is_partitioned_until(r, [](int i) { return i < 3; }) != r.end()); + std::vector out; + ba::copy_if(r, std::back_inserter(out), [](int i) { return i > 1; }); + CHECK(out == std::vector{2, 3, 2}); +} + +TEST_CASE("searchers: user element type with operator== and std::hash") { + struct sym { + char c; + bool operator==(const sym&) const = default; + }; + std::vector corpus{{'a'}, {'b'}, {'c'}, {'a'}, {'b'}}; + std::vector pat{{'c'}, {'a'}}; + auto r = ba::knuth_morris_pratt_search(corpus.begin(), corpus.end(), pat.begin(), pat.end()); + CHECK(r.first - corpus.begin() == 2); +} + +TEST_CASE("string algorithms over a user predicate and std::vector") { + struct is_vowel { + bool operator()(char c) const { return std::string_view("aeiou").find(c) != std::string_view::npos; } + }; + std::string s("programming"); + CHECK(boost::erase_all_copy(s, "m") == "prograing"); + CHECK(boost::trim_copy_if(std::string("aloha"), is_vowel()) == "loh"); + CHECK(boost::all("aei", is_vowel())); + std::vector chars{'x', 'y', 'z'}; + boost::to_upper(chars); + CHECK(chars == std::vector{'X', 'Y', 'Z'}); + std::vector parts; + boost::split(parts, s, is_vowel()); + CHECK(parts.front() == "pr"); +} diff --git a/tests/algorithm/examples.cpp b/tests/algorithm/examples.cpp new file mode 100644 index 00000000..a6474c3f --- /dev/null +++ b/tests/algorithm/examples.cpp @@ -0,0 +1,357 @@ +// mcpplibs.mwrap.boost.algorithm — the official examples of Boost.Algorithm +// (libs/algorithm/example, minmax/example, string/example) as test cases. Each +// case keeps the example's data and calls and turns what it prints into +// checks. string/example/regex_example.cpp is include_string_regex.cpp: the +// *_regex family is not in the module (tools/export_policy.toml [algorithm]). +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.algorithm; + +namespace ba = boost::algorithm; + +TEST_CASE("example: apply_permutation_example") { + { + std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; + ba::apply_permutation(vec.begin(), vec.end(), order.begin(), order.end()); + CHECK(vec == std::vector{5, 3, 4, 2, 1}); + } + { + std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; + ba::apply_reverse_permutation(vec.begin(), vec.end(), order.begin(), order.end()); + CHECK(vec == std::vector{5, 4, 2, 3, 1}); + } + { + std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; + ba::apply_reverse_permutation(vec, order); + CHECK(vec == std::vector{5, 4, 2, 3, 1}); + } + { + std::vector vec{1, 2, 3, 4, 5}, order{4, 2, 3, 1, 0}; + ba::apply_permutation(vec, order); + CHECK(vec == std::vector{5, 3, 4, 2, 1}); + } +} + +namespace { +bool compare_string_lengths(const std::string& one, const std::string& two) { + return one.length() < two.length(); +} +} // namespace + +TEST_CASE("example: clamp_example") { + CHECK(ba::clamp(5, 1, 10) == 5); + CHECK(ba::clamp(15, 1, 10) == 10); + CHECK(ba::clamp(-15, 1, 10) == 1); + CHECK(ba::clamp(5.1, 1.0, 10.0) == doctest::Approx(5.1)); + std::string one("Lower Bound"), two("upper bound!"), test1("test#"), test2("#test"); + CHECK(ba::clamp(test1, one, two) == "test#"); + CHECK(ba::clamp(test2, one, two) == "Lower Bound"); + CHECK(ba::clamp(test1, one, two, compare_string_lengths) == "Lower Bound"); + CHECK(ba::clamp(test2, one, two, compare_string_lengths) == "Lower Bound"); + // "Somewhat unexpected": T is deduced from the first argument (int). + CHECK(ba::clamp(12, 14.7, 15.9) == 14); + CHECK(ba::clamp(static_cast(12), 14.7, 15.9) == doctest::Approx(14.7)); +} + +namespace { +template +bool funcComparator(const T& v1, const T& v2) { return v1 == v2; } + +struct functorComparator { + template + bool operator()(const T& v1, const T& v2) const { return v1 == v2; } +}; +} // namespace + +TEST_CASE("example: is_palindrome_example") { + std::vector vec{1, 2, 1}; + CHECK(ba::is_palindrome(vec.begin(), vec.end())); + CHECK(ba::is_palindrome(vec.cbegin(), vec.cend())); + std::list list{1, 2, 1}; + CHECK(ba::is_palindrome(list.begin(), list.end())); + auto lambdaComparator = [](int v1, int v2) { return v1 == v2; }; + auto objFunc = std::function(lambdaComparator); + CHECK(ba::is_palindrome(vec.begin(), vec.end(), lambdaComparator)); + CHECK(ba::is_palindrome(vec.begin(), vec.end(), funcComparator)); + CHECK(ba::is_palindrome(vec.begin(), vec.end(), functorComparator())); + CHECK(ba::is_palindrome(vec.begin(), vec.end(), objFunc)); + CHECK(ba::is_palindrome(vec)); + CHECK(ba::is_palindrome("aba")); + CHECK_FALSE(ba::is_palindrome("abc")); +} + +namespace { +bool isOdd(const int v1) { return v1 % 2 != 0; } +struct isOddComp { + bool operator()(const int v1) const { return v1 % 2 != 0; } +}; +} // namespace + +TEST_CASE("example: is_partitioned_until_example") { + std::vector good{1, 2, 4}; + std::vector bad{1, 2, 3}; + CHECK(ba::is_partitioned_until(good.begin(), good.end(), isOdd) == good.end()); + CHECK(ba::is_partitioned_until(good.begin(), good.end(), isOddComp()) == good.end()); + auto it3 = ba::is_partitioned_until(bad, isOdd); + REQUIRE(it3 != bad.end()); + CHECK(*it3 == 3); +} + +TEST_CASE("example: search_example") { + const std::string haystack("ABACAB is it everywhere!"); + const std::string needle1("ACAB"); + const std::string needle2("not ABA"); + const auto none = std::make_pair(haystack.end(), haystack.end()); + + auto r1 = ba::boyer_moore_search(haystack.begin(), haystack.end(), + needle1.begin(), needle1.end()); + CHECK(r1 != none); + CHECK(r1.first - haystack.begin() == 2); + + ba::boyer_moore search1(needle1.begin(), needle1.end()); + CHECK(search1(haystack.begin(), haystack.end()) != none); + + CHECK(ba::boyer_moore_horspool_search(haystack.begin(), haystack.end(), + needle1.begin(), needle1.end()) != none); + CHECK(ba::knuth_morris_pratt_search(haystack.begin(), haystack.end(), + needle1.begin(), needle1.end()) != none); + CHECK(ba::boyer_moore_search(haystack.begin(), haystack.end(), + needle2.begin(), needle2.end()) == none); +} + +TEST_CASE("example: minmax_ex") { + boost::tuple result1 = boost::minmax(1, 0); + CHECK(result1.get<0>() == 0); + CHECK(result1.get<1>() == 1); + + std::list L; + std::minstd_rand gen(42); + std::generate_n(std::front_inserter(L), 1000, [&] { return static_cast(gen()); }); + using iterator = std::list::const_iterator; + std::pair result2 = boost::minmax_element(L.cbegin(), L.cend()); + CHECK(result2.first == std::min_element(L.cbegin(), L.cend())); + CHECK(result2.second == std::max_element(L.cbegin(), L.cend())); +} + +TEST_CASE("example: minmax_timer") { + // The timing harness of minmax_timer.cpp, without the clock: every + // first/last min/max variant it times must agree with the std algorithms. + std::vector v{5, 1, 9, 1, 9, 3}; + auto first = v.begin(), last = v.end(); + CHECK(*boost::first_min_element(first, last) == 1); + CHECK(boost::first_min_element(first, last) - first == 1); + CHECK(boost::last_min_element(first, last) - first == 3); + CHECK(boost::first_max_element(first, last) - first == 2); + CHECK(boost::last_max_element(first, last) - first == 4); + auto fl = boost::first_min_last_max_element(first, last); + CHECK(fl.first - first == 1); + CHECK(fl.second - first == 4); + auto lf = boost::last_min_first_max_element(first, last); + CHECK(lf.first - first == 3); + CHECK(lf.second - first == 2); + auto ll = boost::last_min_last_max_element(first, last); + CHECK(ll.first - first == 3); + CHECK(ll.second - first == 4); + auto mm = boost::minmax_element(first, last, std::greater()); + CHECK(*mm.first == 9); + CHECK(*mm.second == 1); +} + +TEST_CASE("example: conv_example") { + std::string str1("AbCdEfG"); + std::vector vec1(str1.begin(), str1.end()); + std::string lowered; + boost::to_lower_copy(std::back_inserter(lowered), vec1); + CHECK(lowered == "abcdefg"); + CHECK(boost::to_upper_copy(str1) == "ABCDEFG"); + boost::to_lower(str1); + CHECK(str1 == "abcdefg"); +} + +TEST_CASE("example: find_example") { + std::string str1("abc___cde___efg"); + std::string str2("abc"); + boost::iterator_range range = + boost::find_first(str1, std::string("cde")); + boost::to_upper(range); + CHECK(str1 == "abc___CDE___efg"); + boost::iterator_range head = boost::find_head(str1, 3); + CHECK(std::string(head.begin(), head.end()) == "abc"); + head = boost::find_tail(str2, 5); + CHECK(std::string(head.begin(), head.end()) == "abc"); + char text[] = "hello dolly!"; + boost::iterator_range crange = boost::find_last(text, "ll"); + std::transform(crange.begin(), crange.end(), crange.begin(), + [](char c) { return static_cast(c + 1); }); + boost::to_upper(crange); + CHECK(std::string(text) == "hello doMMy!"); +} + +TEST_CASE("example: predicate_example") { + std::string str1("123xxx321"); + std::string str2("abc"); + CHECK(boost::starts_with(str1, std::string("123"))); + CHECK_FALSE(boost::ends_with(str1, std::string("123"))); + CHECK(boost::contains(str1, std::string("xxx"))); + CHECK(boost::equals(str2, std::string("abc"))); + CHECK(boost::all(";.,", boost::is_punct())); + CHECK(boost::all("abcxxx", boost::is_any_of("xabc") && !boost::is_space())); +} + +namespace { +std::string upcase_formatter(const boost::iterator_range& Replace) { + std::string Temp(Replace.begin(), Replace.end()); + boost::to_upper(Temp); + return Temp; +} +} // namespace + +TEST_CASE("example: replace_example") { + std::string str1("abc___cde___efg"); + CHECK(boost::erase_range_copy( + str1, boost::make_iterator_range(str1.begin() + 6, str1.begin() + 9)) == + "abc______efg"); + CHECK(boost::replace_range_copy( + str1, boost::make_iterator_range(str1.begin() + 6, str1.begin() + 9), "+++") == + "abc___+++___efg"); + std::string out; + boost::replace_first_copy(std::back_inserter(out), str1, "cde", "XYZ"); + CHECK(out == "abc___XYZ___efg"); + CHECK(boost::replace_all_copy(str1, "___", "---") == "abc---cde---efg"); + CHECK(boost::erase_all_copy(str1, "___") == "abccdeefg"); + boost::replace_nth(str1, "_", 4, "+"); + boost::replace_nth(str1, "_", 2, "+"); + CHECK(str1 == "abc__+cde_+_efg"); + std::string str2("abC-xxxx-AbC-xxxx-abc"); + CHECK(boost::find_format_all_copy(str2, boost::first_finder("abc", boost::is_iequal()), + upcase_formatter) == "ABC-xxxx-ABC-xxxx-ABC"); +} + +namespace rle { +template +T repeat_mark() { return (std::numeric_limits::max)(); } + +struct find_compressF { + template + boost::iterator_range operator()(ForwardIteratorT Begin, + ForwardIteratorT End) const { + using value_type = typename std::iterator_traits::value_type; + using result_type = boost::iterator_range; + ForwardIteratorT MStart = End; + value_type Cnt = 0; + for (ForwardIteratorT It = Begin; It != End;) { + ForwardIteratorT It2 = It++; + if (It == End || Cnt >= (std::numeric_limits::max)()) + return result_type(MStart, It); + if (*It == *It2) { + if (MStart == End) MStart = It2; + Cnt++; + } else if (MStart != End) { + if (Cnt > 2) return result_type(MStart, It); + MStart = End; + Cnt = 0; + } + } + return result_type(End, End); + } +}; + +template +struct format_compressF { + using result_type = SeqT; + using value_type = typename SeqT::value_type; + template + result_type operator()(const ReplaceT& Replace) const { + SeqT r; + if (!Replace.empty()) { + r.push_back(repeat_mark()); + r.push_back(*(Replace.begin())); + r.push_back(value_type(Replace.size())); + } + return r; + } +}; + +struct find_decompressF { + template + boost::iterator_range operator()(ForwardIteratorT Begin, + ForwardIteratorT End) const { + using value_type = typename std::iterator_traits::value_type; + for (ForwardIteratorT It = Begin; It != End; It++) { + if (*It == repeat_mark()) { + ForwardIteratorT It2 = It++; + if (It == End) break; + It++; + if (It == End) break; + It++; + return boost::iterator_range(It2, It); + } + } + return boost::iterator_range(End, End); + } +}; + +template +struct format_decompressF { + using result_type = SeqT; + using value_type = typename SeqT::value_type; + template + result_type operator()(const ReplaceT& Replace) const { + SeqT r; + if (!Replace.empty()) { + auto It = Replace.begin(); + value_type Value = *(++It); + value_type Repeat = *(++It); + for (value_type Index = 0; Index < Repeat; Index++) r.push_back(Value); + } + return r; + } +}; +} // namespace rle + +TEST_CASE("example: rle_example") { + const std::string source("123_AA_*ZZZZZZZZZZZZZZ*34"); + std::string original = source; + std::string compress = boost::find_format_all_copy(original, rle::find_compressF(), + rle::format_compressF()); + CHECK(compress.size() < original.size()); + CHECK(compress.find('Z') != std::string::npos); + std::string decompress = boost::find_format_all_copy( + compress, rle::find_decompressF(), rle::format_decompressF()); + CHECK(decompress == source); + boost::find_format_all(original, rle::find_compressF(), rle::format_compressF()); + CHECK(original == compress); + boost::find_format_all(original, rle::find_decompressF(), + rle::format_decompressF()); + CHECK(original == source); +} + +TEST_CASE("example: split_example") { + std::string str1("abc-*-ABC-*-aBc"); + using string_find_iterator = boost::find_iterator; + std::vector found; + for (string_find_iterator It = boost::make_find_iterator( + str1, boost::first_finder("abc", boost::is_iequal())); + It != string_find_iterator(); ++It) { + found.push_back(boost::copy_range(*It)); + std::transform(It->begin(), It->end(), It->begin(), + [](char c) { return static_cast(c + 1); }); + } + CHECK(found == std::vector{"abc", "ABC", "aBc"}); + CHECK(str1 == "bcd-*-BCD-*-bCd"); + std::vector ResultCopy; + boost::split(ResultCopy, str1, boost::is_any_of("-*"), boost::token_compress_on); + CHECK(ResultCopy == std::vector{"bcd", "BCD", "bCd"}); +} + +TEST_CASE("example: trim_example") { + std::string str1(" 1x x x x1 "); + std::string str2("<>trim<>"); + std::string str3("123abs343"); + CHECK(boost::trim_left_copy(str1) == "1x x x x1 "); + boost::trim_right(str1); + CHECK(str1 == " 1x x x x1"); + CHECK(boost::trim_copy_if(str2, boost::is_any_of("<>")) == "trim"); + CHECK(boost::trim_copy_if(str3, boost::is_digit()) == "abs"); +} diff --git a/tests/algorithm/include_string_regex.cpp b/tests/algorithm/include_string_regex.cpp new file mode 100644 index 00000000..abaa6932 --- /dev/null +++ b/tests/algorithm/include_string_regex.cpp @@ -0,0 +1,36 @@ +// boost.algorithm string/regex — pure include. The *_regex family is not in +// the module: gcc 16 rejects the boost::regex v5 abi-tag surface inside a +// module interface (tools/export_policy.toml [algorithm]), so it is consumed +// as headers. No import in this TU (gcc include+import ODR rule). Covers +// libs/algorithm/string/example/regex_example.cpp; regex's compiled TUs come +// from the package's base source list. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include +#include +#include + +TEST_CASE("example: regex_example") { + std::string str1("abc__(456)__123__(123)__cde"); + CHECK(boost::replace_all_regex_copy(str1, boost::regex("\\(([0-9]+)\\)"), + std::string("#$1#")) == "abc__#456#__123__#123#__cde"); + CHECK(boost::erase_all_regex_copy(str1, boost::regex("[[:alpha:]]+")) == + "__(456)__123__(123)__"); + boost::replace_all_regex(str1, boost::regex("_(\\([^\\)]*\\))_"), std::string("-$1-")); + CHECK(str1 == "abc_-(456)-_123_-(123)-_cde"); +} + +TEST_CASE("regex finder / find_regex / split_regex") { + std::string s("a1b22c333"); + auto r = boost::find_regex(s, boost::regex("[0-9]+")); + CHECK(std::string(r.begin(), r.end()) == "1"); + std::vector parts; + boost::split_regex(parts, s, boost::regex("[0-9]+")); + CHECK(parts == std::vector{"a", "b", "c", ""}); + std::vector nums; + boost::find_all_regex(nums, s, boost::regex("[0-9]+")); + CHECK(nums == std::vector{"1", "22", "333"}); + CHECK(boost::erase_regex_copy(s, boost::regex("[a-z]")) == "1b22c333"); +} diff --git a/tests/algorithm.cpp b/tests/algorithm/smoke.cpp similarity index 98% rename from tests/algorithm.cpp rename to tests/algorithm/smoke.cpp index 13de1143..733a4464 100644 --- a/tests/algorithm.cpp +++ b/tests/algorithm/smoke.cpp @@ -1,6 +1,6 @@ // mcpplibs.mwrap.boost.algorithm smoke — clamp, hex, minmax, is_palindrome, find, copy_if, // split/join and names the module re-exports from its dependencies -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.algorithm; diff --git a/tests/any/api.cpp b/tests/any/api.cpp new file mode 100644 index 00000000..de22705e --- /dev/null +++ b/tests/any/api.cpp @@ -0,0 +1,91 @@ +// mcpplibs.mwrap.boost.any — boost::any, boost::anys::basic_any and +// boost::anys::unique_any: construction, assignment, swap, the any_cast +// family (value, reference, pointer, rvalue), unsafe_any_cast, type queries. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.any; + +TEST_CASE("any: construct, assign, query") { + boost::any a; + CHECK(a.empty()); + CHECK(a.type() == typeid(void)); + a = 3; + CHECK_FALSE(a.empty()); + CHECK(a.type() == typeid(int)); + a = std::string("x"); + CHECK(a.type() == typeid(std::string)); + a.clear(); + CHECK(a.empty()); + boost::any b(std::vector{1, 2}); + boost::any c(std::move(b)); + CHECK(b.empty()); + CHECK(boost::any_cast&>(c).size() == 2); +} + +TEST_CASE("any: any_cast forms") { + boost::any a = std::string("abc"); + CHECK(boost::any_cast(a) == "abc"); + CHECK(boost::any_cast(a) == "abc"); + boost::any_cast(a) += "d"; + CHECK(*boost::any_cast(&a) == "abcd"); + const boost::any& ca = a; + CHECK(boost::any_cast(&ca)->size() == 4); + CHECK(boost::any_cast(&a) == nullptr); + CHECK_THROWS_AS(boost::any_cast(a), boost::bad_any_cast); + CHECK_THROWS_WITH_AS(boost::any_cast(a), doctest::Contains("any_cast"), + boost::bad_any_cast); + std::string moved = boost::any_cast(std::move(a)); + CHECK(moved == "abcd"); + boost::any n = 5; + CHECK(*boost::unsafe_any_cast(&n) == 5); +} + +TEST_CASE("any: swap") { + boost::any a = 1, b = std::string("two"); + boost::swap(a, b); + CHECK(boost::any_cast(a) == "two"); + CHECK(boost::any_cast(b) == 1); + a.swap(b); + CHECK(boost::any_cast(a) == 1); +} + +TEST_CASE("basic_any: small buffer and large values") { + using small_any = boost::anys::basic_any<16, 8>; + small_any s = 7; + CHECK(boost::anys::any_cast(s) == 7); + small_any big = std::array{}; + CHECK(boost::anys::any_cast&>(big).size() == 256); + small_any copy = big; + CHECK(copy.type() == typeid(std::array)); + small_any moved = std::move(s); + CHECK(boost::anys::any_cast(moved) == 7); + boost::anys::swap(moved, copy); + CHECK(moved.type() == typeid(std::array)); + CHECK(boost::anys::any_cast(©) != nullptr); + CHECK(*boost::anys::unsafe_any_cast(©) == 7); + CHECK_THROWS_AS(boost::anys::any_cast(copy), boost::bad_any_cast); + copy.clear(); + CHECK(copy.empty()); +} + +TEST_CASE("unique_any: in_place, emplace, reset, from boost::any") { + boost::anys::unique_any u(boost::anys::in_place_type>, 3, 9); + CHECK(boost::anys::any_cast&>(u) == std::vector{9, 9, 9}); + auto& s = u.emplace("hi"); + s += "!"; + CHECK(boost::anys::any_cast(u) == "hi!"); + CHECK(u.type() == typeid(std::string)); + u.emplace>({1, 2, 3}); + CHECK(boost::anys::any_cast&>(u).size() == 3); + u.reset(); + CHECK_FALSE(u.has_value()); + boost::anys::unique_any from_any(boost::any(5)); + CHECK(boost::anys::any_cast(from_any) == 5); + boost::anys::unique_any other = std::move(from_any); + CHECK(boost::anys::any_cast(other) == 5); + boost::anys::unique_any w; + w.swap(other); + CHECK(w.has_value()); + CHECK_FALSE(other.has_value()); +} diff --git a/tests/any/coverage.toml b/tests/any/coverage.toml new file mode 100644 index 00000000..025aafbc --- /dev/null +++ b/tests/any/coverage.toml @@ -0,0 +1,16 @@ +# tests/any/coverage.toml — official examples and documentation snippets of +# Boost.any 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "any" + +[[snippet]] +doc = "libs/any/doc/any.qbk#c-20-module" +title = "C++20 module" +case = "any/examples:doc: c-20-module" + +[[snippet]] +doc = "libs/any/doc/any.qbk#examples" +title = "Examples" +case = "any/examples:doc: examples" diff --git a/tests/any/custom_types.cpp b/tests/any/custom_types.cpp new file mode 100644 index 00000000..fddb2113 --- /dev/null +++ b/tests/any/custom_types.cpp @@ -0,0 +1,102 @@ +// mcpplibs.mwrap.boost.any — user-defined value types in boost::any, +// basic_any and unique_any: the ValueType requirements (copyable for any, +// move-only allowed for unique_any), small-object storage vs heap storage, +// a throwing copy constructor leaving the target unchanged, and destructors +// running exactly once. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.any; + +namespace user { +inline int live = 0; +struct tracked { + int id; + explicit tracked(int i) : id(i) { ++live; } + tracked(const tracked& o) : id(o.id) { ++live; } + tracked(tracked&& o) noexcept : id(o.id) { ++live; } + tracked& operator=(const tracked&) = default; + ~tracked() { --live; } +}; + +struct move_only { + std::unique_ptr p; + explicit move_only(int v) : p(std::make_unique(v)) {} + move_only(move_only&&) noexcept = default; + move_only& operator=(move_only&&) noexcept = default; +}; + +struct throwing_copy { + int v = 0; + throwing_copy() = default; + throwing_copy(const throwing_copy&) { throw std::runtime_error("copy"); } + throwing_copy(throwing_copy&&) noexcept = default; +}; + +struct big { + std::array data{}; + std::string label; +}; +} // namespace user + +TEST_CASE("any: user type copy/move and lifetime") { + user::live = 0; + { + boost::any a = user::tracked(1); + CHECK(user::live == 1); + boost::any b = a; + CHECK(user::live == 2); + CHECK(boost::any_cast(b).id == 1); + a = user::tracked(2); + CHECK(boost::any_cast(a).id == 2); + CHECK(user::live == 2); + } + CHECK(user::live == 0); +} + +TEST_CASE("any: throwing copy keeps the target (strong guarantee)") { + boost::any target = 5; + user::throwing_copy src; + CHECK_THROWS_AS(target = src, std::runtime_error); + CHECK(boost::any_cast(target) == 5); + target = user::throwing_copy{}; // move: does not throw + CHECK(target.type() == typeid(user::throwing_copy)); +} + +TEST_CASE("basic_any: user types below and above the buffer size") { + using any16 = boost::anys::basic_any<16, 8>; + user::live = 0; + { + any16 small = user::tracked(3); + any16 large = user::big{{}, "big"}; + CHECK(boost::anys::any_cast(small).id == 3); + CHECK(boost::anys::any_cast(large).label == "big"); + any16 copy = small; + CHECK(user::live == 2); + boost::anys::swap(small, large); + CHECK(boost::anys::any_cast(small).label == "big"); + CHECK(boost::anys::any_cast(large).id == 3); + } + CHECK(user::live == 0); +} + +TEST_CASE("unique_any: move-only user type") { + boost::anys::unique_any u = user::move_only(9); + CHECK(*boost::anys::any_cast(u).p == 9); + boost::anys::unique_any v = std::move(u); + CHECK_FALSE(u.has_value()); + user::move_only out = boost::anys::any_cast(std::move(v)); + CHECK(*out.p == 9); + auto& e = v.emplace(4); + CHECK(*e.p == 4); + // unique_any accepts what boost::any (CopyConstructible values only) cannot. + static_assert(std::is_constructible_v); +} + +TEST_CASE("containers of any holding user types") { + std::vector bag{user::big{{}, "a"}, std::string("b"), 3}; + std::size_t bigs = std::count_if(bag.begin(), bag.end(), [](const boost::any& x) { + return boost::any_cast(&x) != nullptr; + }); + CHECK(bigs == 1); +} diff --git a/tests/any/examples.cpp b/tests/any/examples.cpp new file mode 100644 index 00000000..81e1d7c0 --- /dev/null +++ b/tests/any/examples.cpp @@ -0,0 +1,103 @@ +// mcpplibs.mwrap.boost.any — the documentation examples of Boost.Any +// (libs/any/doc/any.qbk, sections "Examples" and "C++20 module") as test cases. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.any; + +using boost::any_cast; +using many = std::list; + +namespace { +void append_int(many& values, int value) { + boost::any to_append = value; + values.push_back(to_append); +} +void append_string(many& values, const std::string& value) { values.push_back(value); } +void append_char_ptr(many& values, const char* value) { values.push_back(value); } +void append_any(many& values, const boost::any& value) { values.push_back(value); } +void append_nothing(many& values) { values.push_back(boost::any()); } + +bool is_empty(const boost::any& operand) { return operand.empty(); } +bool is_int(const boost::any& operand) { return operand.type() == typeid(int); } +bool is_char_ptr(const boost::any& operand) { + try { + any_cast(operand); + return true; + } catch (const boost::bad_any_cast&) { + return false; + } +} +bool is_string(const boost::any& operand) { return any_cast(&operand); } + +void count_all(many& values, std::ostream& out) { + out << "#empty == " << std::count_if(values.begin(), values.end(), is_empty) << '\n'; + out << "#int == " << std::count_if(values.begin(), values.end(), is_int) << '\n'; + out << "#const char * == " << std::count_if(values.begin(), values.end(), is_char_ptr) << '\n'; + out << "#string == " << std::count_if(values.begin(), values.end(), is_string) << '\n'; +} + +struct property { + property() = default; + property(const std::string& n, const boost::any& v) : name(n), value(v) {} + std::string name; + boost::any value; +}; +using properties = std::list; + +class consumer { +public: + virtual ~consumer() = default; + virtual void notify(const boost::any&) = 0; +}; +class recorder : public consumer { +public: + void notify(const boost::any& a) override { seen.push_back(a.type().name()); } + std::vector seen; +}; +} // namespace + +TEST_CASE("doc: examples") { + many values; + append_int(values, 1); + append_string(values, "two"); + append_char_ptr(values, "three"); + append_any(values, boost::any(4.0)); + append_nothing(values); + std::ostringstream out; + count_all(values, out); + CHECK(out.str() == "#empty == 1\n#int == 1\n#const char * == 1\n#string == 1\n"); + + properties props{{"width", 10}, {"title", std::string("box")}}; + CHECK(any_cast(props.front().value) == 10); + CHECK(any_cast(props.back().value) == "box"); + + recorder r; + consumer& c = r; + c.notify(42); + c.notify(std::string("s")); + CHECK(r.seen.size() == 2); + CHECK(r.seen[0] == typeid(int).name()); + + // unique_any carrying optional user data from an authorizer to a handler. + struct user_info { std::string login; }; + auto authorizer = [](const std::map& headers, + boost::anys::unique_any& user_data) { + auto it = headers.find("user"); + if (it == headers.end()) return false; + user_data = user_info{it->second}; + return true; + }; + boost::anys::unique_any user_data; + REQUIRE(authorizer({{"user", "alice"}}, user_data)); + CHECK(any_cast(user_data).login == "alice"); + boost::anys::unique_any none; + CHECK_FALSE(authorizer({}, none)); + CHECK_FALSE(none.has_value()); +} + +TEST_CASE("doc: c-20-module") { + // libs/any/modules/usage_sample.cpp, against this package's module. + boost::any a = 42; + CHECK(any_cast(a) == 42); +} diff --git a/tests/any.cpp b/tests/any/smoke.cpp similarity index 96% rename from tests/any.cpp rename to tests/any/smoke.cpp index 21d61fb5..af2dc2e2 100644 --- a/tests/any.cpp +++ b/tests/any/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.any smoke — any, any_cast (value/ref/pointer), empty -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.any; diff --git a/tests/asio/coverage.toml b/tests/asio/coverage.toml new file mode 100644 index 00000000..51851d52 --- /dev/null +++ b/tests/asio/coverage.toml @@ -0,0 +1,1042 @@ +# tests/asio/coverage.toml — official examples and documentation snippets of +# Boost.asio 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "asio" + +[[example]] +path = "libs/asio/example/cpp11/allocation/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/buffers/reference_counted.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/chat/chat_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/chat/chat_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/chat/posix_chat_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/deferred/deferred_1.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/deferred/deferred_2.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/echo/async_tcp_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/echo/async_udp_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/echo/blocking_tcp_echo_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/echo/blocking_tcp_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/echo/blocking_udp_echo_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/echo/blocking_udp_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/executors/actor.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/executors/bank_account_1.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/executors/bank_account_2.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/executors/fork_join.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/executors/pipeline.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/executors/priority_scheduler.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/files/async_file_copy.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/files/blocking_file_copy.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/fork/daemon.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/fork/process_per_connection.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/futures/daytime_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/handler_tracking/async_tcp_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/client/async_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/client/sync_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server/connection.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server/connection_manager.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server/main.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server/mime_types.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server/reply.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server/request_handler.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server/request_parser.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server2/connection.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server2/io_context_pool.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server2/main.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server2/mime_types.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server2/reply.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server2/request_handler.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server2/request_parser.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server2/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server3/connection.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server3/main.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server3/mime_types.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server3/reply.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server3/request_handler.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server3/request_parser.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server3/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server4/file_handler.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server4/main.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server4/mime_types.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server4/reply.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server4/request_handler.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server4/request_parser.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/http/server4/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/icmp/ping.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/invocation/prioritised_handlers.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/iostreams/daytime_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/iostreams/daytime_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/iostreams/http_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/local/connect_pair.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/local/fd_passing_stream_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/local/fd_passing_stream_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/local/iostream_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/local/stream_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/local/stream_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/multicast/receiver.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/multicast/sender.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/nonblocking/third_party_lib.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/operations/composed_1.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/operations/composed_2.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/operations/composed_3.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/operations/composed_4.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/operations/composed_5.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/operations/composed_6.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/operations/composed_7.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/operations/composed_8.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/parallel_group/ranged_wait_for_all.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/parallel_group/wait_for_all.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/parallel_group/wait_for_one.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/parallel_group/wait_for_one_error.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/parallel_group/wait_for_one_success.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/porthopper/client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/porthopper/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/serialization/client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/serialization/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/services/daytime_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/services/logger_service.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/socks4/sync_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/spawn/echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/spawn/parallel_grep.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/ssl/client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/ssl/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/timeouts/async_tcp_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/timeouts/blocking_tcp_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/timeouts/blocking_token_tcp_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/timeouts/blocking_udp_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/timeouts/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/timers/time_t_timer.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/daytime1/client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/daytime2/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/daytime3/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/daytime4/client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/daytime5/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/daytime6/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/daytime7/server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/timer1/timer.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/timer2/timer.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/timer3/timer.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/timer4/timer.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/tutorial/timer5/timer.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/type_erasure/main.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/type_erasure/sleep.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/type_erasure/stdin_line_reader.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp11/windows/transmit_file.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/deferred/deferred_1.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/deferred/deferred_2.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/deferred/deferred_3.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/deferred/deferred_4.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/deferred/deferred_5.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/deferred/deferred_6.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/deferred/deferred_7.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/echo/async_tcp_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/echo/async_udp_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/echo/blocking_tcp_echo_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/echo/blocking_tcp_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/echo/blocking_udp_echo_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/echo/blocking_udp_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/executors/actor.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/executors/async_1.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/executors/async_2.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/executors/bank_account_1.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/executors/bank_account_2.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/executors/fork_join.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/executors/pipeline.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/executors/priority_scheduler.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/iostreams/http_client.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/c_callback_wrapper.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/callback_wrapper.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/composed_1.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/composed_2.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/composed_3.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/composed_4.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/composed_5.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/composed_6.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/composed_7.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/operations/composed_8.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/parallel_group/parallel_sort.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/parallel_group/ranged_wait_for_all.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/parallel_group/wait_for_all.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/parallel_group/wait_for_one.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/parallel_group/wait_for_one_error.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp14/parallel_group/wait_for_one_success.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp17/coroutines_ts/chat_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp17/coroutines_ts/echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp17/coroutines_ts/echo_server_with_as_single_default.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp17/coroutines_ts/echo_server_with_as_tuple_default.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp17/coroutines_ts/echo_server_with_default.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp17/coroutines_ts/range_based_for.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp17/coroutines_ts/refactored_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/channels/mutual_exclusion_1.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/channels/mutual_exclusion_2.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/channels/throttling_proxy.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/chat_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/echo_server_with_as_single_default.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/echo_server_with_as_tuple_default.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/echo_server_with_default.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/echo_server_with_deferred.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/echo_server_with_deferred_default.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/refactored_echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/timeout_cancel_after.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/coroutines/timeout_watchdog.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/invocation/completion_executor.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/c_callback_wrapper.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/callback_wrapper.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/composed_1.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/composed_2.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/composed_3.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/composed_4.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/composed_5.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/composed_6.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/composed_7.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/operations/composed_8.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/type_erasure/main.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/type_erasure/sleep.cpp" +status = "todo" + +[[example]] +path = "libs/asio/example/cpp20/type_erasure/stdin_line_reader.cpp" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/allocation.qbk#allocation" +title = "Custom Memory Allocation" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/buffers.qbk#buffers" +title = "Buffers" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/cancellation.qbk#cancellation" +title = "Per-Operation Cancellation" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/channels.qbk#channels" +title = "Channels" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/compose.qbk#compose" +title = "Compositions as Asynchronous Operations" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/configuration.qbk#configuration" +title = "Runtime Configuration and Concurrency Hints" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/coro.qbk#coro" +title = "Resumable C++ 20 Coroutines" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/coroutine.qbk#coroutine" +title = "Stackless Coroutines" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/cpp2011.qbk#move_handlers" +title = "Movable Handlers" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/cpp2011.qbk#move_objects" +title = "Movable I/O Objects" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/cpp20_coroutines.qbk#cpp20_coroutines" +title = "C++20 Coroutines Support" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/deferred.qbk#deferred" +title = "Deferred Operations" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/files.qbk#files" +title = "Files" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/futures.qbk#futures" +title = "Futures" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/handler_tracking.qbk#handler_tracking" +title = "Handler Tracking" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/immediate_completion.qbk#immediate_completion" +title = "Customising Immediate Completion" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/implementation.qbk#implementation" +title = "Platform-Specific Implementation Notes" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/iostreams.qbk#iostreams" +title = "Socket Iostreams" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/line_based.qbk#line_based" +title = "Line-Based Operations" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/model/completion_tokens.qbk#completion_tokens" +title = "Completion Tokens" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/parallel_group.qbk#parallel_group" +title = "Co-ordinating Parallel Operations" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/pipes.qbk#pipes" +title = "Pipes" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/posix.qbk#fork" +title = "Fork" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/promises.qbk#promises" +title = "Promises" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/protocols.qbk#protocols" +title = "TCP, UDP and ICMP" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/reactor.qbk#reactor" +title = "Reactor-Style Operations" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/signals.qbk#signals" +title = "Signal Handling" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/spawn.qbk#spawn" +title = "Stackful Coroutines" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/strands.qbk#strands" +title = "Strands: Use Threads Without Explicit Locking" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/token_adapters.qbk#token_adapters" +title = "Completion Token Adapters" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/overview/type_erasure.qbk#type_erasure" +title = "Type Erasure, Separate Compilation and Virtual Functions" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/AcceptToken.qbk#AcceptToken" +title = "Accept token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/BufferedHandshakeToken.qbk#BufferedHandshakeToken" +title = "SSL buffered handshake token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/CancellationHandler.qbk#CancellationHandler" +title = "Cancellation handler requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/ConnectToken.qbk#ConnectToken" +title = "Connect token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/ConstBufferSequence.qbk#ConstBufferSequence" +title = "Constant buffer sequence requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/Executor.qbk#Executor1" +title = "Executor requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/HandshakeToken.qbk#HandshakeToken" +title = "SSL handshake token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/IoObjectService.qbk#IoObjectService" +title = "I/O object service requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/IteratorConnectToken.qbk#IteratorConnectToken" +title = "Iterator connect token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/MoveAcceptToken.qbk#MoveAcceptToken" +title = "Move accept token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/MutableBufferSequence.qbk#MutableBufferSequence" +title = "Mutable buffer sequence requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/NullaryToken.qbk#NullaryToken" +title = "Nullary token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/RangeConnectToken.qbk#RangeConnectToken" +title = "Range connect token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/ReadToken.qbk#ReadToken" +title = "Read token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/ResolveToken.qbk#ResolveToken" +title = "Resolve token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/Service.qbk#Service" +title = "Service requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/ShutdownToken.qbk#ShutdownToken" +title = "SSL shutdown token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/SignalToken.qbk#SignalToken" +title = "Signal token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/SyncRandomAccessReadDevice.qbk#SyncRandomAccessReadDevice" +title = "Buffer-oriented synchronous random-access" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/SyncRandomAccessWriteDevice.qbk#SyncRandomAccessWriteDevice" +title = "Buffer-oriented synchronous random-access" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/WaitToken.qbk#WaitToken" +title = "Wait token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/WriteToken.qbk#WriteToken" +title = "Write token requirements" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/requirements/asynchronous_operations.qbk#asynchronous_operations" +title = "Requirements on asynchronous operations" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/std_executors.qbk#std_executors" +title = "Proposed Standard Executors" +status = "todo" + +[[snippet]] +doc = "libs/asio/doc/using.qbk#using" +title = "Using, Building, and Configuring Boost.Asio" +status = "todo" diff --git a/tests/asio.cpp b/tests/asio/smoke.cpp similarity index 95% rename from tests/asio.cpp rename to tests/asio/smoke.cpp index b831e8ea..caef33e5 100644 --- a/tests/asio.cpp +++ b/tests/asio/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.asio smoke — io_context + steady_timer + post (no sockets, no winsock) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.asio; diff --git a/tests/beast/coverage.toml b/tests/beast/coverage.toml new file mode 100644 index 00000000..4409660e --- /dev/null +++ b/tests/beast/coverage.toml @@ -0,0 +1,318 @@ +# tests/beast/coverage.toml — official examples and documentation snippets of +# Boost.beast 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "beast" + +[[example]] +path = "libs/beast/example/advanced/server-flex-awaitable/advanced_server_flex_awaitable.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/advanced/server-flex/advanced_server_flex.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/advanced/server/advanced_server.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/doc/ssl/client.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/doc/ssl/server.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/echo-op/echo_op.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/async-local/http_client_async_local.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/async-ssl-system-executor/http_client_async_ssl_system_executor.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/async-ssl/http_client_async_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/async/http_client_async.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/awaitable-ssl/http_client_awaitable_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/awaitable/http_client_awaitable.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/body/json_client.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/coro-ssl/http_client_coro_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/coro/http_client_coro.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/crawl/http_crawl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/crawl/urls_large_data.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/methods/http_client_methods.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/sync-ssl/http_client_sync_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/client/sync/http_client_sync.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/async-local/http_server_async_local.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/async-ssl/http_server_async_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/async/http_server_async.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/awaitable/http_server_awaitable.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/coro-ssl/http_server_coro_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/coro/http_server_coro.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/fast/http_server_fast.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/flex/http_server_flex.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/small/http_server_small.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/stackless/http_server_stackless.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/sync-ssl/http_server_sync_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/http/server/sync/http_server_sync.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/client/async-local/websocket_client_async_local.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/client/async-ssl-system-executor/websocket_client_async_ssl_system_executor.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/client/async/websocket_client_async.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/client/awaitable/websocket_client_awaitable.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/client/coro/websocket_client_coro.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/client/sync/websocket_client_sync.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/async-local/websocket_server_async_local.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/async/websocket_server_async.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/awaitable/websocket_server_awaitable.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/chat-multi/http_session.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/chat-multi/listener.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/chat-multi/main.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/chat-multi/shared_state.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/chat-multi/websocket_session.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/coro/websocket_server_coro.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/fast/websocket_server_fast.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/stackless/websocket_server_stackless.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/sync-ssl/websocket_server_sync_ssl.cpp" +status = "todo" + +[[example]] +path = "libs/beast/example/websocket/server/sync/websocket_server_sync.cpp" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/03_core/9_ssl_tls_certificate.qbk#ssl-tls-certificate" +title = "SSL/TLS Certificate" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/03_core/_core.qbk#using_io" +title = "Networking" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/04_http/01_primer.qbk#protocol-primer" +title = "Protocol Primer" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/04_http/02_message.qbk#message-containers" +title = "Message Containers" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/04_http/04_serializer_streams.qbk#serializer-stream-operations" +title = "Serializer Stream Operations" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/04_http/05_parser_streams.qbk#parser-stream-operations" +title = "Parser Stream Operations" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/04_http/08_chunked_encoding.qbk#chunked-encoding" +title = "Chunked Encoding" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/04_http/09_custom_body.qbk#custom-body-types" +title = "Custom Body Types" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/06_websocket/02_handshaking.qbk#handshaking" +title = "Handshaking" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/06_websocket/_websocket.qbk#using_websocket" +title = "WebSocket" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/07_concepts/Body.qbk#Body" +title = "Body" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/07_concepts/BuffersGenerator.qbk#BuffersGenerator" +title = "BuffersGenerator" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/08_design/1_http_message.qbk#http_message_container" +title = "HTTP Message Container __video__" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/08_design/2_http_comparison.qbk#http-comparison-to-other-libraries" +title = "HTTP Comparison to Other Libraries" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/08_design/3_websocket_zaphoyd.qbk#comparison-to-zaphoyd-studios-websocket" +title = "Comparison to Zaphoyd Studios WebSocket++" +status = "todo" + +[[snippet]] +doc = "libs/beast/doc/qbk/08_design/4_faq.qbk#faq" +title = "FAQ" +status = "todo" diff --git a/tests/beast.cpp b/tests/beast/smoke.cpp similarity index 96% rename from tests/beast.cpp rename to tests/beast/smoke.cpp index 9ff6bb2b..1904648b 100644 --- a/tests/beast.cpp +++ b/tests/beast/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.beast smoke — flat_buffer + http request/response types (no sockets) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.beast; diff --git a/tests/bimap/coverage.toml b/tests/bimap/coverage.toml new file mode 100644 index 00000000..94fb5877 --- /dev/null +++ b/tests/bimap/coverage.toml @@ -0,0 +1,179 @@ +# tests/bimap/coverage.toml — official examples and documentation snippets of +# Boost.bimap 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "bimap" + +[[example]] +path = "libs/bimap/example/at_function_examples.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/bimap_and_boost/assign.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/bimap_and_boost/foreach.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/bimap_and_boost/lambda.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/bimap_and_boost/property_map.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/bimap_and_boost/range.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/bimap_and_boost/serialization.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/bimap_and_boost/typeof.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/bimap_and_boost/xpressive.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/mi_to_b_path/bidirectional_map.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/mi_to_b_path/hashed_indices.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/mi_to_b_path/mi_bidirectional_map.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/mi_to_b_path/mi_hashed_indices.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/mi_to_b_path/tagged_bidirectional_map.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/mighty_bimap.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/population_bimap.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/projection.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/repetitions_counter.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/simple_bimap.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/standard_map_comparison.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/step_by_step.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/tagged_simple_bimap.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/tutorial_info_hook.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/tutorial_modify_and_replace.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/tutorial_range.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/unconstrained_collection.cpp" +status = "todo" + +[[example]] +path = "libs/bimap/example/user_defined_names.cpp" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/bimap_and_boost.qbk#boost-typeof" +title = "Boost.Typeof" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/quick_tutorial.qbk#one-minute-tutorial" +title = "One minute tutorial" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/rationale.qbk#general-design" +title = "General Design" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/rationale.qbk#the-student-and-the-mentor" +title = "The student and the mentor" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/tutorial.qbk#bimap-mapping-framework" +title = "Bimap mapping framework" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/tutorial.qbk#bimaps-with-user-defined-names" +title = "Bimaps with user defined names" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/tutorial.qbk#complete-instantiation-scheme" +title = "Complete instantiation scheme" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/tutorial.qbk#examples" +title = "Examples" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/tutorial.qbk#examples-2" +title = "Examples" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/tutorial.qbk#freedom-of-choice" +title = "Freedom of choice" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/tutorial.qbk#insertion" +title = "Insertion" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/tutorial.qbk#iterator-value-type" +title = "iterator::value_type" +status = "todo" + +[[snippet]] +doc = "libs/bimap/doc/tutorial.qbk#operator-and-at" +title = "operator\\[\\] and at()" +status = "todo" diff --git a/tests/bimap.cpp b/tests/bimap/smoke.cpp similarity index 95% rename from tests/bimap.cpp rename to tests/bimap/smoke.cpp index dc43348e..9ca77e10 100644 --- a/tests/bimap.cpp +++ b/tests/bimap/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.bimap smoke — bidirectional map -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.bimap; diff --git a/tests/bloom/coverage.toml b/tests/bloom/coverage.toml new file mode 100644 index 00000000..fecdb2ab --- /dev/null +++ b/tests/bloom/coverage.toml @@ -0,0 +1,22 @@ +# tests/bloom/coverage.toml — official examples and documentation snippets of +# Boost.bloom 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "bloom" + +[[example]] +path = "libs/bloom/example/basic.cpp" +status = "todo" + +[[example]] +path = "libs/bloom/example/genome.cpp" +status = "todo" + +[[example]] +path = "libs/bloom/example/rolling_filter.cpp" +status = "todo" + +[[example]] +path = "libs/bloom/example/serialization.cpp" +status = "todo" diff --git a/tests/bloom.cpp b/tests/bloom/smoke.cpp similarity index 93% rename from tests/bloom.cpp rename to tests/bloom/smoke.cpp index 5c9b89f3..8685cdee 100644 --- a/tests/bloom.cpp +++ b/tests/bloom/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.bloom smoke — bloom_filter add/probably_contains -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.bloom; diff --git a/tests/callable_traits/coverage.toml b/tests/callable_traits/coverage.toml new file mode 100644 index 00000000..4d684f51 --- /dev/null +++ b/tests/callable_traits/coverage.toml @@ -0,0 +1,174 @@ +# tests/callable_traits/coverage.toml — official examples and documentation snippets of +# Boost.callable_traits 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "callable_traits" + +[[example]] +path = "libs/callable_traits/example/add_member_const.cpp" +case = "callable_traits/examples:example: add_member_const" + +[[example]] +path = "libs/callable_traits/example/add_member_cv.cpp" +case = "callable_traits/examples:example: add_member_cv" + +[[example]] +path = "libs/callable_traits/example/add_member_lvalue_reference.cpp" +case = "callable_traits/examples:example: add_member_lvalue_reference" + +[[example]] +path = "libs/callable_traits/example/add_member_rvalue_reference.cpp" +case = "callable_traits/examples:example: add_member_rvalue_reference" + +[[example]] +path = "libs/callable_traits/example/add_member_volatile.cpp" +case = "callable_traits/examples:example: add_member_volatile" + +[[example]] +path = "libs/callable_traits/example/add_noexcept.cpp" +case = "callable_traits/examples:example: add_noexcept" + +[[example]] +path = "libs/callable_traits/example/add_transaction_safe.cpp" +status = "excluded" +reason = "transaction_safe function types need GCC's -fgnu-tm (Transactional Memory TS); without it upstream compiles this example to an empty main" + +[[example]] +path = "libs/callable_traits/example/add_varargs.cpp" +case = "callable_traits/examples:example: add_varargs" + +[[example]] +path = "libs/callable_traits/example/apply_member_pointer.cpp" +case = "callable_traits/examples:example: apply_member_pointer" + +[[example]] +path = "libs/callable_traits/example/args.cpp" +case = "callable_traits/examples:example: args" + +[[example]] +path = "libs/callable_traits/example/class_of.cpp" +case = "callable_traits/examples:example: class_of" + +[[example]] +path = "libs/callable_traits/example/function_type.cpp" +case = "callable_traits/examples:example: function_type" + +[[example]] +path = "libs/callable_traits/example/function_types_remove_const_comparison.cpp" +case = "callable_traits/examples:example: function_types_remove_const_comparison" + +[[example]] +path = "libs/callable_traits/example/has_member_qualifiers.cpp" +case = "callable_traits/examples:example: has_member_qualifiers" + +[[example]] +path = "libs/callable_traits/example/has_varargs.cpp" +case = "callable_traits/examples:example: has_varargs" + +[[example]] +path = "libs/callable_traits/example/has_void_return.cpp" +case = "callable_traits/examples:example: has_void_return" + +[[example]] +path = "libs/callable_traits/example/intro.cpp" +case = "callable_traits/examples:example: intro" + +[[example]] +path = "libs/callable_traits/example/is_const_member.cpp" +case = "callable_traits/examples:example: is_const_member" + +[[example]] +path = "libs/callable_traits/example/is_cv_member.cpp" +case = "callable_traits/examples:example: is_cv_member" + +[[example]] +path = "libs/callable_traits/example/is_invocable.cpp" +case = "callable_traits/examples:example: is_invocable" + +[[example]] +path = "libs/callable_traits/example/is_lvalue_reference_member.cpp" +case = "callable_traits/examples:example: is_lvalue_reference_member" + +[[example]] +path = "libs/callable_traits/example/is_noexcept.cpp" +case = "callable_traits/examples:example: is_noexcept" + +[[example]] +path = "libs/callable_traits/example/is_reference_member.cpp" +case = "callable_traits/examples:example: is_reference_member" + +[[example]] +path = "libs/callable_traits/example/is_rvalue_reference_member.cpp" +case = "callable_traits/examples:example: is_rvalue_reference_member" + +[[example]] +path = "libs/callable_traits/example/is_transaction_safe.cpp" +status = "excluded" +reason = "transaction_safe function types need GCC's -fgnu-tm (Transactional Memory TS); without it upstream compiles this example to an empty main" + +[[example]] +path = "libs/callable_traits/example/is_volatile_member.cpp" +case = "callable_traits/examples:example: is_volatile_member" + +[[example]] +path = "libs/callable_traits/example/overview.cpp" +case = "callable_traits/examples:example: overview" + +[[example]] +path = "libs/callable_traits/example/qualified_class_of.cpp" +case = "callable_traits/examples:example: qualified_class_of" + +[[example]] +path = "libs/callable_traits/example/remove_member_const.cpp" +case = "callable_traits/examples:example: remove_member_const" + +[[example]] +path = "libs/callable_traits/example/remove_member_cv.cpp" +case = "callable_traits/examples:example: remove_member_cv" + +[[example]] +path = "libs/callable_traits/example/remove_member_reference.cpp" +case = "callable_traits/examples:example: remove_member_reference" + +[[example]] +path = "libs/callable_traits/example/remove_member_volatile.cpp" +case = "callable_traits/examples:example: remove_member_volatile" + +[[example]] +path = "libs/callable_traits/example/remove_noexcept.cpp" +case = "callable_traits/examples:example: remove_noexcept" + +[[example]] +path = "libs/callable_traits/example/remove_transaction_safe.cpp" +status = "excluded" +reason = "transaction_safe function types need GCC's -fgnu-tm (Transactional Memory TS); without it upstream compiles this example to an empty main" + +[[example]] +path = "libs/callable_traits/example/remove_varargs.cpp" +case = "callable_traits/examples:example: remove_varargs" + +[[example]] +path = "libs/callable_traits/example/return_type.cpp" +case = "callable_traits/examples:example: return_type" + +[[example]] +path = "libs/callable_traits/example/void.cpp" +case = "callable_traits/examples:example: void" + +[[snippet]] +doc = "libs/callable_traits/doc/callable_traits.qbk#boost_function_types" +title = "Regarding [^Boost.FunctionTypes]" +case = "callable_traits/examples:example: function_types_remove_const_comparison" +reason = "the section imports the example file" + +[[snippet]] +doc = "libs/callable_traits/doc/callable_traits.qbk#building" +title = "Building the test suite" +status = "excluded" +reason = "shell commands for building upstream's own test suite" + +[[snippet]] +doc = "libs/callable_traits/doc/callable_traits.qbk#faq" +title = "FAQ" +case = "callable_traits/custom_types:doc: faq" diff --git a/tests/callable_traits/custom_types.cpp b/tests/callable_traits/custom_types.cpp new file mode 100644 index 00000000..dfd53f9a --- /dev/null +++ b/tests/callable_traits/custom_types.cpp @@ -0,0 +1,116 @@ +// mcpplibs.mwrap.boost.callable_traits — user callables: member functions of a +// user class with every qualifier combination, user function objects +// (including generic and overloaded ones), user function pointers, and the +// FAQ's point (libs/callable_traits/doc/callable_traits.qbk#faq): one trait +// replaces the hand-written partial specializations per callable shape. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.callable_traits; + +namespace ct = boost::callable_traits; + +namespace user { +struct widget { + int get() const { return v; } + void set(int x) & { v = x; } + std::string name() && { return "temp"; } + double scale(double f) const volatile noexcept { return f; } + int printf_like(const char*, ...) { return 0; } + int v = 0; +}; + +struct adder { + long operator()(int a, int b) const { return a + b; } +}; + +struct stateful { + int calls = 0; + void operator()(std::string_view) { ++calls; } +}; + +inline int free_fn(float, char&) noexcept { return 1; } +} // namespace user + +TEST_CASE("member functions of a user class") { + using get_t = decltype(&user::widget::get); + static_assert(std::is_same_v, int>); + static_assert(std::is_same_v, user::widget>); + static_assert(std::is_same_v, const user::widget&>); + static_assert(ct::is_const_member::value); + static_assert(!ct::is_volatile_member::value); + static_assert(std::is_same_v, std::tuple>); + + using set_t = decltype(&user::widget::set); + static_assert(ct::is_lvalue_reference_member::value); + static_assert(ct::has_void_return::value); + static_assert(std::is_same_v, void (user::widget::*)(int)>); + + using name_t = decltype(&user::widget::name); + static_assert(ct::is_rvalue_reference_member::value); + static_assert(std::is_same_v, user::widget&&>); + + using scale_t = decltype(&user::widget::scale); + static_assert(ct::is_cv_member::value); + static_assert(ct::is_noexcept::value); + static_assert(std::is_same_v, + double (user::widget::*)(double) const volatile>); + static_assert(std::is_same_v>, + double (user::widget::*)(double)>); + + using var_t = decltype(&user::widget::printf_like); + static_assert(ct::has_varargs::value); + static_assert(std::is_same_v, int (user::widget::*)(const char*)>); + + // Rebuild a member pointer of a user class from a plain function type. + static_assert(std::is_same_v, int (user::widget::*)()>); + static_assert(std::is_same_v, + int (user::widget::*)() const>); + // And the traits agree with a call through the pointer. + user::widget w; + std::invoke(&user::widget::set, w, 4); + CHECK(std::invoke(&user::widget::get, w) == 4); + static_assert(ct::is_invocable_r::value); +} + +TEST_CASE("user function objects and function pointers") { + static_assert(std::is_same_v, std::tuple>); + static_assert(std::is_same_v, long>); + static_assert(std::is_same_v, long(int, int)>); + static_assert(!ct::is_const_member::value); + static_assert(std::is_same_v, std::tuple>); + static_assert(ct::is_noexcept::value); + static_assert(std::is_same_v, std::tuple>); + static_assert(std::is_same_v, int (*)(int) noexcept>); + auto lambda = [n = 2](double d) mutable { return d * n++; }; + static_assert(std::is_same_v, std::tuple>); + static_assert(!ct::is_const_member::value); + CHECK(lambda(1.5) == 3.0); +} + +namespace faq { +// What the FAQ calls the alternative: one specialization per shape. +template +struct arity_by_hand; +template +struct arity_by_hand : std::integral_constant {}; +template +struct arity_by_hand : arity_by_hand {}; +template +struct arity_by_hand : arity_by_hand {}; + +// With callable_traits, one definition covers every callable shape. +template +inline constexpr std::size_t arity = std::tuple_size_v>; +} // namespace faq + +TEST_CASE("doc: faq") { + static_assert(faq::arity_by_hand::value == 2); + static_assert(faq::arity == 2); + static_assert(faq::arity_by_hand::value == faq::arity); + // The member pointer form counts the object parameter, as args_t does. + static_assert(faq::arity == 1); + static_assert(faq::arity == 2); + static_assert(faq::arity == 2); + CHECK(faq::arity == 3); +} diff --git a/tests/callable_traits/examples.cpp b/tests/callable_traits/examples.cpp new file mode 100644 index 00000000..a1acb1bc --- /dev/null +++ b/tests/callable_traits/examples.cpp @@ -0,0 +1,952 @@ +// mcpplibs.mwrap.boost.callable_traits — the 37 official examples +// (libs/callable_traits/example/*.cpp) as test cases, each in its own +// namespace. They are compile-time checks: the test passes by compiling. +// The examples' feature guards are resolved for a C++23 compiler: +// BOOST_CLBL_TRTS_DISABLE_* and _MSVC are off, noexcept function types are on, +// and transaction_safe (GCC's -fgnu-tm) is off: the three *_transaction_safe +// examples compile to an empty main upstream then, and are excluded in +// coverage.toml. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.callable_traits; + +// libs/callable_traits/example/add_member_const.cpp +namespace ex_add_member_const { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: add_member_const") { + + { + using pmf = int(foo::*)(); + using expect = int(foo::*)() const; + using test = ct::add_member_const_t; + static_assert(std::is_same::value, ""); + } { + // add_member_const_t doesn't change anything when + // the function type is already const. + using pmf = int(foo::*)() const &&; + using expect = int(foo::*)() const &&; + using test = ct::add_member_const_t; + static_assert(std::is_same::value, ""); + } { + using pmf = int(foo::*)() volatile &; + using expect = int(foo::*)() const volatile &; + using test = ct::add_member_const_t; + static_assert(std::is_same::value, ""); + } { + // add_member_const_t can also be used with "abominable" + // function types. + using f = int(); + using expect = int() const; + using test = ct::add_member_const_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_add_member_const + +// libs/callable_traits/example/add_member_cv.cpp +namespace ex_add_member_cv { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: add_member_cv") { + + { + using pmf = void(foo::*)(); + using expect = void(foo::*)() const volatile; + using test = ct::add_member_cv_t; + static_assert(std::is_same::value, ""); + } { + // add_member_cv_t doesn't change anything when + // the function type is already cv-qualified. + using pmf = void(foo::*)() const volatile &&; + using expect = void(foo::*)() const volatile &&; + using test = ct::add_member_cv_t; + static_assert(std::is_same::value, ""); + } { + using pmf = void(foo::*)() volatile &; + using expect = void(foo::*)() const volatile &; + using test = ct::add_member_cv_t; + static_assert(std::is_same::value, ""); + } { + // add_member_cv_t can also be used with "abominable" + // function types. + using f = void(); + using expect = void() const volatile; + using test = ct::add_member_cv_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_add_member_cv + +// libs/callable_traits/example/add_member_lvalue_reference.cpp +namespace ex_add_member_lvalue_reference { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: add_member_lvalue_reference") { + + { + using pmf = void(foo::*)(); + using expect = void(foo::*)() &; + using test = ct::add_member_lvalue_reference_t; + static_assert(std::is_same::value, ""); + } { + // add_member_lvalue_reference_t doesn't change anything when + // the function type already has an lvalue qualifier. + using pmf = void(foo::*)() &; + using expect = void(foo::*)() &; + using test = ct::add_member_lvalue_reference_t; + static_assert(std::is_same::value, ""); + } { + // add_member_lvalue_reference_t models C++11 reference collapsing + // rules, so that adding an lvalue qualifier to an + // rvalue-qualified type will force the lvalue. + using pmf = void(foo::*)() &&; + using expect = void(foo::*)() &; + using test = ct::add_member_lvalue_reference_t; + static_assert(std::is_same::value, ""); + } { + // add_member_lvalue_reference_t can also be used to create "abominable" + // function types. + using f = void(); + using expect = void() &; + using test = ct::add_member_lvalue_reference_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_add_member_lvalue_reference + +// libs/callable_traits/example/add_member_rvalue_reference.cpp +namespace ex_add_member_rvalue_reference { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: add_member_rvalue_reference") { + + { + using pmf = void(foo::*)(); + using expect = void(foo::*)() &&; + using test = ct::add_member_rvalue_reference_t; + static_assert(std::is_same::value, ""); + } { + // add_member_rvalue_reference_t doesn't change anything when + // the function type already has an rvalue qualifier. + using pmf = void(foo::*)() &&; + using expect = void(foo::*)() &&; + using test = ct::add_member_rvalue_reference_t; + static_assert(std::is_same::value, ""); + } { + // add_member_rvalue_reference_t models C++11 reference collapsing + // rules, so that adding an rvalue qualifier to an + // lvalue-qualified type will not change anything. + using pmf = void(foo::*)() const &; + using expect = void(foo::*)() const &; + using test = ct::add_member_rvalue_reference_t; + static_assert(std::is_same::value, ""); + } { + // add_member_rvalue_reference_t can also be used with "abominable" + // function types. + using f = void() const; + using expect = void() const &&; + using test = ct::add_member_rvalue_reference_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_add_member_rvalue_reference + +// libs/callable_traits/example/add_member_volatile.cpp +namespace ex_add_member_volatile { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: add_member_volatile") { + + { + using pmf = void(foo::*)(); + using expect = void(foo::*)() volatile; + using test = ct::add_member_volatile_t; + static_assert(std::is_same::value, ""); + } { + // add_member_volatile_t doesn't change anything when + // the function type is already volatile. + using pmf = void(foo::*)() volatile &&; + using expect = void(foo::*)() volatile &&; + using test = ct::add_member_volatile_t; + static_assert(std::is_same::value, ""); + } { + using pmf = void(foo::*)() const &; + using expect = void(foo::*)() const volatile &; + using test = ct::add_member_volatile_t; + static_assert(std::is_same::value, ""); + } { + // add_member_volatile_t can also be used with "abominable" + // function types. + using f = void(); + using expect = void() volatile; + using test = ct::add_member_volatile_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_add_member_volatile + +// libs/callable_traits/example/add_noexcept.cpp +namespace ex_add_noexcept { +using boost::callable_traits::add_noexcept_t; + +static_assert(std::is_same< + add_noexcept_t, + int() noexcept +>{}, ""); + +TEST_CASE("example: add_noexcept") {} +} // namespace ex_add_noexcept + +// libs/callable_traits/example/add_varargs.cpp +namespace ex_add_varargs { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: add_varargs") { + + { + using f = void(int); + using expect = void(int, ...); + using test = ct::add_varargs_t; + static_assert(std::is_same::value, ""); + } { + using fp = void(*)(); + using expect = void(*)(...); + using test = ct::add_varargs_t; + static_assert(std::is_same::value, ""); + } { + using fr = void(&)(const char*); + using expect = void(&)(const char*, ...); + using test = ct::add_varargs_t; + static_assert(std::is_same::value, ""); + } { + using pmf = void(foo::*)() const; + using expect = void(foo::*)(...) const; + using test = ct::add_varargs_t; + static_assert(std::is_same::value, ""); + + // add_varargs_t doesn't change anything when + // the type already has varargs. + using twice = ct::add_varargs_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_add_varargs + +// libs/callable_traits/example/apply_member_pointer.cpp +namespace ex_apply_member_pointer { +namespace ct = boost::callable_traits; + +struct foo; +struct bar; + +TEST_CASE("example: apply_member_pointer") { + + { + // function type -> member function pointer type + using f = int(int); + using g = ct::apply_member_pointer_t; + using expect = int(foo::*)(int); + static_assert(std::is_same::value, ""); + } + + { + // function pointer type (unqualified) -> member function pointer type + using f = int(*)(); + using g = ct::apply_member_pointer_t; + using expect = int(foo::*)(); + static_assert(std::is_same::value, ""); + } + + { + // function pointer type (qualified) -> member data pointer type + + // Look out for cases like these two. If the input type to apply_member_pointer + // is a ``[*qualified]`` function pointer type, then the aliased type is a member data + // pointer to a ``[*qualified function pointer]``. + + { + using f = int(*&)(); + using g = ct::apply_member_pointer_t; + using expect = int (* foo::*)(); + static_assert(std::is_same::value, ""); + } + + { + using f = int(* const)(); + using g = ct::apply_member_pointer_t; + using expect = int (* const foo::*)(); + static_assert(std::is_same::value, ""); + } + } + + { + // function reference type -> member function pointer type + using f = void(&)(); + using g = ct::apply_member_pointer_t; + using expect = void(foo::*)(); + static_assert(std::is_same::value, ""); + } + + { + // member function pointer type -> member function pointer type + // (note the different parent class) + using f = int(bar::*)() const; + using g = ct::apply_member_pointer_t; + using expect = int(foo::*)() const; + static_assert(std::is_same::value, ""); + } + + { + // non-callable type -> member data pointer type + using g = ct::apply_member_pointer_t; + using expect = int foo::*; + static_assert(std::is_same::value, ""); + } + + { + // function object type -> member data pointer type + // the same is true for lambdas and generic lambdas + auto lambda = [](){}; + using f = decltype(lambda); + using g = ct::apply_member_pointer_t; + using expect = f foo::*; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_apply_member_pointer + +// libs/callable_traits/example/args.cpp +namespace ex_args { +namespace ct = boost::callable_traits; + +template +void test(){ + using args_t = ct::args_t; + static_assert(std::is_same::value, ""); +} + +TEST_CASE("example: args") { + + { + auto lamda = [](int, float&, const char*){}; + using lam = decltype(lamda); + using expect = std::tuple; + + test(); + } + + { + struct foo; + using pmf = void(foo::*)(int, float&, const char*); + using expect = std::tuple; + + test(); + } + + { + using function_ptr = void(*)(int, float&, const char*); + using expect = std::tuple; + test(); + } + + { + using function_ref = void(&)(int, float&, const char*); + using expect = std::tuple; + test(); + } + + { + using function = void(int, float&, const char*); + using expect = std::tuple; + test(); + } + + { + using abominable = void(int, float&, const char*) const; + using expect = std::tuple; + test(); + } +} +} // namespace ex_args + +// libs/callable_traits/example/class_of.cpp +namespace ex_class_of { +namespace ct = boost::callable_traits; + +struct foo; + +static_assert(std::is_same>::value, ""); +static_assert(std::is_same>::value, ""); + +TEST_CASE("example: class_of") {} +} // namespace ex_class_of + +// libs/callable_traits/example/function_type.cpp +namespace ex_function_type { +namespace ct = boost::callable_traits; + +template +void test(){ + + // this example shows how boost::callable_traits::function_type_t + // bevaves consistently for many different types + using type = ct::function_type_t; + using expect = void(int, float&, const char*); + static_assert(std::is_same{}, ""); +} + +TEST_CASE("example: function_type") { + + auto lamda = [](int, float&, const char*){}; + using lam = decltype(lamda); + test(); + + using function_ptr = void(*)(int, float&, const char*); + test(); + + using function_ref = void(&)(int, float&, const char*); + test(); + + using function = void(int, float&, const char*); + test(); + + using abominable = void(int, float&, const char*) const; + test(); +} +} // namespace ex_function_type + +// libs/callable_traits/example/function_types_remove_const_comparison.cpp +namespace ex_function_types_remove_const_comparison { +struct foo { + void bar() const {} +}; + +using const_removed = boost::callable_traits::remove_member_const_t; + +static_assert(std::is_same::value, ""); + +TEST_CASE("example: function_types_remove_const_comparison") {} +} // namespace ex_function_types_remove_const_comparison + +// libs/callable_traits/example/has_member_qualifiers.cpp +namespace ex_has_member_qualifiers { +namespace ct = boost::callable_traits; + +struct foo; + +static_assert(ct::has_member_qualifiers::value, ""); +static_assert(ct::has_member_qualifiers::value, ""); +static_assert(!ct::has_member_qualifiers::value, ""); + +TEST_CASE("example: has_member_qualifiers") {} +} // namespace ex_has_member_qualifiers + +// libs/callable_traits/example/has_varargs.cpp +namespace ex_has_varargs { +namespace ct = boost::callable_traits; + +static_assert(ct::has_varargs::value, ""); +static_assert(!ct::has_varargs::value, ""); + +TEST_CASE("example: has_varargs") {} +} // namespace ex_has_varargs + +// libs/callable_traits/example/has_void_return.cpp +namespace ex_has_void_return { +namespace ct = boost::callable_traits; + +static_assert(ct::has_void_return::value, ""); +static_assert(!ct::has_void_return::value, ""); + +TEST_CASE("example: has_void_return") {} +} // namespace ex_has_void_return + +// libs/callable_traits/example/intro.cpp +namespace ex_intro { +namespace ct = boost::callable_traits; + +// This function template helps keep our example code neat +template +void assert_same(){ static_assert(std::is_same::value, ""); } + +// foo is a function object +struct foo { + void operator()(int, char, float) const {} +}; + +TEST_CASE("example: intro") { + + // Use args_t to retrieve a parameter list as a std::tuple: + assert_same< + ct::args_t, + std::tuple + >(); + + // has_void_return lets us perform a quick check for a void return type + static_assert(ct::has_void_return::value, ""); + + // Detect C-style variadics (ellipses) in a signature (e.g. printf) + static_assert(!ct::has_varargs::value, ""); + + // pmf is a pointer-to-member function: void (foo::*)(int, char, float) const + using pmf = decltype(&foo::operator()); + + // remove_member_const_t lets you remove the const member qualifier + assert_same< + ct::remove_member_const_t, + void (foo::*)(int, char, float) /*no const!*/ + >(); + + // Conversely, add_member_const_t adds a const member qualifier + assert_same< + pmf, + ct::add_member_const_t + >(); + + // is_const_member_v checks for the presence of member const + static_assert(ct::is_const_member::value, ""); +} +} // namespace ex_intro + +// libs/callable_traits/example/is_const_member.cpp +namespace ex_is_const_member { +namespace ct = boost::callable_traits; + +struct foo; + +static_assert(ct::is_const_member::value, ""); +static_assert(!ct::is_const_member::value, ""); + +TEST_CASE("example: is_const_member") {} +} // namespace ex_is_const_member + +// libs/callable_traits/example/is_cv_member.cpp +namespace ex_is_cv_member { +namespace ct = boost::callable_traits; + +struct foo; + +static_assert(ct::is_cv_member::value, ""); +static_assert(!ct::is_cv_member::value, ""); +static_assert(!ct::is_cv_member::value, ""); +static_assert(!ct::is_cv_member::value, ""); + +TEST_CASE("example: is_cv_member") {} +} // namespace ex_is_cv_member + +// libs/callable_traits/example/is_invocable.cpp +namespace ex_is_invocable { +namespace ct = boost::callable_traits; + +struct foo { + template + typename std::enable_if::value>::type + operator()(T){} +}; + +static_assert(ct::is_invocable::value, ""); +static_assert(!ct::is_invocable::value, ""); + +TEST_CASE("example: is_invocable") {} +} // namespace ex_is_invocable + +// libs/callable_traits/example/is_lvalue_reference_member.cpp +namespace ex_is_lvalue_reference_member { +namespace ct = boost::callable_traits; + +static_assert(ct::is_lvalue_reference_member::value, ""); +static_assert(!ct::is_lvalue_reference_member::value, ""); +static_assert(!ct::is_lvalue_reference_member::value, ""); + +struct foo; + +static_assert(ct::is_lvalue_reference_member::value, ""); +static_assert(!ct::is_lvalue_reference_member::value, ""); +static_assert(!ct::is_lvalue_reference_member::value, ""); + +TEST_CASE("example: is_lvalue_reference_member") {} +} // namespace ex_is_lvalue_reference_member + +// libs/callable_traits/example/is_noexcept.cpp +namespace ex_is_noexcept { +namespace ct = boost::callable_traits; + +struct foo; + +static_assert(ct::is_noexcept::value, ""); +static_assert(ct::is_noexcept::value, ""); +static_assert(ct::is_noexcept::value, ""); +static_assert(ct::is_noexcept::value, ""); + +static_assert(!ct::is_noexcept::value, ""); +static_assert(!ct::is_noexcept::value, ""); +static_assert(!ct::is_noexcept::value, ""); +static_assert(!ct::is_noexcept::value, ""); + +TEST_CASE("example: is_noexcept") {} +} // namespace ex_is_noexcept + +// libs/callable_traits/example/is_reference_member.cpp +namespace ex_is_reference_member { +namespace ct = boost::callable_traits; + +static_assert(ct::is_reference_member::value, ""); +static_assert(ct::is_reference_member::value, ""); +static_assert(!ct::is_reference_member::value, ""); + +struct foo; + +static_assert(ct::is_reference_member::value, ""); +static_assert(ct::is_reference_member::value, ""); +static_assert(!ct::is_reference_member::value, ""); + +TEST_CASE("example: is_reference_member") {} +} // namespace ex_is_reference_member + +// libs/callable_traits/example/is_rvalue_reference_member.cpp +namespace ex_is_rvalue_reference_member { +namespace ct = boost::callable_traits; + +static_assert(ct::is_rvalue_reference_member::value, ""); +static_assert(!ct::is_rvalue_reference_member::value, ""); +static_assert(!ct::is_rvalue_reference_member::value, ""); + +struct foo; + +static_assert(ct::is_rvalue_reference_member::value, ""); +static_assert(!ct::is_rvalue_reference_member::value, ""); +static_assert(!ct::is_rvalue_reference_member::value, ""); + +TEST_CASE("example: is_rvalue_reference_member") {} +} // namespace ex_is_rvalue_reference_member + +// libs/callable_traits/example/is_volatile_member.cpp +namespace ex_is_volatile_member { +namespace ct = boost::callable_traits; + +static_assert(ct::is_volatile_member::value, ""); +static_assert(ct::is_volatile_member::value, ""); +static_assert(!ct::is_volatile_member::value, ""); + +struct foo; + +static_assert(ct::is_volatile_member::value, ""); +static_assert(!ct::is_volatile_member::value, ""); +static_assert(!ct::is_volatile_member::value, ""); + +TEST_CASE("example: is_volatile_member") {} +} // namespace ex_is_volatile_member + +// libs/callable_traits/example/overview.cpp +namespace ex_overview { +using std::is_same; +using std::tuple; + +using namespace boost::callable_traits; + +struct number { + int value; + int add(int n) const { return value + n; } +}; + +using pmf = decltype(&number::add); + +//` Manipulate member functions pointers with ease: +static_assert(is_same< + remove_member_const_t, + int(number::*)(int) +>{}, ""); + +static_assert(is_same< + add_member_volatile_t, + int(number::*)(int) const volatile +>{}, ""); + +static_assert(is_same< + class_of_t, + number +>{}, ""); + +//` INVOKE-aware metafunctions: + +static_assert(is_same< + args_t, + tuple +>{}, ""); + +static_assert(is_same< + return_type_t, + int +>{}, ""); + +static_assert(is_same< + function_type_t, + int(const number&, int) +>{}, ""); + +static_assert(is_same< + qualified_class_of_t, + const number& +>{}, ""); + +//` Here are a few other trait examples: +static_assert(is_const_member{}, ""); +static_assert(!is_volatile_member{}, ""); +static_assert(!has_void_return{}, ""); +static_assert(!has_varargs{}, ""); + +TEST_CASE("example: overview") {} +} // namespace ex_overview + +// libs/callable_traits/example/qualified_class_of.cpp +namespace ex_qualified_class_of { +namespace ct = boost::callable_traits; + +struct foo; + +static_assert(std::is_same>::value, ""); + +static_assert(std::is_same>::value, ""); + +static_assert(std::is_same>::value, ""); + +TEST_CASE("example: qualified_class_of") {} +} // namespace ex_qualified_class_of + +// libs/callable_traits/example/remove_member_const.cpp +namespace ex_remove_member_const { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: remove_member_const") { + + { + using pmf = int(foo::*)() const; + using expect = int(foo::*)(); + using test = ct::remove_member_const_t; + static_assert(std::is_same::value, ""); + } { + using pmf = int(foo::*)() const &&; + using expect = int(foo::*)() &&; + using test = ct::remove_member_const_t; + static_assert(std::is_same::value, ""); + } { + using pmf = int(foo::*)() const volatile &; + using expect = int(foo::*)() volatile &; + using test = ct::remove_member_const_t; + static_assert(std::is_same::value, ""); + } { + using f = int() const; + using expect = int(); + using test = ct::remove_member_const_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_remove_member_const + +// libs/callable_traits/example/remove_member_cv.cpp +namespace ex_remove_member_cv { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: remove_member_cv") { + + { + using pmf = int(foo::*)() const volatile; + using expect = int(foo::*)(); + using test = ct::remove_member_cv_t; + static_assert(std::is_same::value, ""); + } { + using pmf = int(foo::*)() const &&; + using expect = int(foo::*)() &&; + using test = ct::remove_member_cv_t; + static_assert(std::is_same::value, ""); + } { + using pmf = int(foo::*)() const volatile &; + using expect = int(foo::*)() &; + using test = ct::remove_member_cv_t; + static_assert(std::is_same::value, ""); + } { + using f = int() const volatile; + using expect = int(); + using test = ct::remove_member_cv_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_remove_member_cv + +// libs/callable_traits/example/remove_member_reference.cpp +namespace ex_remove_member_reference { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: remove_member_reference") { + + { + using pmf = int(foo::*)() &; + using expect = int(foo::*)(); + using test = ct::remove_member_reference_t; + static_assert(std::is_same::value, ""); + } { + using pmf = int(foo::*)() const &&; + using expect = int(foo::*)() const; + using test = ct::remove_member_reference_t; + static_assert(std::is_same::value, ""); + } { + using pmf = int(foo::*)() const volatile &; + using expect = int(foo::*)() const volatile; + using test = ct::remove_member_reference_t; + static_assert(std::is_same::value, ""); + } { + using f = int() &&; + using expect = int(); + using test = ct::remove_member_reference_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_remove_member_reference + +// libs/callable_traits/example/remove_member_volatile.cpp +namespace ex_remove_member_volatile { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: remove_member_volatile") { + + { + using pmf = int(foo::*)() const volatile; + using expect = int(foo::*)() const; + using test = ct::remove_member_volatile_t; + static_assert(std::is_same::value, ""); + } { + using pmf = int(foo::*)() volatile &&; + using expect = int(foo::*)() &&; + using test = ct::remove_member_volatile_t; + static_assert(std::is_same::value, ""); + } { + using pmf = int(foo::*)() volatile &; + using expect = int(foo::*)() &; + using test = ct::remove_member_volatile_t; + static_assert(std::is_same::value, ""); + } { + using f = int() const volatile; + using expect = int() const; + using test = ct::remove_member_volatile_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_remove_member_volatile + +// libs/callable_traits/example/remove_noexcept.cpp +namespace ex_remove_noexcept { +using boost::callable_traits::remove_noexcept_t; + +static_assert(std::is_same< + remove_noexcept_t, + int() +>{}, ""); + +TEST_CASE("example: remove_noexcept") {} +} // namespace ex_remove_noexcept + +// libs/callable_traits/example/remove_varargs.cpp +namespace ex_remove_varargs { +namespace ct = boost::callable_traits; + +struct foo {}; + +TEST_CASE("example: remove_varargs") { + + { + using f = void(int, ...); + using expect = void(int); + using test = ct::remove_varargs_t; + static_assert(std::is_same::value, ""); + } { + using fp = void(*)(...); + using expect = void(*)(); + using test = ct::remove_varargs_t; + static_assert(std::is_same::value, ""); + } { + using fr = void(&)(const char*, ...); + using expect = void(&)(const char*); + using test = ct::remove_varargs_t; + static_assert(std::is_same::value, ""); + } { + using pmf = void(foo::*)(...) const; + using expect = void(foo::*)() const; + using test = ct::remove_varargs_t; + static_assert(std::is_same::value, ""); + } +} +} // namespace ex_remove_varargs + +// libs/callable_traits/example/return_type.cpp +namespace ex_return_type { +namespace ct = boost::callable_traits; + +using expect = int; + +struct foo; + +template +void test() { + using result = ct::return_type_t; + static_assert(std::is_same{}, ""); +} + +TEST_CASE("example: return_type") { + + test(); + test(); + test(); + test(); + test(); + + auto x = []() -> int { return 0; }; + + test(); +} +} // namespace ex_return_type + +// libs/callable_traits/example/void.cpp +namespace ex_void { +struct foo { + void operator()() const {} +}; + +namespace ct = boost::callable_traits; + +TEST_CASE("example: void") { + + using args = ct::args_t; + using expected_args = std::tuple<>; + static_assert(std::is_same{}, ""); + + using signature = ct::function_type_t; + using expected_signature = void(); + static_assert(std::is_same{}, ""); + +} +} // namespace ex_void diff --git a/tests/callable_traits.cpp b/tests/callable_traits/smoke.cpp similarity index 97% rename from tests/callable_traits.cpp rename to tests/callable_traits/smoke.cpp index 9ad40fdd..ffe319e8 100644 --- a/tests/callable_traits.cpp +++ b/tests/callable_traits/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.callable_traits smoke — signature traits -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.callable_traits; diff --git a/tests/charconv/api.cpp b/tests/charconv/api.cpp new file mode 100644 index 00000000..b30e224b --- /dev/null +++ b/tests/charconv/api.cpp @@ -0,0 +1,236 @@ +// mcpplibs.mwrap.boost.charconv — the documentation examples of Boost.Charconv +// (libs/charconv/doc/charconv/*.adoc) as cases "doc: #
", plus +// the overloads the definitions list (string_view input, precision, +// from_chars_erange, every base, every chars_format). The library has no +// user-type customization point. to_chars does not null-terminate, so results +// are compared as [first, ptr) instead of with strcmp. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.charconv; +import mcpplibs.mwrap.boost.core; // boost::core::string_view + +namespace cc = boost::charconv; + +namespace { +template +std::string to_s(T v, A... a) { + char buf[128]; + auto r = cc::to_chars(buf, buf + sizeof buf, v, a...); + REQUIRE(r.ec == std::errc()); + return std::string(buf, r.ptr); +} +} // namespace + +TEST_CASE("doc: basic_usage#basic_usage") { + const char* buffer = "42"; + int v = 0; + cc::from_chars_result r = cc::from_chars(buffer, buffer + std::strlen(buffer), v); + CHECK(r.ec == std::errc()); + CHECK(v == 42); + char out[64]; + cc::to_chars_result t = cc::to_chars(out, out + sizeof(out), 123456); + CHECK(t.ec == std::errc()); + CHECK(std::string_view(out, t.ptr) == "123456"); +} + +TEST_CASE("doc: chars_format#definition") { + static_assert(static_cast(cc::chars_format::general) == + (static_cast(cc::chars_format::fixed) | static_cast(cc::chars_format::scientific))); + CHECK(to_s(1234.5, cc::chars_format::fixed) == "1234.5"); + CHECK(to_s(1234.5, cc::chars_format::scientific) == "1.2345e+03"); + CHECK(to_s(1234.5, cc::chars_format::general) == "1234.5"); + CHECK(to_s(1.0, cc::chars_format::hex) == "1p+0"); +} + +TEST_CASE("doc: from_chars#integral") { + const char* buffer = "42"; + int v = 0; + cc::from_chars_result r = cc::from_chars(buffer, buffer + std::strlen(buffer), v); + CHECK(r.ec == std::errc()); + CHECK(static_cast(r)); + CHECK(v == 42); + std::string str_buffer(buffer); + boost::core::string_view sv(str_buffer); + int v2; + auto r2 = cc::from_chars(sv, v2); + CHECK(static_cast(r2)); + CHECK(v2 == v); +} + +TEST_CASE("doc: from_chars#floating-point") { + const char* buffer = "1.2345"; + double v = 0; + auto r = cc::from_chars(buffer, buffer + std::strlen(buffer), v); + CHECK(static_cast(r)); + CHECK(v == 1.2345); + std::string str_buffer(buffer); + boost::core::string_view sv(str_buffer); + double v2; + CHECK(static_cast(cc::from_chars(sv, v2))); + CHECK(v == v2); +} + +TEST_CASE("doc: from_chars#integral-2") { + const char* buffer = "2a"; + unsigned v = 0; + auto r = cc::from_chars(buffer, buffer + std::strlen(buffer), v, 16); + CHECK(static_cast(r)); + CHECK(v == 42); + unsigned b36 = 0; + std::string_view z = "zz"; + CHECK(static_cast(cc::from_chars(z.data(), z.data() + z.size(), b36, 36))); + CHECK(b36 == 35 * 36 + 35); +} + +TEST_CASE("doc: from_chars#floating-point-2") { + // Boost.Charconv 1.92 reads the hex digits as one integer (0x13a2b = + // 80427) and applies the p-exponent and the fraction digits as powers of + // ten: upstream's libs/charconv/test/from_chars_float.cpp + // (simple_hex_scientific_test) asserts 80427e-14 for this input. The + // documentation prints 8.0427e-18, and std::from_chars reads the + // C hexfloat 0x1.3a2bp-10 (about 1.1985e-3). The test pins upstream's + // tested behavior, so a change of it shows up here. + const char* buffer = "1.3a2bp-10"; + double v = 0, std_value = 0; + auto r = cc::from_chars(buffer, buffer + std::strlen(buffer), v, cc::chars_format::hex); + CHECK(static_cast(r)); + CHECK(v == 80427e-14); + std::from_chars(buffer, buffer + std::strlen(buffer), std_value, std::chars_format::hex); + CHECK(std_value == doctest::Approx(1.19846e-3).epsilon(1e-4)); + const char* b2 = "1.234p-10"; // upstream's second case + double v2 = 0; + cc::from_chars(b2, b2 + std::strlen(b2), v2, cc::chars_format::hex); + CHECK(v2 == 4660e-13); +} + +TEST_CASE("doc: from_chars#std-errc-invalid-argument") { + const char* buffer = "-123"; + unsigned v = 0; + auto r = cc::from_chars(buffer, buffer + std::strlen(buffer), v); + CHECK(r.ec == std::errc::invalid_argument); + CHECK_FALSE(static_cast(r)); + // The documentation shows "-1.573e-3" with chars_format::fixed failing; + // like std::from_chars it parses the fixed prefix and stops at 'e'. + const char* b2 = "-1.573e-3"; + double d = 0; + auto r2 = cc::from_chars(b2, b2 + std::strlen(b2), d, cc::chars_format::fixed); + CHECK(r2.ec == std::errc()); + CHECK(*r2.ptr == 'e'); + CHECK(d == -1.573); + const char* b3 = "e5"; + CHECK(cc::from_chars(b3, b3 + 2, d, cc::chars_format::fixed).ec == std::errc::invalid_argument); +} + +TEST_CASE("doc: from_chars#std-errc-result-out-of-range") { + const char* buffer = "1234"; + unsigned char v = 0; + auto r = cc::from_chars(buffer, buffer + std::strlen(buffer), v); + CHECK(r.ec == std::errc::result_out_of_range); + CHECK(v == 0); +} + +TEST_CASE("doc: from_chars#definitions") { + // from_chars on an out-of-range double leaves the value; from_chars_erange + // sets it to +-HUGE_VAL / 0 as strtod does. + const char* big = "1e9999"; + double v = 1.0; + auto r = cc::from_chars(big, big + std::strlen(big), v); + CHECK(r.ec == std::errc::result_out_of_range); + CHECK(v == 1.0); + double e = 1.0; + auto re = cc::from_chars_erange(big, big + std::strlen(big), e); + CHECK(re.ec == std::errc::result_out_of_range); + CHECK(std::isinf(e)); + float f = 0; + std::string_view sv = "2.5e1"; + CHECK(static_cast(cc::from_chars(sv.data(), sv.data() + sv.size(), f, cc::chars_format::scientific))); + CHECK(f == 25.0f); + long long ll = 0; + std::string_view neg = "-9223372036854775808"; + CHECK(static_cast(cc::from_chars(neg.data(), neg.data() + neg.size(), ll))); + CHECK(ll == std::numeric_limits::min()); + auto r3 = cc::from_chars(sv.data(), sv.data() + 3, f); // stops at the first unparsed char + CHECK(r3.ptr == sv.data() + 3); +} + +TEST_CASE("doc: to_chars#integral") { + char buffer[64]{}; + auto r = cc::to_chars(buffer, buffer + sizeof(buffer) - 1, 42); + CHECK(r.ec == std::errc()); + CHECK(std::strcmp(buffer, "42") == 0); +} +TEST_CASE("doc: to_chars#floating-point") { + char buffer[64]{}; + auto r = cc::to_chars(buffer, buffer + sizeof(buffer) - 1, 1e300); + CHECK(static_cast(r)); + CHECK(std::strcmp(buffer, "1e+300") == 0); +} +TEST_CASE("doc: to_chars#integral-2") { + char buffer[64]{}; + auto r = cc::to_chars(buffer, buffer + sizeof(buffer) - 1, 42, 16); + CHECK(static_cast(r)); + CHECK(std::strcmp(buffer, "2a") == 0); + CHECK(to_s(-255, 2) == "-11111111"); +} +TEST_CASE("doc: to_chars#floating-point-2") { + char buffer_u[64]{}, buffer_v[64]{}; + double u = -1.08260383390082950e+20; + double v = -1.08260383390082946e+20; + cc::to_chars(buffer_u, buffer_u + sizeof(buffer_u) - 1, u, cc::chars_format::hex); + cc::to_chars(buffer_v, buffer_v + sizeof(buffer_v) - 1, v, cc::chars_format::hex); + CHECK(std::string(buffer_u) == "-1.779a8946bb5fap+66"); + CHECK(std::string(buffer_v) == "-1.779a8946bb5f9p+66"); // one ULP apart +} +TEST_CASE("doc: to_chars#integral-3") { + char buffer[3]{}; + auto r = cc::to_chars(buffer, buffer + sizeof(buffer) - 1, -1234); + CHECK(r.ec == std::errc::value_too_large); + CHECK_FALSE(static_cast(r)); +} +TEST_CASE("doc: to_chars#floating-point-3") { + char buffer[3]{}; + auto r = cc::to_chars(buffer, buffer + sizeof(buffer) - 1, 1.2345); + CHECK(r.ec == std::errc::value_too_large); +} +TEST_CASE("doc: to_chars#definitions") { + CHECK(to_s(3.14159, cc::chars_format::fixed, 2) == "3.14"); + CHECK(to_s(3.14159, cc::chars_format::scientific, 3) == "3.142e+00"); + CHECK(to_s(0.1f) == "0.1"); // shortest round trip + CHECK(to_s(std::numeric_limits::infinity()) == "inf"); + CHECK(to_s(-std::numeric_limits::quiet_NaN()).find("nan") != std::string::npos); + CHECK(to_s(static_cast(-1)) == "18446744073709551615"); +} + +TEST_CASE("doc: limits#definitions") { + static_assert(cc::limits::max_chars10 >= 11); + static_assert(cc::limits::max_chars >= 16); + static_assert(cc::limits::max_chars10 >= 24); +} +TEST_CASE("doc: limits#examples") { + char buffer[cc::limits::max_chars10]; + auto r = cc::to_chars(buffer, buffer + sizeof(buffer), std::numeric_limits::max()); + CHECK(static_cast(r)); + CHECK(std::string_view(buffer, r.ptr) == "2147483647"); + char fb[cc::limits::max_chars10]; + auto rf = cc::to_chars(fb, fb + sizeof(fb), std::numeric_limits::max()); + // The documentation shows 3.40282347e+38; to_chars writes the shortest + // representation that round-trips. + CHECK(std::string_view(fb, rf.ptr) == "3.4028235e+38"); + char bb[cc::limits::max_chars]; + auto rb = cc::to_chars(bb, bb + sizeof(bb), std::numeric_limits::max(), 2); + CHECK(std::string_view(bb, rb.ptr) == "1111111111111111"); +} + +TEST_CASE("round trips over many values") { + std::mt19937_64 gen(7); + for (int i = 0; i < 2000; ++i) { + double d = std::bit_cast(gen()); + if (!std::isfinite(d)) continue; + char buf[64]; + auto r = cc::to_chars(buf, buf + sizeof buf, d); + double back = 0; + cc::from_chars(buf, r.ptr, back); + REQUIRE(back == d); + } +} diff --git a/tests/charconv/coverage.toml b/tests/charconv/coverage.toml new file mode 100644 index 00000000..813dbfb1 --- /dev/null +++ b/tests/charconv/coverage.toml @@ -0,0 +1,102 @@ +# tests/charconv/coverage.toml — official examples and documentation snippets of +# Boost.charconv 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "charconv" + +[[snippet]] +doc = "libs/charconv/doc/charconv/basic_usage.adoc#basic_usage" +title = "Usage Examples" +case = "charconv/api:doc: basic_usage#basic_usage" + +[[snippet]] +doc = "libs/charconv/doc/charconv/build.adoc#b2" +title = "B2" +status = "excluded" +reason = "build instructions (b2 invocation), no API use" + +[[snippet]] +doc = "libs/charconv/doc/charconv/chars_format.adoc#definition" +title = "Definition" +case = "charconv/api:doc: chars_format#definition" + +[[snippet]] +doc = "libs/charconv/doc/charconv/from_chars.adoc#definitions" +title = "Definitions" +case = "charconv/api:doc: from_chars#definitions" + +[[snippet]] +doc = "libs/charconv/doc/charconv/from_chars.adoc#floating-point" +title = "Floating Point" +case = "charconv/api:doc: from_chars#floating-point" + +[[snippet]] +doc = "libs/charconv/doc/charconv/from_chars.adoc#floating-point-2" +title = "Floating Point" +case = "charconv/api:doc: from_chars#floating-point-2" + +[[snippet]] +doc = "libs/charconv/doc/charconv/from_chars.adoc#integral" +title = "Integral" +case = "charconv/api:doc: from_chars#integral" + +[[snippet]] +doc = "libs/charconv/doc/charconv/from_chars.adoc#integral-2" +title = "Integral" +case = "charconv/api:doc: from_chars#integral-2" + +[[snippet]] +doc = "libs/charconv/doc/charconv/from_chars.adoc#std-errc-invalid-argument" +title = "std::errc::invalid_argument" +case = "charconv/api:doc: from_chars#std-errc-invalid-argument" + +[[snippet]] +doc = "libs/charconv/doc/charconv/from_chars.adoc#std-errc-result-out-of-range" +title = "std::errc::result_out_of_range" +case = "charconv/api:doc: from_chars#std-errc-result-out-of-range" + +[[snippet]] +doc = "libs/charconv/doc/charconv/limits.adoc#definitions" +title = "Definitions" +case = "charconv/api:doc: limits#definitions" + +[[snippet]] +doc = "libs/charconv/doc/charconv/limits.adoc#examples" +title = "Examples" +case = "charconv/api:doc: limits#examples" + +[[snippet]] +doc = "libs/charconv/doc/charconv/to_chars.adoc#definitions" +title = "Definitions" +case = "charconv/api:doc: to_chars#definitions" + +[[snippet]] +doc = "libs/charconv/doc/charconv/to_chars.adoc#floating-point" +title = "Floating Point" +case = "charconv/api:doc: to_chars#floating-point" + +[[snippet]] +doc = "libs/charconv/doc/charconv/to_chars.adoc#floating-point-2" +title = "Floating Point" +case = "charconv/api:doc: to_chars#floating-point-2" + +[[snippet]] +doc = "libs/charconv/doc/charconv/to_chars.adoc#floating-point-3" +title = "Floating Point" +case = "charconv/api:doc: to_chars#floating-point-3" + +[[snippet]] +doc = "libs/charconv/doc/charconv/to_chars.adoc#integral" +title = "Integral" +case = "charconv/api:doc: to_chars#integral" + +[[snippet]] +doc = "libs/charconv/doc/charconv/to_chars.adoc#integral-2" +title = "Integral" +case = "charconv/api:doc: to_chars#integral-2" + +[[snippet]] +doc = "libs/charconv/doc/charconv/to_chars.adoc#integral-3" +title = "Integral" +case = "charconv/api:doc: to_chars#integral-3" diff --git a/tests/charconv.cpp b/tests/charconv/smoke.cpp similarity index 96% rename from tests/charconv.cpp rename to tests/charconv/smoke.cpp index bf083d5f..80f12818 100644 --- a/tests/charconv.cpp +++ b/tests/charconv/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.charconv smoke — compiled lib linkage (from_chars/to_chars TUs) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.charconv; diff --git a/tests/circular_buffer/api.cpp b/tests/circular_buffer/api.cpp new file mode 100644 index 00000000..1d011d39 --- /dev/null +++ b/tests/circular_buffer/api.cpp @@ -0,0 +1,131 @@ +// mcpplibs.mwrap.boost.circular_buffer — circular_buffer and +// circular_buffer_space_optimized: construction, capacity control, insertion +// at both ends with overwrite, rotation and linearization, array_one/array_two, +// insert/erase/rinsert/rerase, assign, set_capacity / rset_capacity, resize, +// comparison and swap. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.circular_buffer; + +using cb_t = boost::circular_buffer; + +namespace { +std::vector v(const cb_t& c) { return {c.begin(), c.end()}; } +} // namespace + +TEST_CASE("construction") { + CHECK(cb_t().capacity() == 0); + cb_t a(5, 7); + CHECK(a.size() == 5); + CHECK(a.full()); + cb_t b(4, 2, 9); // capacity 4, two elements of 9 + CHECK(b.size() == 2); + CHECK(b.reserve() == 2); + std::vector src{1, 2, 3, 4, 5}; + cb_t c(3, src.begin(), src.end()); // keeps the last three + CHECK(v(c) == std::vector{3, 4, 5}); + cb_t d(src.begin(), src.end()); + CHECK(d.capacity() == 5); + cb_t e = {1, 2}; + (void)e; +} + +TEST_CASE("push/pop at both ends and overwrite") { + cb_t c(3); + c.push_back(1); + c.push_back(2); + c.push_front(0); + CHECK(v(c) == std::vector{0, 1, 2}); + c.push_front(-1); // full: overwrites the back + CHECK(v(c) == std::vector{-1, 0, 1}); + c.push_back(5); // full: overwrites the front + CHECK(v(c) == std::vector{0, 1, 5}); + c.pop_front(); + c.pop_back(); + CHECK(v(c) == std::vector{1}); + c.push_back(8); + c.push_front(9); + CHECK(v(c) == std::vector{9, 1, 8}); + CHECK(c.at(2) == 8); + CHECK_THROWS_AS(c.at(3), std::out_of_range); +} + +TEST_CASE("linearize, rotate, array_one/array_two") { + cb_t c(4); + for (int i = 1; i <= 6; ++i) c.push_back(i); // 3 4 5 6, wrapped + CHECK_FALSE(c.is_linearized()); + auto one = c.array_one(); + auto two = c.array_two(); + CHECK(one.second + two.second == 4); + int* p = c.linearize(); + CHECK(c.is_linearized()); + CHECK(std::vector(p, p + 4) == std::vector{3, 4, 5, 6}); + c.rotate(c.begin() + 1); + CHECK(v(c) == std::vector{4, 5, 6, 3}); +} + +TEST_CASE("insert / erase / rinsert / rerase") { + cb_t c; + c.assign(5, 3, 0); // capacity 5, three zeros (assign(n, x) would set capacity n) + CHECK(c.capacity() == 5); + c.insert(c.begin() + 1, 7); + CHECK(v(c) == std::vector{0, 7, 0, 0}); + c.insert(c.end(), 2, 9); // full: elements at the front are overwritten + CHECK(v(c) == std::vector{7, 0, 0, 9, 9}); + c.rinsert(c.begin(), 1); // full: inserting toward the front drops the back + CHECK(v(c) == std::vector{1, 7, 0, 0, 9}); + c.erase(c.begin() + 2, c.begin() + 4); + CHECK(v(c) == std::vector{1, 7, 9}); + c.rerase(c.begin()); + CHECK(v(c) == std::vector{7, 9}); + c.erase_begin(1); + c.erase_end(1); + CHECK(c.empty()); +} + +TEST_CASE("capacity changes and resize") { + cb_t c(5); + for (int i = 0; i < 5; ++i) c.push_back(i); + c.set_capacity(3); // keeps the front + CHECK(v(c) == std::vector{0, 1, 2}); + c.rset_capacity(2); // keeps the back + CHECK(v(c) == std::vector{1, 2}); + c.set_capacity(4); + c.resize(4, 8); + CHECK(v(c) == std::vector{1, 2, 8, 8}); + c.rresize(2); + CHECK(v(c) == std::vector{8, 8}); + c.clear(); + CHECK(c.empty()); + CHECK(c.capacity() == 4); + CHECK(c.max_size() >= 4); +} + +TEST_CASE("comparison and swap") { + cb_t a(3), b(3); + a.push_back(1); + b.push_back(2); + CHECK(a < b); + CHECK(a != b); + swap(a, b); + CHECK(a.front() == 2); + cb_t c(a); + CHECK(c == a); +} + +TEST_CASE("space optimized") { + using so_t = boost::circular_buffer_space_optimized; + so_t s(so_t::capacity_type(100, 10)); // capacity 100, at least 10 allocated + CHECK(s.capacity().capacity() == 100); + CHECK(s.capacity().min_capacity() == 10); + CHECK(s.empty()); + for (int i = 0; i < 150; ++i) s.push_back(i); + CHECK(s.size() == 100); + CHECK(s.front() == 50); + s.set_capacity(so_t::capacity_type(20)); + CHECK(s.size() == 20); + s.erase(s.begin(), s.begin() + 10); + CHECK(s.front() == 60); + CHECK(s.size() == 10); +} diff --git a/tests/circular_buffer/coverage.toml b/tests/circular_buffer/coverage.toml new file mode 100644 index 00000000..60ea86bb --- /dev/null +++ b/tests/circular_buffer/coverage.toml @@ -0,0 +1,26 @@ +# tests/circular_buffer/coverage.toml — official examples and documentation snippets of +# Boost.circular_buffer 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "circular_buffer" + +[[example]] +path = "libs/circular_buffer/example/bounded_buffer_comparison.cpp" +case = "circular_buffer/examples:example: bounded_buffer_comparison" + +[[example]] +path = "libs/circular_buffer/example/circular_buffer_bound_example.cpp" +case = "circular_buffer/examples:example: circular_buffer_bound_example" + +[[example]] +path = "libs/circular_buffer/example/circular_buffer_example.cpp" +case = "circular_buffer/examples:example: circular_buffer_example" + +[[example]] +path = "libs/circular_buffer/example/circular_buffer_iter_example.cpp" +case = "circular_buffer/examples:example: circular_buffer_iter_example" + +[[example]] +path = "libs/circular_buffer/example/circular_buffer_sum_example.cpp" +case = "circular_buffer/examples:example: circular_buffer_sum_example" diff --git a/tests/circular_buffer/custom_types.cpp b/tests/circular_buffer/custom_types.cpp new file mode 100644 index 00000000..df8129fc --- /dev/null +++ b/tests/circular_buffer/custom_types.cpp @@ -0,0 +1,99 @@ +// mcpplibs.mwrap.boost.circular_buffer — user element types: non-trivial +// destructors run exactly once when elements are overwritten or popped, +// move-only elements, a user allocator, and a user aggregate with an ordering. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.circular_buffer; + +namespace user { +inline int live = 0; +struct tracked { + int id; + explicit tracked(int i = 0) : id(i) { ++live; } + tracked(const tracked& o) : id(o.id) { ++live; } + tracked(tracked&& o) noexcept : id(o.id) { ++live; } + tracked& operator=(const tracked&) = default; + tracked& operator=(tracked&&) noexcept = default; + ~tracked() { --live; } +}; + +template +struct tagged_allocator { + using value_type = T; + int tag = 0; + tagged_allocator() = default; + explicit tagged_allocator(int t) : tag(t) {} + template + tagged_allocator(const tagged_allocator& o) noexcept : tag(o.tag) {} + T* allocate(std::size_t n) { return std::allocator().allocate(n); } + void deallocate(T* p, std::size_t n) noexcept { std::allocator().deallocate(p, n); } + template + bool operator==(const tagged_allocator& o) const noexcept { return tag == o.tag; } +}; + +struct sample { + double t; + double value; + auto operator<=>(const sample&) const = default; +}; +} // namespace user + +TEST_CASE("element lifetime through overwrite, pop, clear") { + user::live = 0; + { + boost::circular_buffer c(3); + for (int i = 0; i < 10; ++i) c.push_back(user::tracked(i)); + CHECK(user::live == 3); + CHECK(c.front().id == 7); + c.pop_front(); + CHECK(user::live == 2); + c.push_front(user::tracked(42)); + c.rotate(c.begin() + 1); + CHECK(c.back().id == 42); + boost::circular_buffer copy = c; + CHECK(user::live == 6); + copy.clear(); + CHECK(user::live == 3); + } + CHECK(user::live == 0); +} + +TEST_CASE("move-only elements") { + boost::circular_buffer> c(2); + c.push_back(std::make_unique(1)); + c.push_back(std::make_unique(2)); + c.push_back(std::make_unique(3)); // overwrites 1 + CHECK(*c.front() == 2); + std::unique_ptr taken = std::move(c.back()); + c.pop_back(); + CHECK(*taken == 3); + boost::circular_buffer> moved = std::move(c); + CHECK(moved.size() == 1); +} + +TEST_CASE("user allocator") { + using buf = boost::circular_buffer>; + buf c(4, user::tagged_allocator(7)); + CHECK(c.get_allocator().tag == 7); + c.push_back(1); + buf d(c); + CHECK(d.get_allocator() == c.get_allocator()); + boost::circular_buffer_space_optimized> s( + boost::circular_buffer_space_optimized>::capacity_type(8), + user::tagged_allocator(3)); + s.push_back(5); + CHECK(s.get_allocator().tag == 3); +} + +TEST_CASE("sliding window over user samples") { + boost::circular_buffer window(4); + for (int i = 0; i < 10; ++i) window.push_back({double(i), double(i * i)}); + double mean = 0; + for (const auto& s : window) mean += s.value; + mean /= double(window.size()); + CHECK(mean == doctest::Approx((36 + 49 + 64 + 81) / 4.0)); + CHECK(std::is_sorted(window.begin(), window.end())); + auto it = std::lower_bound(window.begin(), window.end(), user::sample{8, 0}); + CHECK(it->t == 8); +} diff --git a/tests/circular_buffer/examples.cpp b/tests/circular_buffer/examples.cpp new file mode 100644 index 00000000..4aa0e724 --- /dev/null +++ b/tests/circular_buffer/examples.cpp @@ -0,0 +1,215 @@ +// mcpplibs.mwrap.boost.circular_buffer — libs/circular_buffer/example/*.cpp as +// test cases. The bounded-buffer examples keep their boost::mutex / +// boost::condition / boost::bind producer-consumer, with smaller element +// counts and without boost::timer (they are timing comparisons upstream; the +// test keeps the FIFO behavior). boost::call_traits::param_type is spelled +// `const T&` (the int / std::string case it resolves to). +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.circular_buffer; +import mcpplibs.mwrap.boost.thread; +import mcpplibs.mwrap.boost.bind; + +namespace { +constexpr unsigned long queue_size = 100; +constexpr unsigned long total_elements = queue_size * 20; + +template +class bounded_buffer { +public: + using container_type = boost::circular_buffer; + using size_type = typename container_type::size_type; + using value_type = typename container_type::value_type; + explicit bounded_buffer(size_type capacity) : m_unread(0), m_container(capacity) {} + bounded_buffer(const bounded_buffer&) = delete; + bounded_buffer& operator=(const bounded_buffer&) = delete; + void push_front(const value_type& item) { + boost::mutex::scoped_lock lock(m_mutex); + m_not_full.wait(lock, boost::bind(&bounded_buffer::is_not_full, this)); + m_container.push_front(item); + ++m_unread; + lock.unlock(); + m_not_empty.notify_one(); + } + void pop_back(value_type* pItem) { + boost::mutex::scoped_lock lock(m_mutex); + m_not_empty.wait(lock, boost::bind(&bounded_buffer::is_not_empty, this)); + *pItem = m_container[--m_unread]; + lock.unlock(); + m_not_full.notify_one(); + } +private: + bool is_not_empty() const { return m_unread > 0; } + bool is_not_full() const { return m_unread < m_container.capacity(); } + size_type m_unread; + container_type m_container; + boost::mutex m_mutex; + boost::condition m_not_empty; + boost::condition m_not_full; +}; + +template +class bounded_buffer_space_optimized { +public: + using container_type = boost::circular_buffer_space_optimized; + using size_type = typename container_type::size_type; + using value_type = typename container_type::value_type; + explicit bounded_buffer_space_optimized(size_type capacity) : m_container(capacity) {} + void push_front(const value_type& item) { + boost::mutex::scoped_lock lock(m_mutex); + m_not_full.wait(lock, boost::bind(&bounded_buffer_space_optimized::is_not_full, this)); + m_container.push_front(item); + lock.unlock(); + m_not_empty.notify_one(); + } + void pop_back(value_type* pItem) { + boost::mutex::scoped_lock lock(m_mutex); + m_not_empty.wait(lock, boost::bind(&bounded_buffer_space_optimized::is_not_empty, this)); + *pItem = m_container.back(); + m_container.pop_back(); + lock.unlock(); + m_not_full.notify_one(); + } + std::size_t reserved() const { return m_container.capacity().capacity(); } +private: + bool is_not_empty() const { return m_container.size() > 0; } + bool is_not_full() const { return m_container.size() < m_container.capacity(); } + container_type m_container; + boost::mutex m_mutex; + boost::condition m_not_empty; + boost::condition m_not_full; +}; + +template +class bounded_buffer_deque_based { +public: + using container_type = std::deque; + using size_type = typename container_type::size_type; + using value_type = typename container_type::value_type; + explicit bounded_buffer_deque_based(size_type capacity) : m_capacity(capacity) {} + void push_front(const value_type& item) { + boost::mutex::scoped_lock lock(m_mutex); + m_not_full.wait(lock, boost::bind(&bounded_buffer_deque_based::is_not_full, this)); + m_container.push_front(item); + lock.unlock(); + m_not_empty.notify_one(); + } + void pop_back(value_type* pItem) { + boost::mutex::scoped_lock lock(m_mutex); + m_not_empty.wait(lock, boost::bind(&bounded_buffer_deque_based::is_not_empty, this)); + *pItem = m_container.back(); + m_container.pop_back(); + lock.unlock(); + m_not_full.notify_one(); + } +private: + bool is_not_empty() const { return m_container.size() > 0; } + bool is_not_full() const { return m_container.size() < m_capacity; } + size_type m_capacity; + container_type m_container; + boost::mutex m_mutex; + boost::condition m_not_empty; + boost::condition m_not_full; +}; + +// Producer pushes 0..n-1, consumer checks they come out in the same order. +template +std::vector fifo_test(Buffer* buffer, + std::function make) { + using value_type = typename Buffer::value_type; + std::vector received; + received.reserve(total_elements); + boost::thread consumer([&] { + value_type item; + for (unsigned long i = 0; i < total_elements; ++i) { + buffer->pop_back(&item); + received.push_back(item); + } + }); + boost::thread producer([&] { + for (unsigned long i = 0; i < total_elements; ++i) buffer->push_front(make(i)); + }); + producer.join(); + consumer.join(); + return received; +} +} // namespace + +TEST_CASE("example: circular_buffer_example") { + boost::circular_buffer cb(3); + cb.push_back(1); + cb.push_back(2); + cb.push_back(3); + CHECK(cb[0] == 1); + CHECK(cb[1] == 2); + CHECK(cb[2] == 3); + cb.push_back(4); // overwrites 1 + cb.push_back(5); // overwrites 2 + CHECK(cb[0] == 3); + CHECK(cb[1] == 4); + CHECK(cb[2] == 5); + cb.pop_back(); + cb.pop_front(); + CHECK(cb[0] == 4); +} + +TEST_CASE("example: circular_buffer_iter_example") { + boost::circular_buffer cb(3); + cb.push_back(1); + cb.push_back(2); + cb.push_back(3); + boost::circular_buffer::iterator it = cb.begin(); + CHECK(*it == 1); + cb.push_back(4); + CHECK(*it == 4); // the iterator still points to the initialized memory +} + +TEST_CASE("example: circular_buffer_sum_example") { + boost::circular_buffer cb(3); + CHECK(cb.capacity() == 3); + CHECK(cb.size() == 0); + CHECK(cb.empty()); + cb.push_back(1); + cb.push_back(2); + CHECK(cb[0] == 1); + CHECK(cb[1] == 2); + CHECK_FALSE(cb.full()); + CHECK(cb.size() == 2); + cb.push_back(3); + cb.push_back(4); + CHECK(std::accumulate(cb.begin(), cb.end(), 0) == 9); + CHECK(cb[0] == 2); + CHECK(cb[1] == 3); + CHECK(cb[2] == 4); + CHECK(*cb.begin() == 2); + CHECK(cb.front() == 2); + CHECK(cb.back() == 4); + CHECK(cb.full()); +} + +TEST_CASE("example: circular_buffer_bound_example") { + bounded_buffer bb_int(queue_size); + auto got = fifo_test(&bb_int, std::function([](unsigned long i) { + return static_cast(i); + })); + REQUIRE(got.size() == total_elements); + CHECK(std::is_sorted(got.begin(), got.end())); + CHECK(got.back() == static_cast(total_elements - 1)); +} + +TEST_CASE("example: bounded_buffer_comparison") { + auto as_string = std::function([](unsigned long i) { + return std::to_string(i); + }); + bounded_buffer a(queue_size); + bounded_buffer_space_optimized b(queue_size); + bounded_buffer_deque_based c(queue_size); + auto ra = fifo_test(&a, as_string); + auto rb = fifo_test(&b, as_string); + auto rc = fifo_test(&c, as_string); + CHECK(ra == rb); + CHECK(rb == rc); + CHECK(ra.front() == "0"); + CHECK(b.reserved() == queue_size); +} diff --git a/tests/circular_buffer.cpp b/tests/circular_buffer/smoke.cpp similarity index 95% rename from tests/circular_buffer.cpp rename to tests/circular_buffer/smoke.cpp index 06d0f1de..df95dfa4 100644 --- a/tests/circular_buffer.cpp +++ b/tests/circular_buffer/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.circular_buffer smoke — circular buffer container -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.circular_buffer; diff --git a/tests/cobalt/coverage.toml b/tests/cobalt/coverage.toml new file mode 100644 index 00000000..bb9522be --- /dev/null +++ b/tests/cobalt/coverage.toml @@ -0,0 +1,224 @@ +# tests/cobalt/coverage.toml — official examples and documentation snippets of +# Boost.cobalt 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "cobalt" + +[[example]] +path = "libs/cobalt/example/channel.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/delay.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/delay_op.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/echo_server.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/http.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/outcome.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/python.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/signals.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/spsc.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/thread.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/thread_pool.cpp" +status = "todo" + +[[example]] +path = "libs/cobalt/example/ticker.cpp" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/background/asio_awaitable.adoc#asio-awaitable" +title = "`asio::awaitable`" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/background/custom_executors.adoc#a-minimal-executor" +title = "A minimal executor" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/background/custom_executors.adoc#adding-a-work-guard" +title = "Adding a work guard." +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/background/custom_executors.adoc#execute" +title = "`execute`" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/background/custom_executors.adoc#properties" +title = "Properties" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/background/custom_executors.adoc#properties-of-the-asio-any-io-executor" +title = "Properties of the `asio::any_io_executor`" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/background/lazy_eager.adoc#lazy-eager" +title = "Lazy & eager" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/background/stackless.adoc#stackless" +title = "Stackless" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/benchmarks.adoc#immediate" +title = "Immediate" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/benchmarks.adoc#posting-to-an-executor" +title = "Posting to an executor" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/benchmarks.adoc#running-noop-coroutine-in-parallel" +title = "Running noop coroutine in parallel" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/compiler.adoc#compiler" +title = "Compiler" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/design/associators.adoc#associators" +title = "Associators" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/design/concepts.adoc#concepts" +title = "Concepts" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/design/promise.adoc#promise" +title = "Promise" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/design/race.adoc#interrupt-await" +title = "interrupt_await" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/design/race.adoc#race" +title = "Race" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/design/thread.adoc#threading" +title = "Threading" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/design/thread_local.adoc#using-strands" +title = "Using Strands" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/primer/async.adoc#async-programming" +title = "Async programming" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/primer/awaitables.adoc#awaitables" +title = "Awaitables" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/primer/coroutines.adoc#co-await" +title = "`co_await`" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/primer/coroutines.adoc#co-yield" +title = "`co_yield`" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tour/entry.adoc#entry-into-a-cobalt-environment" +title = "Entry into a cobalt environment" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tour/generator.adoc#tour-generator" +title = "Generator" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tour/join.adoc#tour-join" +title = "join" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tour/promise.adoc#promises" +title = "Promises" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tour/race.adoc#tour-race" +title = "race" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tour/task.adoc#tasks" +title = "Tasks" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tutorial/delay.adoc#delay" +title = "delay" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tutorial/delay_op.adoc#delay-op" +title = "delay op" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tutorial/echo_server.adoc#echo-server" +title = "echo server" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tutorial/push_generator.adoc#generator-with-push-value" +title = "Generator with push value" +status = "todo" + +[[snippet]] +doc = "libs/cobalt/doc/tutorial/ticker.adoc#price-ticker" +title = "price ticker" +status = "todo" diff --git a/tests/cobalt.cpp b/tests/cobalt/smoke.cpp similarity index 96% rename from tests/cobalt.cpp rename to tests/cobalt/smoke.cpp index 789c0ab3..3f1650a9 100644 --- a/tests/cobalt.cpp +++ b/tests/cobalt/smoke.cpp @@ -2,7 +2,7 @@ // is included before the import so std::coroutine_traits is // findable for the consumer-defined coroutine; run() and task<> are exported // by the module. -#include "test_assert.hpp" +#include "../test_assert.hpp" #include // gcc 16.1 module instantiation context: the std::_Sp_counted_ptr_inplace / // __is_nothrow_new_constructible_impl bodies instantiated from the diff --git a/tests/compat/api.cpp b/tests/compat/api.cpp new file mode 100644 index 00000000..29f12d56 --- /dev/null +++ b/tests/compat/api.cpp @@ -0,0 +1,525 @@ +// mcpplibs.mwrap.boost.compat — every documented component of Boost.Compat +// (libs/compat/doc/compat/*.adoc): bind_front/bind_back, function_ref, +// integer_sequence, invoke and its traits, latch, mem_fn, move_only_function, +// nontype, shared_lock, to_array, to_underlying. Cases are named +// "doc: #
" after the documentation section they exercise. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.compat; + +namespace compat = boost::compat; + +namespace { +struct X { + int f(int a, int b) const noexcept { return a * 10 + b; } + int g(int a) { return a + base; } + int base = 100; +}; +int add(int x, int y) noexcept { return x * 10 + y; } +struct point { + int x = 1, y = 2; +}; +} // namespace + +// ---- bind_front / bind_back --------------------------------------------- + +TEST_CASE("doc: bind_back#example") { + auto fn = compat::bind_back(&X::f, 1, 2); + X x; + CHECK(fn(x) == 12); +} + +TEST_CASE("doc: bind_back#bind-back") { + auto minus = compat::bind_back([](int a, int b) { return a - b; }, 1); + CHECK(minus(10) == 9); + std::string s = "x"; + auto append = compat::bind_back([](std::string a, const std::string& b) { return a + b; }, s); + s = "changed"; // bound arguments are copied + CHECK(append(std::string("a")) == "ax"); +} + +TEST_CASE("doc: bind_front#example") { + X x; + auto fn = compat::bind_front(&X::f, &x); + CHECK(fn(1, 2) == 12); +} + +TEST_CASE("doc: bind_front#bind-front") { + auto minus = compat::bind_front([](int a, int b) { return a - b; }, 10); + CHECK(minus(1) == 9); + auto ptr = std::make_unique(5); + auto owns = compat::bind_front([](const std::unique_ptr& p, int k) { return *p * k; }, + std::move(ptr)); + CHECK(owns(3) == 15); +} + +// ---- function_ref -------------------------------------------------------- + +TEST_CASE("doc: function_ref#example") { + auto add2 = [](int x, int y) { return x * 100 + y; }; + std::vector> fns; + fns.push_back({add}); + fns.push_back({add2}); + std::vector out; + for (auto fn : fns) out.push_back(fn(1, 2)); + CHECK(out == std::vector{12, 102}); +} + +TEST_CASE("doc: function_ref#function-pointer-constructor") { + compat::function_ref f(&add); + CHECK(f(3, 4) == 34); +} + +TEST_CASE("doc: function_ref#object-constructor") { + int calls = 0; + auto counter = [&](int) { return ++calls; }; + compat::function_ref f(counter); + f(0); + f(0); + CHECK(calls == 2); // refers to the object, does not copy it +} + +TEST_CASE("doc: function_ref#pointer-to-member-function-constructor") { + point p; + compat::function_ref f(compat::nontype<&point::x>); + CHECK(f(p) == 1); +} + +TEST_CASE("doc: function_ref#bound-object-constructor") { + point p; + compat::function_ref f(compat::nontype<&point::x>, p); + CHECK(f() == 1); +} + +TEST_CASE("doc: function_ref#bound-pointer-constructor") { + point p; + p.x = 7; + compat::function_ref f(compat::nontype<&point::x>, &p); + CHECK(f() == 7); +} + +TEST_CASE("doc: function_ref#copy-constructor") { + compat::function_ref f(add); + compat::function_ref g(f); + CHECK(g(1, 1) == 11); +} + +TEST_CASE("doc: function_ref#call-operator") { + compat::function_ref f(add); + static_assert(noexcept(f(1, 2))); + CHECK(f(2, 3) == 23); +} + +TEST_CASE("doc: function_ref#copy-assignment") { + auto mul = [](int a, int b) { return a * b; }; + compat::function_ref f(add), g(mul); + f = g; + CHECK(f(3, 4) == 12); +} + +// ---- integer_sequence ---------------------------------------------------- + +namespace { +template +constexpr T sum(compat::integer_sequence) { + return (T(0) + ... + I); +} +template +constexpr std::size_t count(compat::index_sequence) { + return sizeof...(I); +} +} // namespace + +TEST_CASE("doc: integer_sequence#integer-sequence") { + static_assert(sum(compat::integer_sequence()) == 6); +} +TEST_CASE("doc: integer_sequence#make-integer-sequence") { + static_assert(sum(compat::make_integer_sequence()) == 10); +} +TEST_CASE("doc: integer_sequence#index-sequence") { + static_assert(std::is_same_v, compat::integer_sequence>); +} +TEST_CASE("doc: integer_sequence#make-index-sequence") { + static_assert(std::is_same_v, compat::index_sequence<0, 1, 2>>); +} +TEST_CASE("doc: integer_sequence#index-sequence-for") { + static_assert(count(compat::index_sequence_for()) == 3); +} + +// ---- invoke --------------------------------------------------------------- + +TEST_CASE("doc: invoke#example") { + X x; + CHECK(compat::invoke(&X::f, x, 1, 2) == 12); +} +TEST_CASE("doc: invoke#invoke") { + X x; + CHECK(compat::invoke(&X::g, &x, 1) == 101); + CHECK(compat::invoke(&X::base, x) == 100); // pointer to data member + CHECK(compat::invoke(add, 4, 5) == 45); + CHECK(compat::invoke([](int v) { return -v; }, 3) == -3); + static_assert(noexcept(compat::invoke(&X::f, x, 1, 2))); +} +TEST_CASE("doc: invoke#invoke-result-t") { + static_assert(std::is_same_v, int>); + static_assert(std::is_same_v, int&>); +} +TEST_CASE("doc: invoke#is-invocable") { + static_assert(compat::is_invocable::value); + static_assert(!compat::is_invocable::value); +} +TEST_CASE("doc: invoke#is-nothrow-invocable") { + static_assert(compat::is_nothrow_invocable::value); + static_assert(!compat::is_nothrow_invocable::value); +} +TEST_CASE("doc: invoke#invoke-r") { + X x; + long r = compat::invoke_r(&X::f, x, 1, 2); + CHECK(r == 12L); + compat::invoke_r(&X::f, x, 1, 2); // result discarded +} +TEST_CASE("doc: invoke#is-invocable-r") { + static_assert(compat::is_invocable_r::value); + static_assert(compat::is_invocable_r::value); + static_assert(!compat::is_invocable_r::value); +} +TEST_CASE("doc: invoke#is-nothrow-invocable-r") { + static_assert(compat::is_nothrow_invocable_r::value); +} + +// ---- latch ----------------------------------------------------------------- + +TEST_CASE("doc: latch#example") { + std::ptrdiff_t const num_threads = 8; + compat::latch l(num_threads); + std::atomic after{0}; + std::vector threads; + for (int i = 0; i < num_threads; ++i) + threads.emplace_back([&] { + l.arrive_and_wait(); + ++after; + }); + for (auto& t : threads) t.join(); + CHECK(after == num_threads); + CHECK(l.try_wait()); +} +TEST_CASE("doc: latch#counter-constructor") { + compat::latch l(2); + CHECK_FALSE(l.try_wait()); + compat::latch zero(0); + CHECK(zero.try_wait()); +} +TEST_CASE("doc: latch#copy-constructor") { + static_assert(!std::is_copy_constructible_v); + static_assert(!std::is_copy_assignable_v); +} +TEST_CASE("doc: latch#count-down") { + compat::latch l(3); + l.count_down(); + l.count_down(2); + CHECK(l.try_wait()); +} +TEST_CASE("doc: latch#try-wait") { + compat::latch l(1); + CHECK_FALSE(l.try_wait()); + l.count_down(); + CHECK(l.try_wait()); +} +TEST_CASE("doc: latch#wait") { + compat::latch l(1); + std::thread t([&] { l.count_down(); }); + l.wait(); + t.join(); + CHECK(l.try_wait()); +} +TEST_CASE("doc: latch#arrive-and-wait") { + compat::latch l(2); + std::thread t([&] { l.arrive_and_wait(); }); + l.arrive_and_wait(); + t.join(); + CHECK(l.try_wait()); +} +TEST_CASE("doc: latch#max") { + static_assert(compat::latch::max() > 0); + CHECK(compat::latch::max() >= 1000); +} + +// ---- mem_fn ---------------------------------------------------------------- + +TEST_CASE("doc: mem_fn#example") { + auto fn = compat::mem_fn(&X::f); + X x; + CHECK(fn(x, 1, 2) == 12); +} +TEST_CASE("doc: mem_fn#mem-fn") { + auto data = compat::mem_fn(&X::base); + X x; + CHECK(data(x) == 100); + CHECK(data(&x) == 100); + std::vector xs(3); + std::vector bases; + std::transform(xs.begin(), xs.end(), std::back_inserter(bases), compat::mem_fn(&X::base)); + CHECK(bases == std::vector{100, 100, 100}); +} + +// ---- move_only_function ------------------------------------------------------ + +namespace { +struct moveonly { + std::unique_ptr p_ = std::make_unique(1234); + auto operator()(int x) noexcept { return *p_ + x; } +}; +} // namespace + +TEST_CASE("doc: move_only_function#example") { + compat::move_only_function f(moveonly{}); + CHECK(f(4321) == 5555); +} +TEST_CASE("doc: move_only_function#default-constructor") { + compat::move_only_function f; + CHECK_FALSE(f); +} +TEST_CASE("doc: move_only_function#null-constructor") { + compat::move_only_function f(nullptr); + CHECK(f == nullptr); +} +TEST_CASE("doc: move_only_function#object-constructor") { + compat::move_only_function f([k = std::make_unique(2)](int v) { return v * *k; }); + CHECK(f(21) == 42); + compat::move_only_function g(&add); + CHECK(g(1, 2) == 12); + compat::move_only_function null_ptr(static_cast(nullptr)); + CHECK_FALSE(null_ptr); // a null function pointer gives an empty object +} +TEST_CASE("doc: move_only_function#in-place-constructor") { + compat::move_only_function f(compat::in_place_type_t{}); + CHECK(f(1) == 1235); +} +TEST_CASE("doc: move_only_function#list-in-place-constructor") { + struct summer { + std::vector v; + summer(std::initializer_list il, int extra) : v(il) { v.push_back(extra); } + int operator()() const { return std::accumulate(v.begin(), v.end(), 0); } + }; + compat::move_only_function f(compat::in_place_type_t{}, {1, 2, 3}, 4); + CHECK(f() == 10); +} +TEST_CASE("doc: move_only_function#move-constructor") { + compat::move_only_function f(moveonly{}); + compat::move_only_function g(std::move(f)); + CHECK(g(0) == 1234); +} +TEST_CASE("doc: move_only_function#move-assignment") { + compat::move_only_function f(moveonly{}), g; + g = std::move(f); + CHECK(g(1) == 1235); +} +TEST_CASE("doc: move_only_function#null-assigment") { + compat::move_only_function f(moveonly{}); + f = nullptr; + CHECK_FALSE(f); +} +TEST_CASE("doc: move_only_function#callable-assignment") { + compat::move_only_function f; + f = [](int v) { return v + 1; }; + CHECK(f(1) == 2); +} +TEST_CASE("doc: move_only_function#null-equality") { + compat::move_only_function f, g([] {}); + CHECK(f == nullptr); + CHECK(g != nullptr); +} +TEST_CASE("doc: move_only_function#swap") { + compat::move_only_function a([] { return 1; }), b([] { return 2; }); + a.swap(b); + CHECK(a() == 2); + swap(a, b); + CHECK(a() == 1); +} +TEST_CASE("doc: move_only_function#boolean-conversion") { + compat::move_only_function f; + CHECK_FALSE(static_cast(f)); + f = [] {}; + CHECK(static_cast(f)); +} +TEST_CASE("doc: move_only_function#call-operator") { + struct qualified { + int operator()() & { return 1; } + int operator()() && { return 2; } + int operator()() const& { return 3; } + }; + compat::move_only_function l(qualified{}); + CHECK(l() == 1); + compat::move_only_function r(qualified{}); + CHECK(std::move(r)() == 2); + const compat::move_only_function c(qualified{}); + CHECK(c() == 3); +} + +// ---- nontype ------------------------------------------------------------------ + +TEST_CASE("doc: nontype#example") { + X x; + auto fnref = compat::function_ref(compat::nontype<&X::f>, &x); + CHECK(fnref(1, 2) == 12); + static_assert(std::is_same_v), const compat::nontype_t<&add>>); +} + +// ---- shared_lock -------------------------------------------------------------- + +TEST_CASE("doc: shared_lock#example") { + std::shared_mutex m; + compat::shared_lock guard(m); + CHECK(guard.owns_lock()); + CHECK_FALSE(m.try_lock()); // a writer is excluded +} +TEST_CASE("doc: shared_lock#default-constructor") { + compat::shared_lock l; + CHECK(l.mutex() == nullptr); + CHECK_FALSE(l.owns_lock()); +} +TEST_CASE("doc: shared_lock#locking-constructor") { + std::shared_mutex m; + compat::shared_lock a(m), b(m); // shared: both hold it + CHECK(a.owns_lock()); + CHECK(b.owns_lock()); +} +TEST_CASE("doc: shared_lock#deferred-constructor") { + std::shared_mutex m; + compat::shared_lock l(m, std::defer_lock); + CHECK_FALSE(l.owns_lock()); + CHECK(l.mutex() == &m); +} +TEST_CASE("doc: shared_lock#try-to-lock-constructor") { + std::shared_mutex m; + m.lock(); + compat::shared_lock l(m, std::try_to_lock); + CHECK_FALSE(l.owns_lock()); + m.unlock(); + compat::shared_lock l2(m, std::try_to_lock); + CHECK(l2.owns_lock()); +} +TEST_CASE("doc: shared_lock#adopting-constructor") { + std::shared_mutex m; + m.lock_shared(); + compat::shared_lock l(m, std::adopt_lock); + CHECK(l.owns_lock()); +} +TEST_CASE("doc: shared_lock#copy-constructor") { + static_assert(!std::is_copy_constructible_v>); +} +TEST_CASE("doc: shared_lock#copy-assignment") { + static_assert(!std::is_copy_assignable_v>); +} +TEST_CASE("doc: shared_lock#move-constructor") { + std::shared_mutex m; + compat::shared_lock a(m); + compat::shared_lock b(std::move(a)); + CHECK(b.owns_lock()); + CHECK_FALSE(a.owns_lock()); + CHECK(a.mutex() == nullptr); +} +TEST_CASE("doc: shared_lock#move-assignment") { + std::shared_mutex m1, m2; + compat::shared_lock a(m1), b(m2); + b = std::move(a); // releases m2 + CHECK(b.mutex() == &m1); + CHECK(m2.try_lock()); + m2.unlock(); +} +TEST_CASE("doc: shared_lock#destructor") { + std::shared_mutex m; + { compat::shared_lock l(m); } + CHECK(m.try_lock()); + m.unlock(); +} +TEST_CASE("doc: shared_lock#lock") { + std::shared_mutex m; + compat::shared_lock l(m, std::defer_lock); + l.lock(); + CHECK(l.owns_lock()); + CHECK_THROWS_AS(l.lock(), std::system_error); // already owns +} +TEST_CASE("doc: shared_lock#try-lock") { + std::shared_mutex m; + compat::shared_lock l(m, std::defer_lock); + CHECK(l.try_lock()); + compat::shared_lock none; + CHECK_THROWS_AS(none.try_lock(), std::system_error); // no mutex +} +TEST_CASE("doc: shared_lock#unlock") { + std::shared_mutex m; + compat::shared_lock l(m); + l.unlock(); + CHECK_FALSE(l.owns_lock()); + CHECK_THROWS_AS(l.unlock(), std::system_error); +} +TEST_CASE("doc: shared_lock#swap") { + std::shared_mutex m1, m2; + compat::shared_lock a(m1), b(m2, std::defer_lock); + a.swap(b); + CHECK(a.mutex() == &m2); + CHECK(b.owns_lock()); +} +TEST_CASE("doc: shared_lock#free-function-swap") { + std::shared_mutex m; + compat::shared_lock a(m), b; + swap(a, b); + CHECK(b.owns_lock()); +} +TEST_CASE("doc: shared_lock#release") { + std::shared_mutex m; + compat::shared_lock l(m); + std::shared_mutex* p = l.release(); + CHECK(p == &m); + CHECK_FALSE(l.owns_lock()); + p->unlock_shared(); +} +TEST_CASE("doc: shared_lock#mutex") { + std::shared_mutex m; + compat::shared_lock l(m); + CHECK(l.mutex() == &m); +} +TEST_CASE("doc: shared_lock#owns-lock") { + std::shared_mutex m; + compat::shared_lock l(m, std::defer_lock); + CHECK_FALSE(l.owns_lock()); +} +TEST_CASE("doc: shared_lock#boolean-conversion") { + std::shared_mutex m; + compat::shared_lock l(m); + CHECK(static_cast(l)); +} + +// ---- to_array / to_underlying ------------------------------------------------- + +TEST_CASE("doc: to_array#example") { + int input[] = {1, 2, 3}; + std::array output = compat::to_array(input); + CHECK(output == std::array{{1, 2, 3}}); +} +TEST_CASE("doc: to_array#to-array") { + constexpr auto a = compat::to_array({4, 5}); + static_assert(a.size() == 2 && a[1] == 5); + const char str[] = "hi"; + auto c = compat::to_array(str); + static_assert(std::is_same_v>); + auto m = compat::to_array({std::make_unique(1)}); // moves from the rvalue array + CHECK(*m[0] == 1); +} + +namespace { +enum e : std::uint16_t { e_value = 5 }; +enum class color : char { red = 'r' }; +} // namespace + +TEST_CASE("doc: to_underlying#example") { + auto output = compat::to_underlying(e_value); + static_assert(std::is_same_v); + CHECK(output == 5); +} +TEST_CASE("doc: to_underlying#to-underlying") { + static_assert(compat::to_underlying(color::red) == 'r'); + static_assert(noexcept(compat::to_underlying(color::red))); +} diff --git a/tests/compat/coverage.toml b/tests/compat/coverage.toml new file mode 100644 index 00000000..82210ee4 --- /dev/null +++ b/tests/compat/coverage.toml @@ -0,0 +1,381 @@ +# tests/compat/coverage.toml — official examples and documentation snippets of +# Boost.compat 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "compat" + +[[snippet]] +doc = "libs/compat/doc/compat/bind_back.adoc#bind-back" +title = "bind_back" +case = "compat/api:doc: bind_back#bind-back" + +[[snippet]] +doc = "libs/compat/doc/compat/bind_back.adoc#example" +title = "Example" +case = "compat/api:doc: bind_back#example" + +[[snippet]] +doc = "libs/compat/doc/compat/bind_front.adoc#bind-front" +title = "bind_front" +case = "compat/api:doc: bind_front#bind-front" + +[[snippet]] +doc = "libs/compat/doc/compat/bind_front.adoc#example" +title = "Example" +case = "compat/api:doc: bind_front#example" + +[[snippet]] +doc = "libs/compat/doc/compat/function_ref.adoc#bound-object-constructor" +title = "Bound Object Constructor" +case = "compat/api:doc: function_ref#bound-object-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/function_ref.adoc#bound-pointer-constructor" +title = "Bound Pointer Constructor" +case = "compat/api:doc: function_ref#bound-pointer-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/function_ref.adoc#call-operator" +title = "call operator" +case = "compat/api:doc: function_ref#call-operator" + +[[snippet]] +doc = "libs/compat/doc/compat/function_ref.adoc#copy-assignment" +title = "Copy Assignment" +case = "compat/api:doc: function_ref#copy-assignment" + +[[snippet]] +doc = "libs/compat/doc/compat/function_ref.adoc#copy-constructor" +title = "Copy Constructor" +case = "compat/api:doc: function_ref#copy-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/function_ref.adoc#example" +title = "Example" +case = "compat/api:doc: function_ref#example" + +[[snippet]] +doc = "libs/compat/doc/compat/function_ref.adoc#function-pointer-constructor" +title = "Function Pointer Constructor" +case = "compat/api:doc: function_ref#function-pointer-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/function_ref.adoc#object-constructor" +title = "Object Constructor" +case = "compat/api:doc: function_ref#object-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/function_ref.adoc#pointer-to-member-function-constructor" +title = "Pointer to Member Function Constructor" +case = "compat/api:doc: function_ref#pointer-to-member-function-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/integer_sequence.adoc#index-sequence" +title = "index_sequence" +case = "compat/api:doc: integer_sequence#index-sequence" + +[[snippet]] +doc = "libs/compat/doc/compat/integer_sequence.adoc#index-sequence-for" +title = "index_sequence_for" +case = "compat/api:doc: integer_sequence#index-sequence-for" + +[[snippet]] +doc = "libs/compat/doc/compat/integer_sequence.adoc#integer-sequence" +title = "integer_sequence" +case = "compat/api:doc: integer_sequence#integer-sequence" + +[[snippet]] +doc = "libs/compat/doc/compat/integer_sequence.adoc#make-index-sequence" +title = "make_index_sequence" +case = "compat/api:doc: integer_sequence#make-index-sequence" + +[[snippet]] +doc = "libs/compat/doc/compat/integer_sequence.adoc#make-integer-sequence" +title = "make_integer_sequence" +case = "compat/api:doc: integer_sequence#make-integer-sequence" + +[[snippet]] +doc = "libs/compat/doc/compat/invoke.adoc#example" +title = "Example" +case = "compat/api:doc: invoke#example" + +[[snippet]] +doc = "libs/compat/doc/compat/invoke.adoc#invoke" +title = "invoke" +case = "compat/api:doc: invoke#invoke" + +[[snippet]] +doc = "libs/compat/doc/compat/invoke.adoc#invoke-r" +title = "invoke_r" +case = "compat/api:doc: invoke#invoke-r" + +[[snippet]] +doc = "libs/compat/doc/compat/invoke.adoc#invoke-result-t" +title = "invoke_result_t" +case = "compat/api:doc: invoke#invoke-result-t" + +[[snippet]] +doc = "libs/compat/doc/compat/invoke.adoc#is-invocable" +title = "is_invocable" +case = "compat/api:doc: invoke#is-invocable" + +[[snippet]] +doc = "libs/compat/doc/compat/invoke.adoc#is-invocable-r" +title = "is_invocable_r" +case = "compat/api:doc: invoke#is-invocable-r" + +[[snippet]] +doc = "libs/compat/doc/compat/invoke.adoc#is-nothrow-invocable" +title = "is_nothrow_invocable" +case = "compat/api:doc: invoke#is-nothrow-invocable" + +[[snippet]] +doc = "libs/compat/doc/compat/invoke.adoc#is-nothrow-invocable-r" +title = "is_nothrow_invocable_r" +case = "compat/api:doc: invoke#is-nothrow-invocable-r" + +[[snippet]] +doc = "libs/compat/doc/compat/latch.adoc#arrive-and-wait" +title = "arrive_and_wait" +case = "compat/api:doc: latch#arrive-and-wait" + +[[snippet]] +doc = "libs/compat/doc/compat/latch.adoc#copy-constructor" +title = "Copy Constructor" +case = "compat/api:doc: latch#copy-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/latch.adoc#count-down" +title = "count_down" +case = "compat/api:doc: latch#count-down" + +[[snippet]] +doc = "libs/compat/doc/compat/latch.adoc#counter-constructor" +title = "Counter Constructor" +case = "compat/api:doc: latch#counter-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/latch.adoc#example" +title = "Example" +case = "compat/api:doc: latch#example" + +[[snippet]] +doc = "libs/compat/doc/compat/latch.adoc#max" +title = "max" +case = "compat/api:doc: latch#max" + +[[snippet]] +doc = "libs/compat/doc/compat/latch.adoc#try-wait" +title = "try_wait" +case = "compat/api:doc: latch#try-wait" + +[[snippet]] +doc = "libs/compat/doc/compat/latch.adoc#wait" +title = "wait" +case = "compat/api:doc: latch#wait" + +[[snippet]] +doc = "libs/compat/doc/compat/mem_fn.adoc#example" +title = "Example" +case = "compat/api:doc: mem_fn#example" + +[[snippet]] +doc = "libs/compat/doc/compat/mem_fn.adoc#mem-fn" +title = "mem_fn" +case = "compat/api:doc: mem_fn#mem-fn" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#boolean-conversion" +title = "Boolean Conversion" +case = "compat/api:doc: move_only_function#boolean-conversion" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#call-operator" +title = "Call Operator" +case = "compat/api:doc: move_only_function#call-operator" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#callable-assignment" +title = "Callable Assignment" +case = "compat/api:doc: move_only_function#callable-assignment" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#default-constructor" +title = "Default Constructor" +case = "compat/api:doc: move_only_function#default-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#example" +title = "Example" +case = "compat/api:doc: move_only_function#example" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#in-place-constructor" +title = "In-Place Constructor" +case = "compat/api:doc: move_only_function#in-place-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#list-in-place-constructor" +title = "List In-Place Constructor" +case = "compat/api:doc: move_only_function#list-in-place-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#move-assignment" +title = "Move Assignment" +case = "compat/api:doc: move_only_function#move-assignment" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#move-constructor" +title = "Move Constructor" +case = "compat/api:doc: move_only_function#move-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#null-assigment" +title = "Null Assigment" +case = "compat/api:doc: move_only_function#null-assigment" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#null-constructor" +title = "Null Constructor" +case = "compat/api:doc: move_only_function#null-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#null-equality" +title = "Null Equality" +case = "compat/api:doc: move_only_function#null-equality" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#object-constructor" +title = "Object Constructor" +case = "compat/api:doc: move_only_function#object-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/move_only_function.adoc#swap" +title = "Swap" +case = "compat/api:doc: move_only_function#swap" + +[[snippet]] +doc = "libs/compat/doc/compat/nontype.adoc#example" +title = "Example" +case = "compat/api:doc: nontype#example" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#adopting-constructor" +title = "Adopting Constructor" +case = "compat/api:doc: shared_lock#adopting-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#boolean-conversion" +title = "boolean conversion" +case = "compat/api:doc: shared_lock#boolean-conversion" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#copy-assignment" +title = "Copy Assignment" +case = "compat/api:doc: shared_lock#copy-assignment" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#copy-constructor" +title = "Copy Constructor" +case = "compat/api:doc: shared_lock#copy-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#default-constructor" +title = "Default Constructor" +case = "compat/api:doc: shared_lock#default-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#deferred-constructor" +title = "Deferred Constructor" +case = "compat/api:doc: shared_lock#deferred-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#destructor" +title = "Destructor" +case = "compat/api:doc: shared_lock#destructor" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#example" +title = "Example" +case = "compat/api:doc: shared_lock#example" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#free-function-swap" +title = "Free Function swap" +case = "compat/api:doc: shared_lock#free-function-swap" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#lock" +title = "lock" +case = "compat/api:doc: shared_lock#lock" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#locking-constructor" +title = "Locking Constructor" +case = "compat/api:doc: shared_lock#locking-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#move-assignment" +title = "Move Assignment" +case = "compat/api:doc: shared_lock#move-assignment" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#move-constructor" +title = "Move Constructor" +case = "compat/api:doc: shared_lock#move-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#mutex" +title = "mutex" +case = "compat/api:doc: shared_lock#mutex" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#owns-lock" +title = "owns_lock" +case = "compat/api:doc: shared_lock#owns-lock" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#release" +title = "release" +case = "compat/api:doc: shared_lock#release" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#swap" +title = "swap" +case = "compat/api:doc: shared_lock#swap" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#try-lock" +title = "try_lock" +case = "compat/api:doc: shared_lock#try-lock" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#try-to-lock-constructor" +title = "Try-to-Lock Constructor" +case = "compat/api:doc: shared_lock#try-to-lock-constructor" + +[[snippet]] +doc = "libs/compat/doc/compat/shared_lock.adoc#unlock" +title = "unlock" +case = "compat/api:doc: shared_lock#unlock" + +[[snippet]] +doc = "libs/compat/doc/compat/to_array.adoc#example" +title = "Example" +case = "compat/api:doc: to_array#example" + +[[snippet]] +doc = "libs/compat/doc/compat/to_array.adoc#to-array" +title = "to_array" +case = "compat/api:doc: to_array#to-array" + +[[snippet]] +doc = "libs/compat/doc/compat/to_underlying.adoc#example" +title = "Example" +case = "compat/api:doc: to_underlying#example" + +[[snippet]] +doc = "libs/compat/doc/compat/to_underlying.adoc#to-underlying" +title = "to_underlying" +case = "compat/api:doc: to_underlying#to-underlying" diff --git a/tests/compat/custom_types.cpp b/tests/compat/custom_types.cpp new file mode 100644 index 00000000..927104eb --- /dev/null +++ b/tests/compat/custom_types.cpp @@ -0,0 +1,114 @@ +// mcpplibs.mwrap.boost.compat — user callables and user types through the +// type-erasing and binding components: function_ref and move_only_function +// over user function objects (stateful, move-only, overloaded on value +// category), bind_front/bind_back/invoke over user member functions and data +// members, to_underlying on user enums, shared_lock over a user SharedMutex. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.compat; + +namespace compat = boost::compat; + +namespace user { +struct account { + std::string owner; + long balance = 0; + long deposit(long amount) { + balance += amount; + return balance; + } + bool can_pay(long amount) const noexcept { return balance >= amount; } +}; + +struct audit_log { + std::vector lines; + void operator()(std::string_view s) { lines.emplace_back(s); } +}; + +class unique_task { +public: + explicit unique_task(std::string name) : name_(std::make_unique(std::move(name))) {} + std::string operator()() && { return "ran " + *name_; } +private: + std::unique_ptr name_; +}; + +// A user SharedMutex that records what the lock did. +class tracing_mutex { +public: + void lock_shared() { log.push_back("S+"); } + bool try_lock_shared() { log.push_back("S?"); return true; } + void unlock_shared() { log.push_back("S-"); } + std::vector log; +}; + +enum class level : std::uint8_t { debug = 1, error = 9 }; +} // namespace user + +TEST_CASE("function_ref over user function objects") { + user::audit_log log; + compat::function_ref sink(log); + sink("a"); + sink("b"); + CHECK(log.lines == std::vector{"a", "b"}); // refers, does not copy + user::account acc{"ann", 10}; + compat::function_ref dep(compat::nontype<&user::account::deposit>, acc); + CHECK(dep(5) == 15); + compat::function_ref pay( + compat::nontype<&user::account::can_pay>); + CHECK(pay(acc, 15)); + CHECK_FALSE(pay(acc, 16)); +} + +TEST_CASE("move_only_function over user move-only callables") { + compat::move_only_function task(user::unique_task("backup")); + CHECK(std::move(task)() == "ran backup"); + std::vector> pipeline; + pipeline.emplace_back([p = std::make_unique(3)](int x) { return x * *p; }); + pipeline.emplace_back([](int x) { return x + 1; }); + int v = 2; + for (auto& stage : pipeline) v = stage(v); + CHECK(v == 7); + compat::move_only_function owning(user::audit_log{}); + owning("x"); // the log lives inside the wrapper + CHECK(owning != nullptr); +} + +TEST_CASE("bind and invoke on user members") { + user::account acc{"bob", 0}; + auto add10 = compat::bind_back(&user::account::deposit, 10); + add10(acc); + CHECK(acc.balance == 10); + auto into_acc = compat::bind_front(&user::account::deposit, std::ref(acc)); + into_acc(5); + CHECK(acc.balance == 15); + CHECK(compat::invoke(&user::account::owner, acc) == "bob"); + CHECK(compat::invoke(&user::account::can_pay, &acc, 15L)); + static_assert(compat::is_nothrow_invocable::value); + static_assert(std::is_same_v, + std::string&>); + std::vector accounts{{"a", 1}, {"b", 2}}; + long total = 0; + for (auto& a : accounts) total += compat::mem_fn(&user::account::balance)(a); + CHECK(total == 3); +} + +TEST_CASE("to_underlying on a user enum") { + static_assert(std::is_same_v); + CHECK(compat::to_underlying(user::level::error) == 9); +} + +TEST_CASE("shared_lock over a user SharedMutex") { + user::tracing_mutex m; + { + compat::shared_lock l(m); + CHECK(l.owns_lock()); + } + { + compat::shared_lock l(m, std::try_to_lock); + l.unlock(); + l.lock(); + } + CHECK(m.log == std::vector{"S+", "S-", "S?", "S-", "S+", "S-"}); +} diff --git a/tests/compat.cpp b/tests/compat/smoke.cpp similarity index 95% rename from tests/compat.cpp rename to tests/compat/smoke.cpp index 7b19e96d..6349009d 100644 --- a/tests/compat.cpp +++ b/tests/compat/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.compat smoke — std backports (span, bind_front, function_ref) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.compat; diff --git a/tests/container/coverage.toml b/tests/container/coverage.toml new file mode 100644 index 00000000..807f46cc --- /dev/null +++ b/tests/container/coverage.toml @@ -0,0 +1,136 @@ +# tests/container/coverage.toml — official examples and documentation snippets of +# Boost.container 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "container" + +[[example]] +path = "libs/container/example/doc_custom_deque.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_custom_devector.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_custom_segtor.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_custom_small_vector.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_custom_static_vector.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_custom_string.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_custom_tree.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_custom_vector.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_devector.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_emplace.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_extended_allocators.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_flat_map.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_hub_basic.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_move_containers.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_pmr.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_recursive_containers.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_slist.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_small_vector_base.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_static_vector.cpp" +status = "todo" + +[[example]] +path = "libs/container/example/doc_type_erasure.cpp" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#container_const_reference_parameters" +title = "Parameter taken by const reference that can be changed" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#devector" +title = "['devector]" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#hub_capacity" +title = "Capacity" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#hub_introduction" +title = "Introduction" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#hub_std_hive_operations" +title = "`std::hive` operations" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#hub_unordered_insertion" +title = "Unordered insertion" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#hub_visitation" +title = "Visitation" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#null_iterators" +title = "Null Forward Iterators" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#polymorphic_memory_resources" +title = "Polymorphic Memory Resources" +status = "todo" + +[[snippet]] +doc = "libs/container/doc/container.qbk#stable_vector" +title = "['stable_vector]" +status = "todo" diff --git a/tests/container.cpp b/tests/container/smoke.cpp similarity index 97% rename from tests/container.cpp rename to tests/container/smoke.cpp index 0e80dc61..3b272416 100644 --- a/tests/container.cpp +++ b/tests/container/smoke.cpp @@ -1,6 +1,6 @@ // mcpplibs.mwrap.boost.container smoke — compiled lib linkage (pmr global_resource / // monotonic_buffer_resource TUs) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.container; diff --git a/tests/container_hash/coverage.toml b/tests/container_hash/coverage.toml new file mode 100644 index 00000000..70be4c71 --- /dev/null +++ b/tests/container_hash/coverage.toml @@ -0,0 +1,36 @@ +# tests/container_hash/coverage.toml — official examples and documentation snippets of +# Boost.container_hash 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "container_hash" + +[[example]] +path = "libs/container_hash/examples/books.cpp" +status = "todo" + +[[example]] +path = "libs/container_hash/examples/point.cpp" +status = "todo" + +[[example]] +path = "libs/container_hash/examples/point2.cpp" +status = "todo" + +[[example]] +path = "libs/container_hash/examples/portable.cpp" +status = "todo" + +[[example]] +path = "libs/container_hash/examples/template.cpp" +status = "todo" + +[[snippet]] +doc = "libs/container_hash/doc/hash/notes.adoc#hash-combine" +title = "hash_combine" +status = "todo" + +[[snippet]] +doc = "libs/container_hash/doc/hash/notes.adoc#hash-range" +title = "hash_range" +status = "todo" diff --git a/tests/container_hash.cpp b/tests/container_hash/smoke.cpp similarity index 95% rename from tests/container_hash.cpp rename to tests/container_hash/smoke.cpp index f66faaae..fe72ae00 100644 --- a/tests/container_hash.cpp +++ b/tests/container_hash/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.container_hash smoke — hash_combine, hash_value, hash container -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.container_hash; diff --git a/tests/context/coverage.toml b/tests/context/coverage.toml new file mode 100644 index 00000000..db7aa262 --- /dev/null +++ b/tests/context/coverage.toml @@ -0,0 +1,213 @@ +# tests/context/coverage.toml — official examples and documentation snippets of +# Boost.context 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "context" + +[[example]] +path = "libs/context/example/callcc/backtrace.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/echosse.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/endless_loop.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/fibonacci.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/jump.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/jump_mov.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/jump_void.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/ontop.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/ontop_void.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/parser.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/segmented.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/stack.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/callcc/throw.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/backtrace.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/circle.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/echosse.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/endless_loop.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/fibonacci.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/jump.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/jump_mov.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/jump_void.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/ontop.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/ontop_void.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/parser.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/segmented.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/stack.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[example]] +path = "libs/context/example/fiber/throw.cpp" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/callcc.qbk#cc" +title = "Context switching with call/cc" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/callcc.qbk#class-continuation" +title = "Class `continuation`" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/fiber.qbk#class-fiber" +title = "Class `fiber`" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/fiber.qbk#ff" +title = "Context switching with fibers" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/preallocated.qbk#struct-preallocated" +title = "Struct `preallocated`" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/rationale.qbk#other-apis" +title = "Other APIs" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/stack.qbk#fixedsize" +title = "Class ['fixedsize_stack]" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/stack.qbk#pooled_fixedsize" +title = "Class ['pooled_fixedsize_stack]" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/stack.qbk#protected_fixedsize" +title = "Class ['protected_fixedsize]" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/stack.qbk#segmented" +title = "Class ['segmented_stack]" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/stack.qbk#stack_context" +title = "Class ['stack_context]" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" + +[[snippet]] +doc = "libs/context/doc/stack.qbk#stack_traits" +title = "Class ['stack_traits]" +status = "unsupported" +reason = "Boost.Context needs an assembler toolchain; the package does not declare it (docs/usage.md, unsupported libraries)" diff --git a/tests/conversion/api.cpp b/tests/conversion/api.cpp new file mode 100644 index 00000000..8697c584 --- /dev/null +++ b/tests/conversion/api.cpp @@ -0,0 +1,92 @@ +// mcpplibs.mwrap.boost.conversion — polymorphic_cast (pointer and reference, +// throwing std::bad_cast), polymorphic_downcast, polymorphic_pointer_cast / +// polymorphic_pointer_downcast over raw, std and boost smart pointers, and +// implicit_cast (constexpr, no explicit conversions), after +// libs/conversion/test/{cast_test,polymorphic_cast_test,implicit_cast}.cpp. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.conversion; +import mcpplibs.mwrap.boost.smart_ptr; + +namespace { +struct Base { + virtual ~Base() = default; + virtual char kind() const { return 'B'; } +}; +struct Base2 { + virtual ~Base2() = default; + virtual char kind2() const { return '2'; } +}; +struct Derived : Base, Base2 { + char kind() const override { return 'D'; } +}; +struct Other : Base {}; +} // namespace + +TEST_CASE("polymorphic_cast: downcast and crosscast") { + Derived d; + Base* b = &d; + CHECK(boost::polymorphic_cast(b)->kind() == 'D'); + Base2* cross = boost::polymorphic_cast(b); // crosscast + CHECK(cross->kind2() == '2'); + Other o; + Base* ob = &o; + CHECK_THROWS_AS(boost::polymorphic_cast(ob), std::bad_cast); +} + +TEST_CASE("polymorphic_downcast: pointer and reference") { + Derived d; + Base* b = &d; + CHECK(boost::polymorphic_downcast(b) == &d); + const Base& cb = d; + CHECK(&boost::polymorphic_downcast(cb) == &d); +} + +TEST_CASE("polymorphic_pointer_cast / downcast") { + auto sp = std::make_shared(); + std::shared_ptr base = sp; + std::shared_ptr back = boost::polymorphic_pointer_cast(base); + CHECK(back == sp); + CHECK(boost::polymorphic_pointer_downcast(base) == sp); + CHECK(boost::polymorphic_pointer_cast(base)->kind2() == '2'); + std::shared_ptr other = std::make_shared(); + CHECK_THROWS_AS(boost::polymorphic_pointer_cast(other), std::bad_cast); + boost::shared_ptr bsp(new Derived); + CHECK(boost::polymorphic_pointer_cast(bsp)->kind() == 'D'); + Derived raw; + Base* rb = &raw; + CHECK(boost::polymorphic_pointer_cast(rb) == &raw); + CHECK(boost::polymorphic_pointer_downcast(rb) == &raw); + std::shared_ptr empty; + CHECK_THROWS_AS(boost::polymorphic_pointer_cast(empty), std::bad_cast); +} + +namespace { +struct from_int { + from_int(int v) : v(v) {} // implicit + int v; +}; +struct explicit_only { + explicit explicit_only(int) {} +}; + +template +inline constexpr bool implicit_castable = false; +template +inline constexpr bool implicit_castable(std::declval()))>> = true; +} // namespace + +TEST_CASE("implicit_cast") { + constexpr long l = boost::implicit_cast(7); + static_assert(l == 7); + CHECK(boost::implicit_cast(5).v == 5); + Derived d; + Base* up = boost::implicit_cast(&d); // upcast is implicit + CHECK(up->kind() == 'D'); + CHECK(boost::implicit_cast("x")[0] == 'x'); + static_assert(implicit_castable); + static_assert(!implicit_castable); // explicit constructor + static_assert(!implicit_castable); // no downcast + static_assert(!implicit_castable); // no void* -> T* +} diff --git a/tests/conversion/coverage.toml b/tests/conversion/coverage.toml new file mode 100644 index 00000000..9874c6f0 --- /dev/null +++ b/tests/conversion/coverage.toml @@ -0,0 +1,16 @@ +# tests/conversion/coverage.toml — official examples and documentation snippets of +# Boost.conversion 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "conversion" + +[[snippet]] +doc = "libs/conversion/doc/conversion.qbk#polymorphic-downcast" +title = "polymorphic_downcast" +case = "conversion/examples:doc: polymorphic-downcast" + +[[snippet]] +doc = "libs/conversion/doc/conversion.qbk#polymorphic-pointer-cast" +title = "polymorphic_pointer_cast" +case = "conversion/examples:doc: polymorphic-pointer-cast" diff --git a/tests/conversion/custom_types.cpp b/tests/conversion/custom_types.cpp new file mode 100644 index 00000000..e3dfc3c5 --- /dev/null +++ b/tests/conversion/custom_types.cpp @@ -0,0 +1,95 @@ +// mcpplibs.mwrap.boost.conversion — user types: a user class hierarchy, a user +// smart pointer that opts into polymorphic_pointer_cast through ADL overloads +// of static_pointer_cast / dynamic_pointer_cast (the documented customization +// point), and implicit_cast through user conversion operators. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.conversion; + +namespace zoo { +struct animal { + virtual ~animal() = default; + virtual std::string sound() const = 0; +}; +struct dog : animal { + std::string sound() const override { return "woof"; } +}; +struct cat : animal { + std::string sound() const override { return "meow"; } +}; + +// A user handle: a pointer plus a name; convertible to bool, castable by ADL. +template +class handle { +public: + handle() = default; + handle(T* p, std::string name) : p_(p), name_(std::move(name)) {} + T* operator->() const { return p_; } + T& operator*() const { return *p_; } + explicit operator bool() const { return p_ != nullptr; } + bool operator!() const { return p_ == nullptr; } + T* get() const { return p_; } + const std::string& name() const { return name_; } +private: + T* p_ = nullptr; + std::string name_; +}; + +// polymorphic_pointer_downcast asserts dynamic_pointer_cast(x) == x. +template +bool operator==(const handle& a, const handle& b) { + return a.get() == b.get(); +} + +inline int static_casts = 0, dynamic_casts = 0; + +template +handle static_pointer_cast(const handle& h) { + ++static_casts; + return handle(static_cast(h.get()), h.name()); +} +template +handle dynamic_pointer_cast(const handle& h) { + ++dynamic_casts; + return handle(dynamic_cast(h.get()), h.name()); +} + +struct celsius { + double v; +}; +struct fahrenheit { + double v; + operator celsius() const { return {(v - 32) * 5 / 9}; } // implicit conversion operator +}; +} // namespace zoo + +TEST_CASE("user hierarchy: polymorphic_cast / downcast") { + zoo::dog d; + zoo::animal* a = &d; + CHECK(boost::polymorphic_cast(a)->sound() == "woof"); + CHECK(boost::polymorphic_downcast(a) == &d); + CHECK_THROWS_AS(boost::polymorphic_cast(a), std::bad_cast); +} + +TEST_CASE("user smart pointer through the ADL customization point") { + zoo::dog d; + zoo::handle h(&d, "rex"); + zoo::static_casts = zoo::dynamic_casts = 0; + zoo::handle dh = boost::polymorphic_pointer_cast(h); + CHECK(dh->sound() == "woof"); + CHECK(dh.name() == "rex"); + CHECK(zoo::dynamic_casts == 1); + zoo::handle dd = boost::polymorphic_pointer_downcast(h); + CHECK(dd.get() == &d); + CHECK(zoo::static_casts == 1); // downcast uses static_pointer_cast (+ a debug check) + CHECK_THROWS_AS(boost::polymorphic_pointer_cast(h), std::bad_cast); +} + +TEST_CASE("implicit_cast through a user conversion operator") { + zoo::celsius c = boost::implicit_cast(zoo::fahrenheit{212}); + CHECK(c.v == doctest::Approx(100)); + zoo::dog d; + const zoo::animal& a = boost::implicit_cast(d); + CHECK(a.sound() == "woof"); +} diff --git a/tests/conversion/examples.cpp b/tests/conversion/examples.cpp new file mode 100644 index 00000000..d4745535 --- /dev/null +++ b/tests/conversion/examples.cpp @@ -0,0 +1,49 @@ +// mcpplibs.mwrap.boost.conversion — the documentation examples of +// Boost.Conversion (libs/conversion/doc/conversion.qbk) through the module +// that the 2026-11-09 hardening added (design §5.3). +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.conversion; +import mcpplibs.mwrap.boost.smart_ptr; + +namespace { +class Fruit { +public: + virtual ~Fruit() = default; + // intrusive_ptr support + friend void intrusive_ptr_add_ref(Fruit* f) { ++f->refs; } + friend void intrusive_ptr_release(Fruit* f) { + if (--f->refs == 0) delete f; + } + int refs = 0; +}; +class Banana : public Fruit { +public: + int curve = 3; +}; + +template +int f(FruitPtr fruit) { + // ... logic which leads us to believe it is a banana + auto banana = boost::polymorphic_pointer_downcast(fruit); + return banana->curve; +} +} // namespace + +TEST_CASE("doc: polymorphic-downcast") { + Banana b; + Fruit* fruit = &b; + Banana* banana = boost::polymorphic_downcast(fruit); + CHECK(banana == &b); + Banana& ref = boost::polymorphic_downcast(*fruit); + CHECK(&ref == &b); +} + +TEST_CASE("doc: polymorphic-pointer-cast") { + Banana raw; + CHECK(f(static_cast(&raw)) == 3); + CHECK(f(std::shared_ptr(std::make_shared())) == 3); + CHECK(f(boost::shared_ptr(boost::make_shared())) == 3); + CHECK(f(boost::intrusive_ptr(new Banana)) == 3); +} diff --git a/tests/convert/coverage.toml b/tests/convert/coverage.toml new file mode 100644 index 00000000..3c184afe --- /dev/null +++ b/tests/convert/coverage.toml @@ -0,0 +1,64 @@ +# tests/convert/coverage.toml — official examples and documentation snippets of +# Boost.convert 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "convert" + +[[example]] +path = "libs/convert/example/algorithms.cpp" +status = "todo" + +[[example]] +path = "libs/convert/example/default_converter.cpp" +status = "todo" + +[[example]] +path = "libs/convert/example/default_converter_fail.cpp" +status = "todo" + +[[example]] +path = "libs/convert/example/getting_serious.cpp" +status = "todo" + +[[example]] +path = "libs/convert/example/getting_started.cpp" +status = "todo" + +[[example]] +path = "libs/convert/example/lexical_cast.cpp" +status = "todo" + +[[example]] +path = "libs/convert/example/stream.cpp" +status = "todo" + +[[snippet]] +doc = "libs/convert/doc/design_notes.qbk#converter_signature" +title = "Converter Signature" +status = "todo" + +[[snippet]] +doc = "libs/convert/doc/design_notes.qbk#requirements" +title = "Requirements" +status = "todo" + +[[snippet]] +doc = "libs/convert/doc/design_notes.qbk#user-interface-signature" +title = "User Interface Signature" +status = "todo" + +[[snippet]] +doc = "libs/convert/doc/introduction.qbk#introduction" +title = "Introduction" +status = "todo" + +[[snippet]] +doc = "libs/convert/doc/performance.qbk#boost-convert-overhead" +title = "Boost.Convert Overhead" +status = "todo" + +[[snippet]] +doc = "libs/convert/doc/stream_converter.qbk#the-default-constructible-type-requirement" +title = "The ['Default Constructible] Type Requirement" +status = "todo" diff --git a/tests/convert.cpp b/tests/convert/smoke.cpp similarity index 96% rename from tests/convert.cpp rename to tests/convert/smoke.cpp index 1c251eb5..681e997d 100644 --- a/tests/convert.cpp +++ b/tests/convert/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.convert smoke — string-to-int conversion via cstring converter -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.convert; diff --git a/tests/core/api.cpp b/tests/core/api.cpp new file mode 100644 index 00000000..7d6e2d31 --- /dev/null +++ b/tests/core/api.cpp @@ -0,0 +1,553 @@ +// mcpplibs.mwrap.boost.core — the documented components of Boost.Core +// (libs/core/doc/*.qbk) that are not macros: addressof, allocator_access, +// bit, checked_delete, default_allocator, demangle, empty_value, enable_if, +// exchange, fclose_deleter, first_scalar, identity, ignore_unused, is_same, +// noinit_adaptor, noncopyable, null_deleter, nvp / serialization helpers, +// pointer_in_range, pointer_traits, snprintf, span, typeinfo, +// uncaught_exceptions, use_default, verbose_terminate_handler. The macro +// components are include_macros.cpp. Cases are "doc: #
". +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +import std; +import mcpplibs.mwrap.boost.core; + +namespace { +struct useless_type {}; +class nonaddressable { +public: + useless_type operator&() const { return {}; } +}; +template +struct X {}; +} // namespace + +TEST_CASE("doc: addressof#example") { + nonaddressable x; + nonaddressable* xp = boost::addressof(x); + CHECK(static_cast(xp) == static_cast(std::addressof(x))); + constexpr static int ci = 3; + static_assert(boost::addressof(ci) == &ci); +} + +namespace { +template > +class container : boost::empty_value::type> { +public: + using value_type = T; + using allocator_type = A; + using size_type = typename boost::allocator_size_type::type; + using difference_type = typename boost::allocator_difference_type::type; + using pointer = typename boost::allocator_pointer::type; + using const_pointer = typename boost::allocator_const_pointer::type; + const A& alloc() const { return this->get(); } +}; + +template +auto create(A& a, std::size_t n) { + auto p = a.allocate(n); + try { + boost::allocator_construct_n(a, boost::to_address(p), n); + } catch (...) { + a.deallocate(p, n); + throw; + } + return p; +} +} // namespace + +TEST_CASE("doc: allocator_access#examples") { + using C = container; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(sizeof(C) == 1); // the empty allocator takes no space + std::allocator a; + auto p = create(a, 3); + CHECK(p[2].empty()); + boost::allocator_destroy_n(a, p, 3); + boost::allocator_deallocate(a, p, 3); + static_assert(std::is_same_v>, long>); + static_assert(std::is_same_v, char>, std::allocator>); + static_assert(boost::allocator_is_always_equal_t>::value); + CHECK(boost::allocator_max_size(a) > 0); + auto q = boost::allocator_allocate(a, 1); + boost::allocator_construct(a, q, "x"); + CHECK(*q == "x"); + boost::allocator_destroy(a, q); + boost::allocator_deallocate(a, q, 1); + auto copy = boost::allocator_select_on_container_copy_construction(a); + CHECK(copy == a); +} + +TEST_CASE("doc: bit#endian") { + using boost::core::endian; + CHECK(endian::little != endian::big); + CHECK((endian::native == endian::little || endian::native == endian::big)); + static_assert(boost::core::popcount(0xF0u) == 4); + static_assert(boost::core::countl_zero(std::uint8_t(1)) == 7); + static_assert(boost::core::countr_zero(8u) == 3); + static_assert(boost::core::countl_one(std::uint8_t(0xF0)) == 4); + static_assert(boost::core::countr_one(7u) == 3); + static_assert(boost::core::bit_width(5u) == 3); + static_assert(boost::core::bit_ceil(5u) == 8); + static_assert(boost::core::bit_floor(5u) == 4); + static_assert(boost::core::has_single_bit(64u)); + static_assert(boost::core::rotl(std::uint8_t(0x81), 1) == 0x03); + static_assert(boost::core::rotr(std::uint8_t(0x81), 1) == 0xC0); + CHECK(boost::core::byteswap(std::uint32_t(0x11223344)) == 0x44332211u); + CHECK(boost::core::bit_cast(1.0f) == 0x3F800000u); +} + +namespace { +inline int deleted = 0; +struct counted { + ~counted() { ++deleted; } +}; +} // namespace + +TEST_CASE("doc: checked_delete#checked-deleter") { + deleted = 0; + boost::checked_delete(new counted); + boost::checked_deleter()(new counted); + CHECK(deleted == 2); + std::unique_ptr> p(new counted); + p.reset(); + CHECK(deleted == 3); +} + +TEST_CASE("doc: checked_delete#checked-array-deleter") { + deleted = 0; + boost::checked_array_delete(new counted[2]); + boost::checked_array_deleter()(new counted[3]); + CHECK(deleted == 5); +} + +TEST_CASE("doc: default_allocator#examples") { + std::vector> v{1, 2, 3}; + CHECK(v.size() == 3); + boost::default_allocator a; + boost::default_allocator b(a); + CHECK(a == b); + CHECK_FALSE(a != b); + static_assert(std::is_same_v::rebind::other, + boost::default_allocator>); + auto sp = std::allocate_shared(boost::default_allocator(), "hi"); + CHECK(*sp == "hi"); +} + +TEST_CASE("doc: demangle#example") { + char const* name = typeid(X).name(); + std::string d = boost::core::demangle(name); + CHECK(d.find("X") != std::string::npos); +} + +TEST_CASE("doc: demangle#example-2") { + char const* name = typeid(X).name(); + boost::core::scoped_demangled_name demangled(name); + REQUIRE(demangled.get() != nullptr); + CHECK(std::string(demangled.get()).find("X") != std::string::npos); + const char* raw = boost::core::demangle_alloc(name); + CHECK(raw != nullptr); + boost::core::demangle_free(raw); +} + +namespace { +template +class storage : boost::empty_value, boost::empty_value { +public: + storage() : boost::empty_value(boost::empty_init_t()), + boost::empty_value(boost::empty_init_t()), ptr_() {} + storage(const Compare& c, const Allocator& a) + : boost::empty_value(boost::empty_init_t(), c), + boost::empty_value(boost::empty_init_t(), a), ptr_() {} + const Ptr& pointer() const { return ptr_; } + Ptr& pointer() { return ptr_; } + const Compare& compare() const { return boost::empty_value::get(); } + const Allocator& allocator() const { return boost::empty_value::get(); } +private: + Ptr ptr_; +}; +} // namespace + +TEST_CASE("doc: empty_value#examples") { + using S = storage, std::allocator>; + static_assert(sizeof(S) == sizeof(int*)); // both empty bases are compressed + S s; + CHECK(s.pointer() == nullptr); + CHECK(s.compare()(1, 2)); + S s2{std::less(), std::allocator()}; + CHECK(s2.allocator() == std::allocator()); + boost::empty_value nonempty(boost::empty_init, 7); + CHECK(nonempty.get() == 7); +} + +// ---- enable_if ------------------------------------------------------------- + +namespace ei { +template +typename boost::enable_if_c, T>::type foo(T t) { return t; } + +template +typename boost::enable_if, const char*>::type kind(T) { return "integral"; } +template +typename boost::disable_if, const char*>::type kind(T) { return "other"; } + +template +struct A { + static constexpr int v = 0; +}; +template +struct A>::type> { + static constexpr int v = 1; +}; +template +struct A>::type> { + static constexpr int v = 2; +}; + +// Lazy: the result type is only computed when the condition holds. +template +struct mult_traits { + using type = typename T::result; +}; +struct has_result { + using result = long; +}; +template +typename boost::lazy_enable_if, mult_traits>::type lazy(T) { return 1; } +inline int lazy(int) { return 2; } + +template ::type> +constexpr bool one_byte(T) { return true; } + +template +typename boost::enable_if_has_type::type has_value_type(const T&) { return 1; } +inline int has_value_type(...) { return 0; } +} // namespace ei + +TEST_CASE("doc: enable_if#background") { CHECK(ei::foo(1.5) == 1.5); } +TEST_CASE("doc: enable_if#the-enable-if-templates") { + static_assert(std::is_same_v::type, int>); + static_assert(std::is_same_v::type, void>); + CHECK(std::string(ei::kind(1)) == "integral"); + CHECK(std::string(ei::kind(1.0)) == "other"); +} +TEST_CASE("doc: enable_if#using_enable_if") { CHECK(ei::foo(3) == 3); } +TEST_CASE("doc: enable_if#enable_if_0x") { static_assert(ei::one_byte('a')); } +TEST_CASE("doc: enable_if#enabling-template-class-specializations") { + static_assert(ei::A::v == 0); + static_assert(ei::A::v == 1); + static_assert(ei::A::v == 2); +} +TEST_CASE("doc: enable_if#overlapping-enabler-conditions") { + // integral and floating point are disjoint, so the two partial + // specializations above never overlap; kind() uses enable/disable pairs. + CHECK(std::string(ei::kind('c')) == "integral"); +} +TEST_CASE("doc: enable_if#enable_if_lazy") { + CHECK(ei::lazy(ei::has_result{}) == 1); + CHECK(ei::lazy(0) == 2); // mult_traits is never instantiated + static_assert(std::is_same_v>::type, long>); +} +TEST_CASE("doc: enable_if#compiler-workarounds") { + CHECK(ei::has_value_type(std::vector{}) == 1); + CHECK(ei::has_value_type(3) == 0); +} + +// ---- exchange -------------------------------------------------------------- + +namespace { +struct Node { + int* head_; + int* tail_; + Node(int* h, int* t) : head_(h), tail_(t) {} + Node(Node&& other) + : head_(boost::exchange(other.head_, nullptr)), tail_(boost::exchange(other.tail_, nullptr)) {} +}; +} // namespace + +TEST_CASE("doc: exchange#examples") { + int a = 0, b = 0; + Node n(&a, &b); + Node m(std::move(n)); + CHECK(m.head_ == &a); + CHECK(n.head_ == nullptr); + CHECK(n.tail_ == nullptr); +} +TEST_CASE("doc: exchange#functions") { + std::string s = "old"; + CHECK(boost::exchange(s, "new") == "old"); + CHECK(s == "new"); + constexpr int r = [] { + int v = 1; + boost::exchange(v, 2); + return v; + }(); + static_assert(r == 2); +} + +TEST_CASE("doc: fclose_deleter#example") { + auto path = std::filesystem::temp_directory_path() / "boost-m-core-fclose.txt"; + { + std::unique_ptr f{std::fopen(path.string().c_str(), "w")}; + REQUIRE(f); + std::fputs("x", f.get()); + } // fclose'd here + CHECK(std::filesystem::file_size(path) == 1); + std::filesystem::remove(path); +} + +TEST_CASE("doc: first_scalar#examples") { + std::allocator allocator; + std::allocator_traits>::rebind_alloc other(allocator); + auto ptr = other.allocate(4); + boost::alloc_construct_n(other, boost::first_scalar(boost::to_address(ptr)), 24); + int* first = boost::first_scalar(ptr); + CHECK(first == &ptr[0][0][0]); + CHECK(std::all_of(first, first + 24, [](int v) { return v == 0; })); + boost::alloc_destroy_n(other, first, 24); + other.deallocate(ptr, 4); +} + +namespace { +template +std::string print(Range&& range, Projection projection = {}) { + std::ostringstream os; + for (auto&& x : range) os << projection(x) << ' '; + return os.str(); +} +} // namespace + +TEST_CASE("doc: identity#example") { + std::vector v{1, 2}; + CHECK(print(v) == "1 2 "); + CHECK(print(v, [](int x) { return x * 10; }) == "10 20 "); + int i = 4; + static_assert(std::is_same_v); +} + +namespace { +int fun(int foo, int bar) { + boost::ignore_unused(bar); + return foo + 2; +} +} // namespace + +TEST_CASE("doc: ignore_unused#usage") { + int v1 = 0; + double v2 = 0; + boost::ignore_unused(v1, v2); + boost::ignore_unused(); +} +TEST_CASE("doc: ignore_unused#example") { CHECK(fun(1, 2) == 3); } + +TEST_CASE("doc: is_same#example") { + struct Y { + using type = int&; + }; + static_assert(boost::core::is_same::value); + static_assert(!boost::core::is_same::value); +} + +TEST_CASE("doc: noinit_adaptor#examples") { + std::vector>> v(5); + std::iota(v.begin(), v.end(), 1); + CHECK(v.back() == 5); + auto sp = std::allocate_shared(boost::noinit_adapt(std::allocator()), 4); + sp[3] = 9; + CHECK(sp[3] == 9); +} + +namespace { +class NC : private boost::noncopyable {}; +} // namespace + +TEST_CASE("doc: noncopyable#example") { + static_assert(!std::is_copy_constructible_v); + static_assert(!std::is_copy_assignable_v); + static_assert(std::is_default_constructible_v); +} + +TEST_CASE("doc: null_deleter#example") { + std::ostringstream os; + { + std::shared_ptr s(&os, boost::null_deleter()); + *s << "kept"; + } + CHECK(os.str() == "kept"); // the stream was not deleted +} + +namespace { +struct recorder { + std::vector names; + template + recorder& operator&(const boost::nvp& p) { + names.push_back(p.name()); + return *this; + } +}; +struct pt { + int x_ = 1, y_ = 2; + template + void serialize(A& archive, unsigned) { + archive & boost::make_nvp("x", x_) & boost::make_nvp("y", y_); + } +}; +} // namespace + +TEST_CASE("doc: nvp#examples") { + recorder r; + pt p; + p.serialize(r, 0); + CHECK(r.names == std::vector{"x", "y"}); + int v = 5; + auto n = boost::make_nvp("v", v); + CHECK(n.value() == 5); + n.value() = 6; + CHECK(v == 6); +} + +TEST_CASE("doc: serialization#core-version-type") { + boost::serialization::core_version_type ver(3); + unsigned int u = ver; + CHECK(u == 3); + CHECK(ver.version_ == 3); +} + +TEST_CASE("doc: pointer_in_range#examples") { + int buffer[8]; + CHECK(boost::pointer_in_range(buffer + 3, buffer, buffer + 8)); + CHECK_FALSE(boost::pointer_in_range(buffer + 8, buffer, buffer + 8)); + int other = 0; + CHECK_FALSE(boost::pointer_in_range(&other, buffer, buffer + 8)); +} + +TEST_CASE("doc: pointer_traits#examples") { + std::allocator a; + auto p = a.allocate(1); + std::allocator_traits>::construct(a, boost::to_address(p), "s"); + CHECK(*p == "s"); + std::destroy_at(p); + a.deallocate(p, 1); + static_assert(std::is_same_v::element_type, int>); + static_assert(std::is_same_v::rebind_to::type, char*>); + auto up = std::make_unique(4); + CHECK(boost::to_address(up.get()) == up.get()); +} + +TEST_CASE("doc: snprintf#example") { + char buf[10]; + int i = 12345; + int n = boost::core::snprintf(buf, sizeof(buf), "%d", i); + CHECK(n == 5); + CHECK(std::string(buf) == "12345"); + n = boost::core::snprintf(buf, sizeof(buf), "%d-%d", i, i); + CHECK(n == 11); // would overflow: the returned length exceeds the buffer + CHECK(std::strlen(buf) == 9); +} + +namespace { +// The documentation's sha1 shape, with a toy digest. +auto digest(boost::span input, boost::span output) { + std::fill(output.begin(), output.end(), 0); + for (std::size_t i = 0; i < input.size(); ++i) output[i % 4] ^= input[i]; + return output; +} +} // namespace + +TEST_CASE("doc: span#examples") { + const unsigned char data[] = {1, 2, 3, 4, 5}; + unsigned char out[4]; + auto r = digest(data, out); + CHECK(r.size() == 4); + CHECK(out[0] == (1 ^ 5)); + std::vector v{1, 2, 3, 4}; + boost::span s(v); + CHECK(s.subspan(1, 2).front() == 2); + CHECK(s.first(2).back() == 2); + CHECK(s.last<1>()[0] == 4); + CHECK(boost::as_bytes(s).size() == 4 * sizeof(int)); + boost::as_writable_bytes(s)[0] = std::byte{0}; + auto ms = boost::make_span(v.data(), 2); + CHECK(ms.size() == 2); + static_assert(boost::span::extent == 3); +} + +TEST_CASE("doc: typeinfo#example") { + using T = X; + boost::core::typeinfo const& ti = typeid(T); // BOOST_CORE_TYPEID: include_macros.cpp + CHECK(boost::core::demangled_name(ti).find("X") != std::string::npos); +} + +namespace { +inline int on_unwind = 0, on_normal = 0; +class my_class { + const unsigned int m_exception_count; +public: + my_class() : m_exception_count(boost::core::uncaught_exceptions()) {} + ~my_class() noexcept(false) { + if (m_exception_count == boost::core::uncaught_exceptions()) ++on_normal; + else ++on_unwind; + } +}; +} // namespace + +TEST_CASE("doc: uncaught_exceptions#example") { + { my_class c; } + try { + my_class c; + throw 1; + } catch (int) { + } + CHECK(on_normal == 1); + CHECK(on_unwind == 1); +} + +namespace { +template +struct adaptor { + using value = std::conditional_t, int, Value>; + using ref = std::conditional_t, value&, Ref>; +}; +} // namespace + +TEST_CASE("doc: use_default#example") { + static_assert(std::is_same_v::ref, int&>); + static_assert(std::is_same_v::ref, long&>); + static_assert(std::is_same_v::ref, const int&>); +} + +TEST_CASE("doc: verbose_terminate_handler#example") { + // Installing the handler is what the example does; calling it would end + // the process, which a test cannot observe in-process. + std::terminate_handler previous = std::set_terminate(boost::core::verbose_terminate_handler); + CHECK(std::get_terminate() == &boost::core::verbose_terminate_handler); + std::set_terminate(previous); +} + +TEST_CASE("string_view (libs/core/doc/string_view.qbk)") { + boost::core::string_view sv("hello world"); + CHECK(sv.size() == 11); + CHECK(sv.substr(6) == "world"); + CHECK(sv.find('o') == 4); + CHECK(sv.rfind('o') == 7); + CHECK(sv.starts_with("hell")); + CHECK(sv.ends_with("ld")); + CHECK(sv.contains("lo w")); + CHECK(sv.find_first_of("wr") == 6); + CHECK(sv.find_last_not_of('d') == 9); + std::string s(sv); + CHECK(s == "hello world"); + std::string_view stdsv = sv; // converts to and from std::string_view + boost::core::string_view back(stdsv); + CHECK(back == sv); + std::ostringstream os; + os << sv.substr(0, 5); + CHECK(os.str() == "hello"); + sv.remove_prefix(6); + CHECK(sv.compare("world") == 0); + static_assert(boost::core::string_view("abc").size() == 3); + boost::core::wstring_view w(L"w"); + CHECK(w.size() == 1); +} diff --git a/tests/core/coverage.toml b/tests/core/coverage.toml new file mode 100644 index 00000000..92640949 --- /dev/null +++ b/tests/core/coverage.toml @@ -0,0 +1,341 @@ +# tests/core/coverage.toml — official examples and documentation snippets of +# Boost.core 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "core" + +[[snippet]] +doc = "libs/core/doc/addressof.qbk#example" +title = "Example" +case = "core/api:doc: addressof#example" + +[[snippet]] +doc = "libs/core/doc/alignof.qbk#example" +title = "Example" +case = "core/include_macros:doc: alignof#example" + +[[snippet]] +doc = "libs/core/doc/allocator_access.qbk#examples" +title = "Examples" +case = "core/api:doc: allocator_access#examples" + +[[snippet]] +doc = "libs/core/doc/bit.qbk#endian" +title = "Endian" +case = "core/api:doc: bit#endian" + +[[snippet]] +doc = "libs/core/doc/checked_delete.qbk#checked-array-deleter" +title = "checked_array_deleter" +case = "core/api:doc: checked_delete#checked-array-deleter" + +[[snippet]] +doc = "libs/core/doc/checked_delete.qbk#checked-deleter" +title = "checked_deleter" +case = "core/api:doc: checked_delete#checked-deleter" + +[[snippet]] +doc = "libs/core/doc/default_allocator.qbk#examples" +title = "Examples" +case = "core/api:doc: default_allocator#examples" + +[[snippet]] +doc = "libs/core/doc/demangle.qbk#example" +title = "Example" +case = "core/api:doc: demangle#example" + +[[snippet]] +doc = "libs/core/doc/demangle.qbk#example-2" +title = "Example" +case = "core/api:doc: demangle#example-2" + +[[snippet]] +doc = "libs/core/doc/empty_value.qbk#examples" +title = "Examples" +case = "core/api:doc: empty_value#examples" + +[[snippet]] +doc = "libs/core/doc/enable_if.qbk#background" +title = "Background" +case = "core/api:doc: enable_if#background" + +[[snippet]] +doc = "libs/core/doc/enable_if.qbk#compiler-workarounds" +title = "Compiler workarounds" +case = "core/api:doc: enable_if#compiler-workarounds" + +[[snippet]] +doc = "libs/core/doc/enable_if.qbk#enable_if_0x" +title = "Enabling function templates in C++0x" +case = "core/api:doc: enable_if#enable_if_0x" + +[[snippet]] +doc = "libs/core/doc/enable_if.qbk#enable_if_lazy" +title = "Lazy enable_if" +case = "core/api:doc: enable_if#enable_if_lazy" + +[[snippet]] +doc = "libs/core/doc/enable_if.qbk#enabling-template-class-specializations" +title = "Enabling template class specializations" +case = "core/api:doc: enable_if#enabling-template-class-specializations" + +[[snippet]] +doc = "libs/core/doc/enable_if.qbk#overlapping-enabler-conditions" +title = "Overlapping enabler conditions" +case = "core/api:doc: enable_if#overlapping-enabler-conditions" + +[[snippet]] +doc = "libs/core/doc/enable_if.qbk#the-enable-if-templates" +title = "The enable_if templates" +case = "core/api:doc: enable_if#the-enable-if-templates" + +[[snippet]] +doc = "libs/core/doc/enable_if.qbk#using_enable_if" +title = "Using enable_if" +case = "core/api:doc: enable_if#using_enable_if" + +[[snippet]] +doc = "libs/core/doc/exchange.qbk#examples" +title = "Examples" +case = "core/api:doc: exchange#examples" + +[[snippet]] +doc = "libs/core/doc/exchange.qbk#functions" +title = "Functions" +case = "core/api:doc: exchange#functions" + +[[snippet]] +doc = "libs/core/doc/explicit_operator_bool.qbk#examples" +title = "Examples" +case = "core/include_macros:doc: explicit_operator_bool#examples" + +[[snippet]] +doc = "libs/core/doc/fclose_deleter.qbk#example" +title = "Example" +case = "core/api:doc: fclose_deleter#example" + +[[snippet]] +doc = "libs/core/doc/first_scalar.qbk#examples" +title = "Examples" +case = "core/api:doc: first_scalar#examples" + +[[snippet]] +doc = "libs/core/doc/identity.qbk#example" +title = "Example" +case = "core/api:doc: identity#example" + +[[snippet]] +doc = "libs/core/doc/ignore_unused.qbk#example" +title = "Example" +case = "core/api:doc: ignore_unused#example" + +[[snippet]] +doc = "libs/core/doc/ignore_unused.qbk#usage" +title = "Usage" +case = "core/api:doc: ignore_unused#usage" + +[[snippet]] +doc = "libs/core/doc/is_placeholder.qbk#example" +title = "Example" +case = "core/custom_types:doc: is_placeholder#example" + +[[snippet]] +doc = "libs/core/doc/is_same.qbk#example" +title = "Example" +case = "core/api:doc: is_same#example" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-error" +title = "BOOST_ERROR" +case = "core/include_macros:doc: lightweight_test#boost-error" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test" +title = "BOOST_TEST" +case = "core/include_macros:doc: lightweight_test#boost-test" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-all-eq" +title = "BOOST_TEST_ALL_EQ" +case = "core/include_macros:doc: lightweight_test#boost-test-all-eq" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-all-with" +title = "BOOST_TEST_ALL_WITH" +case = "core/include_macros:doc: lightweight_test#boost-test-all-with" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-cstr-eq" +title = "BOOST_TEST_CSTR_EQ" +case = "core/include_macros:doc: lightweight_test#boost-test-cstr-eq" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-cstr-ne" +title = "BOOST_TEST_CSTR_NE" +case = "core/include_macros:doc: lightweight_test#boost-test-cstr-ne" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-eq" +title = "BOOST_TEST_EQ" +case = "core/include_macros:doc: lightweight_test#boost-test-eq" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-ge" +title = "BOOST_TEST_GE" +case = "core/include_macros:doc: lightweight_test#boost-test-ge" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-gt" +title = "BOOST_TEST_GT" +case = "core/include_macros:doc: lightweight_test#boost-test-gt" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-le" +title = "BOOST_TEST_LE" +case = "core/include_macros:doc: lightweight_test#boost-test-le" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-lt" +title = "BOOST_TEST_LT" +case = "core/include_macros:doc: lightweight_test#boost-test-lt" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-ne" +title = "BOOST_TEST_NE" +case = "core/include_macros:doc: lightweight_test#boost-test-ne" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-no-throw" +title = "BOOST_TEST_NO_THROW" +case = "core/include_macros:doc: lightweight_test#boost-test-no-throw" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-not" +title = "BOOST_TEST_NOT" +case = "core/include_macros:doc: lightweight_test#boost-test-not" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-throws" +title = "BOOST_TEST_THROWS" +case = "core/include_macros:doc: lightweight_test#boost-test-throws" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-trait-false" +title = "BOOST_TEST_TRAIT_FALSE" +case = "core/include_macros:doc: lightweight_test#boost-test-trait-false" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-trait-same" +title = "BOOST_TEST_TRAIT_SAME" +case = "core/include_macros:doc: lightweight_test#boost-test-trait-same" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-trait-true" +title = "BOOST_TEST_TRAIT_TRUE" +case = "core/include_macros:doc: lightweight_test#boost-test-trait-true" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#boost-test-with" +title = "BOOST_TEST_WITH" +case = "core/include_macros:doc: lightweight_test#boost-test-with" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#example" +title = "Example" +case = "core/include_macros:doc: lightweight_test#example" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#example-2" +title = "Example" +case = "core/include_macros:doc: lightweight_test#example-2" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#lwt-init" +title = "lwt_init" +case = "core/include_macros:doc: lightweight_test#lwt-init" + +[[snippet]] +doc = "libs/core/doc/lightweight_test.qbk#report-errors" +title = "report_errors" +case = "core/include_macros:doc: lightweight_test#report-errors" + +[[snippet]] +doc = "libs/core/doc/no_exceptions_support.qbk#example-use" +title = "Example Use" +case = "core/include_macros:doc: no_exceptions_support#example-use" + +[[snippet]] +doc = "libs/core/doc/noinit_adaptor.qbk#examples" +title = "Examples" +case = "core/api:doc: noinit_adaptor#examples" + +[[snippet]] +doc = "libs/core/doc/noncopyable.qbk#example" +title = "Example" +case = "core/api:doc: noncopyable#example" + +[[snippet]] +doc = "libs/core/doc/null_deleter.qbk#example" +title = "Example" +case = "core/api:doc: null_deleter#example" + +[[snippet]] +doc = "libs/core/doc/nvp.qbk#examples" +title = "Examples" +case = "core/api:doc: nvp#examples" + +[[snippet]] +doc = "libs/core/doc/pointer_in_range.qbk#examples" +title = "Examples" +case = "core/api:doc: pointer_in_range#examples" + +[[snippet]] +doc = "libs/core/doc/pointer_traits.qbk#examples" +title = "Examples" +case = "core/api:doc: pointer_traits#examples" + +[[snippet]] +doc = "libs/core/doc/scoped_enum.qbk#deprecated-syntax" +title = "Deprecated syntax" +case = "core/include_macros:doc: scoped_enum#deprecated-syntax" + +[[snippet]] +doc = "libs/core/doc/scoped_enum.qbk#overview" +title = "Overview" +case = "core/include_macros:doc: scoped_enum#overview" + +[[snippet]] +doc = "libs/core/doc/serialization.qbk#core-version-type" +title = "`core_version_type`" +case = "core/api:doc: serialization#core-version-type" + +[[snippet]] +doc = "libs/core/doc/snprintf.qbk#example" +title = "Example" +case = "core/api:doc: snprintf#example" + +[[snippet]] +doc = "libs/core/doc/span.qbk#examples" +title = "Examples" +case = "core/api:doc: span#examples" + +[[snippet]] +doc = "libs/core/doc/typeinfo.qbk#example" +title = "Example" +case = "core/api:doc: typeinfo#example" + +[[snippet]] +doc = "libs/core/doc/uncaught_exceptions.qbk#example" +title = "Example" +case = "core/api:doc: uncaught_exceptions#example" + +[[snippet]] +doc = "libs/core/doc/use_default.qbk#example" +title = "Example" +case = "core/api:doc: use_default#example" + +[[snippet]] +doc = "libs/core/doc/verbose_terminate_handler.qbk#example" +title = "Example" +case = "core/api:doc: verbose_terminate_handler#example" diff --git a/tests/core/custom_types.cpp b/tests/core/custom_types.cpp new file mode 100644 index 00000000..1440ecda --- /dev/null +++ b/tests/core/custom_types.cpp @@ -0,0 +1,166 @@ +// mcpplibs.mwrap.boost.core — user types at Boost.Core's customization points: +// boost::is_placeholder specialized for a user placeholder (the documentation +// example, libs/core/doc/is_placeholder.qbk, through boost::bind), a user +// fancy pointer through pointer_traits / to_address, a user allocator through +// allocator_access, empty_value compressing user empty types, invoke_swap +// finding a user swap by ADL, ref/unwrap_ref, span over a user contiguous +// container, functor<> over a user function, and demangle of user templates. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.core; +import mcpplibs.mwrap.boost.bind; + +namespace user { +struct arg1_type {}; +constexpr arg1_type arg1{}; + +int f(int x) { return x + 1; } + +// A fancy pointer: wraps T* and is not a raw pointer. +template +struct fancy_ptr { + T* p = nullptr; + using element_type = T; + template + using rebind = fancy_ptr; + T& operator*() const { return *p; } + T* operator->() const noexcept { return p; } + static fancy_ptr pointer_to(T& r) noexcept { return {&r}; } +}; + +template +struct arena_allocator { + using value_type = T; + using pointer = fancy_ptr; + using size_type = std::uint32_t; + arena_allocator() = default; + template + arena_allocator(const arena_allocator&) noexcept {} + pointer allocate(std::size_t n) { return {std::allocator().allocate(n)}; } + void deallocate(pointer p, std::size_t n) noexcept { std::allocator().deallocate(p.p, n); } + template + bool operator==(const arena_allocator&) const noexcept { return true; } +}; + +struct empty_hasher { + std::size_t operator()(int v) const noexcept { return std::size_t(v) * 31; } +}; + +struct buffer { + int a = 1, b = 2; + int swaps = 0; + friend void swap(buffer& x, buffer& y) noexcept { + std::swap(x.a, y.a); + ++x.swaps; + ++y.swaps; + } +}; + +class small_vec { +public: + int* data() noexcept { return items; } + const int* data() const noexcept { return items; } + std::size_t size() const noexcept { return 3; } + int* begin() { return items; } + int* end() { return items + 3; } +private: + int items[3] = {7, 8, 9}; +}; + +void close_handle(int* h) noexcept { *h = -1; } + +template +struct tagged {}; +} // namespace user + +template <> +struct boost::is_placeholder { + static constexpr int value = 1; +}; + +TEST_CASE("doc: is_placeholder#example") { + CHECK(boost::bind(user::f, user::arg1)(-1) == 0); + CHECK(boost::bind(user::f, user::arg1)(41) == 42); +} + +TEST_CASE("pointer_traits and to_address over a user fancy pointer") { + int v = 5; + user::fancy_ptr p{&v}; + static_assert(std::is_same_v>::element_type, int>); + static_assert(std::is_same_v>::rebind_to::type, + user::fancy_ptr>); + CHECK(boost::to_address(p) == &v); + CHECK(boost::pointer_traits>::pointer_to(v).p == &v); +} + +TEST_CASE("allocator_access over a user allocator") { + using A = user::arena_allocator; + static_assert(std::is_same_v, user::fancy_ptr>); + static_assert(std::is_same_v, std::uint32_t>); + static_assert(std::is_same_v, user::arena_allocator>); + static_assert(std::is_same_v, user::fancy_ptr>); + A a; + auto p = boost::allocator_allocate(a, 2); + boost::allocator_construct_n(a, boost::to_address(p), 2); + CHECK(p.p[1].empty()); + boost::allocator_destroy_n(a, boost::to_address(p), 2); + boost::allocator_deallocate(a, p, 2); + CHECK(boost::allocator_max_size(a) > 0); +} + +TEST_CASE("empty_value compresses user empty types") { + struct holder : boost::empty_value, boost::empty_value, 1> { + int* storage = nullptr; + holder() : boost::empty_value(boost::empty_init), + boost::empty_value, 1>(boost::empty_init) {} + }; + static_assert(sizeof(holder) == sizeof(int*)); + holder h; + CHECK(h.boost::empty_value::get()(2) == 62); + static_assert(boost::use_empty_value_base::value); +} + +TEST_CASE("invoke_swap finds the user swap by ADL") { + user::buffer x, y; + y.a = 9; + boost::core::invoke_swap(x, y); + CHECK(x.a == 9); + CHECK(x.swaps == 1); +} + +TEST_CASE("ref / unwrap_ref with user objects") { + user::buffer b; + auto r = boost::ref(b); + r.get().a = 4; + CHECK(b.a == 4); + static_assert(boost::is_reference_wrapper::value); + static_assert(std::is_same_v::type, user::buffer>); + CHECK(&boost::unwrap_ref(r) == &b); + auto cr = boost::cref(b); + CHECK(cr.get().a == 4); +} + +TEST_CASE("span over a user contiguous container") { + user::small_vec v; + boost::span s(v.data(), v.size()); + CHECK(s[1] == 8); + boost::span cs(s); + CHECK(std::accumulate(cs.begin(), cs.end(), 0) == 24); + boost::span fixed(v.data(), 3); + CHECK(fixed.back() == 9); +} + +TEST_CASE("functor<> over a user function") { + int h = 3; + std::unique_ptr> guard(&h); + guard.reset(); + CHECK(h == -1); + static_assert(std::is_empty_v>); +} + +TEST_CASE("demangle and type_name of user templates") { + std::string d = boost::core::demangle(typeid(user::tagged).name()); + CHECK(d.find("user::tagged") != std::string::npos); + CHECK(boost::core::type_name>().find("tagged") != std::string::npos); +} diff --git a/tests/core/include_macros.cpp b/tests/core/include_macros.cpp new file mode 100644 index 00000000..f2e360ac --- /dev/null +++ b/tests/core/include_macros.cpp @@ -0,0 +1,199 @@ +// boost.core macro components — pure include: a named module cannot export +// macros (docs/usage.md §3), and this TU does not import, so no include+import +// mixing on gcc. Covers alignof (BOOST_CORE_ALIGNOF), explicit_operator_bool, +// lightweight_test(_trait), no_exceptions_support (BOOST_TRY...), scoped_enum +// and BOOST_CORE_TYPEID. +// +// lightweight_test keeps a process-wide error count that boost::report_errors() +// returns; the last case (cases run in declaration order) checks that count. +// Two failures are made on purpose (BOOST_ERROR, a failing BOOST_TEST_NE) +// to see them counted; lightweight_test reports them on stderr. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE("doc: alignof#example") { + constexpr std::size_t alignment_of_double = BOOST_CORE_ALIGNOF(double); + static_assert(alignment_of_double == alignof(double)); +} + +namespace { +template +class my_ptr { +public: + explicit my_ptr(T* p = nullptr) : m_p(p) {} + BOOST_EXPLICIT_OPERATOR_BOOL() + bool operator!() const { return !m_p; } +private: + T* m_p; +}; +struct flag { + bool on; + BOOST_CONSTEXPR_EXPLICIT_OPERATOR_BOOL() + constexpr bool operator!() const noexcept { return !on; } +}; +} // namespace + +TEST_CASE("doc: explicit_operator_bool#examples") { + int i = 0; + my_ptr p(&i), q; + CHECK(static_cast(p)); + CHECK_FALSE(static_cast(q)); + if (p) CHECK(true); + static_assert(!std::is_convertible_v, int>); // no implicit conversion + static_assert(static_cast(flag{true})); +} + +namespace { +int sqr(int x) { return x * x; } +template +struct X2 { + typedef T type; +}; +} // namespace + +TEST_CASE("doc: lightweight_test#example") { + BOOST_TEST(sqr(2) == 4); + BOOST_TEST_EQ(sqr(-3), 9); +} +TEST_CASE("doc: lightweight_test#boost-test") { BOOST_TEST(1 + 1 == 2); } +TEST_CASE("doc: lightweight_test#boost-test-not") { BOOST_TEST_NOT(1 + 1 == 3); } +TEST_CASE("doc: lightweight_test#boost-error") { BOOST_ERROR("deliberate failure #1"); } +TEST_CASE("doc: lightweight_test#boost-test-eq") { BOOST_TEST_EQ(std::string("a"), "a"); } +TEST_CASE("doc: lightweight_test#boost-test-ne") { + BOOST_TEST_NE(1, 2); + BOOST_TEST_NE(3, 3); // deliberate failure #2 +} +TEST_CASE("doc: lightweight_test#boost-test-lt") { BOOST_TEST_LT(1, 2); } +TEST_CASE("doc: lightweight_test#boost-test-le") { BOOST_TEST_LE(2, 2); } +TEST_CASE("doc: lightweight_test#boost-test-gt") { BOOST_TEST_GT(3, 2); } +TEST_CASE("doc: lightweight_test#boost-test-ge") { BOOST_TEST_GE(3, 3); } +TEST_CASE("doc: lightweight_test#boost-test-cstr-eq") { + const char a[] = "abc"; + std::string b = "abc"; + BOOST_TEST_CSTR_EQ(a, b.c_str()); +} +TEST_CASE("doc: lightweight_test#boost-test-cstr-ne") { BOOST_TEST_CSTR_NE("abc", "abd"); } +TEST_CASE("doc: lightweight_test#boost-test-with") { + BOOST_TEST_WITH(1.0, 1.0 + 1e-12, [](double a, double b) { return a - b < 1e-9 && b - a < 1e-9; }); +} +TEST_CASE("doc: lightweight_test#boost-test-all-eq") { + std::vector a{1, 2}, b{1, 2}; + BOOST_TEST_ALL_EQ(a.begin(), a.end(), b.begin(), b.end()); +} +TEST_CASE("doc: lightweight_test#boost-test-all-with") { + std::vector a{1, 2}, b{2, 4}; + BOOST_TEST_ALL_WITH(a.begin(), a.end(), b.begin(), b.end(), [](int x, int y) { return 2 * x == y; }); +} +TEST_CASE("doc: lightweight_test#boost-test-throws") { + BOOST_TEST_THROWS(throw std::runtime_error("x"), std::runtime_error); +} +TEST_CASE("doc: lightweight_test#boost-test-no-throw") { BOOST_TEST_NO_THROW(sqr(3)); } +TEST_CASE("doc: lightweight_test#lwt-init") { boost::core::lwt_init(); } +TEST_CASE("doc: lightweight_test#example-2") { + BOOST_TEST_TRAIT_TRUE((boost::is_same::type, int>)); + BOOST_TEST_TRAIT_SAME(X2::type, int); +} +TEST_CASE("doc: lightweight_test#boost-test-trait-true") { BOOST_TEST_TRAIT_TRUE((std::is_integral)); } +TEST_CASE("doc: lightweight_test#boost-test-trait-false") { BOOST_TEST_TRAIT_FALSE((std::is_integral)); } +TEST_CASE("doc: lightweight_test#boost-test-trait-same") { BOOST_TEST_TRAIT_SAME(int, signed int); } + +namespace { +int classify(int what) { + int r = 0; + BOOST_TRY { + if (what == 1) throw std::bad_alloc(); + if (what == 2) throw std::runtime_error("e"); + r = 3; + } + BOOST_CATCH(const std::bad_alloc&) { + r = 1; + } + BOOST_CATCH(const std::exception&) { + r = 2; + } + BOOST_CATCH_END + return r; +} +int rethrows() { + BOOST_TRY { + throw std::bad_alloc(); + } + BOOST_CATCH(const std::bad_alloc&) { + BOOST_RETHROW + } + BOOST_CATCH_END + return 0; +} +} // namespace + +TEST_CASE("doc: no_exceptions_support#example-use") { + CHECK(classify(0) == 3); + CHECK(classify(1) == 1); + CHECK(classify(2) == 2); + CHECK_THROWS_AS(rethrows(), std::bad_alloc); +} + +namespace { +BOOST_SCOPED_ENUM_UT_DECLARE_BEGIN(future_errc, unsigned int){ + broken_promise, future_already_retrieved, promise_already_satisfied, no_state} +BOOST_SCOPED_ENUM_DECLARE_END(future_errc) + +BOOST_SCOPED_ENUM_DECLARE_BEGIN(enum_scoped){a, b} +BOOST_SCOPED_ENUM_DECLARE_END(enum_scoped) + +enum enum_regular { REGULAR_A, REGULAR_B }; +int regular_or_int(enum_regular) { return 1; } +int regular_or_int(int) { return 2; } +int scoped_or_int(enum_scoped) { return 3; } +int scoped_or_int(int) { return 4; } + +BOOST_SCOPED_ENUM_UT_DECLARE_BEGIN(algae, char){green, red, cyan} +BOOST_SCOPED_ENUM_DECLARE_END(algae) +} // namespace + +TEST_CASE("doc: scoped_enum#overview") { + future_errc ev = future_errc::no_state; + int r = 0; + switch (boost::native_value(ev)) { + case future_errc::no_state: r = 1; break; + default: break; + } + CHECK(r == 1); + unsigned int val = boost::underlying_cast(ev); + CHECK(val == 3u); + static_assert(std::is_same_v::type, unsigned int>); + algae sample(algae::red); + sample = algae::green; + CHECK(boost::native_value(sample) == algae::green); +} +TEST_CASE("doc: scoped_enum#deprecated-syntax") { + CHECK(regular_or_int(REGULAR_A) == 1); + CHECK(scoped_or_int(enum_scoped::a) == 3); // C++11: the scoped overload + CHECK(scoped_or_int(enum_scoped(enum_scoped::a)) == 3); + static_assert(std::is_same_v); +} + +TEST_CASE("BOOST_CORE_TYPEID") { + using T = X2; // a macro argument cannot carry the comma + boost::core::typeinfo const& ti = BOOST_CORE_TYPEID(T); + CHECK(boost::core::demangled_name(ti).find("X2") != std::string::npos); +} + +TEST_CASE("doc: lightweight_test#report-errors") { + // The two deliberate failures above, and nothing else. + CHECK(boost::report_errors() == 2); +} diff --git a/tests/core.cpp b/tests/core/smoke.cpp similarity index 95% rename from tests/core.cpp rename to tests/core/smoke.cpp index 48a8c056..6be2143e 100644 --- a/tests/core.cpp +++ b/tests/core/smoke.cpp @@ -1,7 +1,7 @@ // mcpplibs.mwrap.boost.core smoke — exchange, ignore_unused, core::swap // (BOOST_VERSION / BOOST_LIB_VERSION moved to mcpplibs.mwrap.boost.version module — C5 2026-09-09; // see tests/version.cpp for those constants.) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.core; diff --git a/tests/coroutine2/coverage.toml b/tests/coroutine2/coverage.toml new file mode 100644 index 00000000..4f302799 --- /dev/null +++ b/tests/coroutine2/coverage.toml @@ -0,0 +1,97 @@ +# tests/coroutine2/coverage.toml — official examples and documentation snippets of +# Boost.coroutine2 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "coroutine2" + +[[example]] +path = "libs/coroutine2/example/fibonacci.cpp" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[example]] +path = "libs/coroutine2/example/layout.cpp" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[example]] +path = "libs/coroutine2/example/parser.cpp" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[example]] +path = "libs/coroutine2/example/same_fringe.cpp" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[example]] +path = "libs/coroutine2/example/segmented.cpp" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/asymmetric.qbk#asymmetric" +title = "Asymmetric coroutine" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/asymmetric.qbk#pull_coro" +title = "Class `coroutine<>::pull_type`" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/asymmetric.qbk#push_coro" +title = "Class `coroutine<>::push_type`" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/coroutine.qbk#coroutine" +title = "Coroutine" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/motivation.qbk#motivation" +title = "Motivation" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/stack.qbk#fixedsize" +title = "Class ['fixedsize_stack]" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/stack.qbk#pooled_fixedsize" +title = "Class ['pooled_fixedsize_stack]" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/stack.qbk#protected_fixedsize" +title = "Class ['protected_fixedsize]" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/stack.qbk#segmented" +title = "Class ['segmented_stack]" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/stack.qbk#stack_context" +title = "Class ['stack_context]" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" + +[[snippet]] +doc = "libs/coroutine2/doc/stack.qbk#stack_traits" +title = "Class ['stack_traits]" +status = "unsupported" +reason = "Boost.Coroutine2 is built on Boost.Context, which the package does not declare (docs/usage.md)" diff --git a/tests/crc/api.cpp b/tests/crc/api.cpp new file mode 100644 index 00000000..5986d090 --- /dev/null +++ b/tests/crc/api.cpp @@ -0,0 +1,85 @@ +// mcpplibs.mwrap.boost.crc — the documented interface: boost::crc(), +// augmented_crc(), crc_basic (theoretical computer), crc_optimal (table +// driven), the predefined samples checked against the CRC catalogue values for +// "123456789", and the member functions of both computer templates. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.crc; + +namespace { +const char check_data[] = "123456789"; +constexpr std::size_t check_len = 9; +} // namespace + +TEST_CASE("doc: crc_function") { + CHECK(boost::crc<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true>(check_data, check_len) == + 0xCBF43926u); + CHECK(boost::crc<16, 0x8005, 0, 0, true, true>(check_data, check_len) == 0xBB3Du); + CHECK(boost::crc<16, 0x1021, 0xFFFF, 0, false, false>(check_data, check_len) == 0x29B1u); +} + +TEST_CASE("doc: acrc_function") { + // An augmented CRC appends Bits zero bits to the message: the remainder of + // message + its own CRC is zero. + unsigned char msg[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0}; + auto c = boost::augmented_crc<16, 0x1021u>(msg, sizeof msg); + CHECK(c == boost::crc<16, 0x1021, 0, 0, false, false>(check_data, check_len)); + msg[9] = static_cast(c >> 8); + msg[10] = static_cast(c & 0xFF); + CHECK(boost::augmented_crc<16, 0x1021u>(msg, sizeof msg) == 0u); +} + +TEST_CASE("doc: crc_basic") { + boost::crc_basic<16> c(0x1021, 0xFFFF, 0, false, false); + CHECK(c.get_truncated_polynominal() == 0x1021); + CHECK(c.get_initial_remainder() == 0xFFFF); + CHECK(c.get_final_xor_value() == 0); + CHECK_FALSE(c.get_reflect_input()); + CHECK_FALSE(c.get_reflect_remainder()); + c.process_bytes(check_data, check_len); + CHECK(c.checksum() == 0x29B1u); + c.reset(); + CHECK(c.get_interim_remainder() == 0xFFFF); + c.process_block(check_data, check_data + check_len); + CHECK(c.checksum() == 0x29B1u); + c.reset(0); + for (char ch : std::string_view(check_data, check_len)) + c.process_byte(static_cast(ch)); + CHECK(c.checksum() == 0x31C3u); // initial remainder 0: XMODEM + boost::crc_basic<16> bits(0x1021, 0xFFFF, 0, false, false); + bits.process_bits(0xA5, 8); + bits.process_bit(true); + CHECK(bits.get_interim_remainder() != 0xFFFF); +} + +TEST_CASE("doc: crc_optimal") { + boost::crc_optimal<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true> c; + static_assert(decltype(c)::bit_count == 32); + CHECK(c.get_truncated_polynominal() == 0x04C11DB7u); + CHECK(c.get_reflect_input()); + c = std::for_each(check_data, check_data + check_len, c); + CHECK(c() == 0xCBF43926u); + c.reset(); + c.process_bytes(check_data, 4); + c.process_block(check_data + 4, check_data + check_len); + CHECK(c.checksum() == 0xCBF43926u); + c.reset(); + c(static_cast('1')); + c.process_byte(static_cast('2')); + CHECK(c.get_interim_remainder() != 0xFFFFFFFFu); +} + +TEST_CASE("doc: crc_samples") { + auto run = [](auto computer) { + computer.process_bytes(check_data, check_len); + return static_cast(computer.checksum()); + }; + CHECK(run(boost::crc_16_type()) == 0xBB3Du); + CHECK(run(boost::crc_ccitt_false_t()) == 0x29B1u); + CHECK(run(boost::crc_ccitt_type()) == 0x29B1u); + CHECK(run(boost::crc_ccitt_true_t()) == 0x2189u); + CHECK(run(boost::crc_xmodem_t()) == 0x31C3u); + CHECK(run(boost::crc_xmodem_type()) == run(boost::crc_optimal<16, 0x8408, 0, 0, true, true>())); + CHECK(run(boost::crc_32_type()) == 0xCBF43926u); +} diff --git a/tests/crc/coverage.toml b/tests/crc/coverage.toml new file mode 100644 index 00000000..cc9f2a8c --- /dev/null +++ b/tests/crc/coverage.toml @@ -0,0 +1,39 @@ +# tests/crc/coverage.toml — official examples and documentation snippets of +# Boost.crc 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "crc" + +[[example]] +path = "libs/crc/crc_example.cpp" +case = "crc/examples:example: crc_example" + +[[example]] +path = "libs/crc/doc/crc_examples.cpp" +case = "crc/examples:example: crc_examples" + +[[snippet]] +doc = "libs/crc/doc/crc.qbk#acrc_function" +title = "Augmented-CRC Function" +case = "crc/api:doc: acrc_function" + +[[snippet]] +doc = "libs/crc/doc/crc.qbk#crc_basic" +title = "Theoretical CRC Computer" +case = "crc/api:doc: crc_basic" + +[[snippet]] +doc = "libs/crc/doc/crc.qbk#crc_function" +title = "CRC Function" +case = "crc/api:doc: crc_function" + +[[snippet]] +doc = "libs/crc/doc/crc.qbk#crc_optimal" +title = "Optimized CRC Computer" +case = "crc/api:doc: crc_optimal" + +[[snippet]] +doc = "libs/crc/doc/crc.qbk#crc_samples" +title = "Pre-Defined CRC Samples" +case = "crc/api:doc: crc_samples" diff --git a/tests/crc/custom_types.cpp b/tests/crc/custom_types.cpp new file mode 100644 index 00000000..3fb0baa9 --- /dev/null +++ b/tests/crc/custom_types.cpp @@ -0,0 +1,75 @@ +// mcpplibs.mwrap.boost.crc — user-chosen CRC parameters and user data: +// crc_optimal / crc_basic instantiated with parameters the library does not +// predefine (CRC-8, CRC-32C, CRC-64/XZ from the CRC catalogue), optimal and +// basic agreeing on them, and CRCs over user-defined record types. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.crc; + +namespace user { +using crc8 = boost::crc_optimal<8, 0x07, 0, 0, false, false>; +using crc32c = boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true>; +using crc64_xz = boost::crc_optimal<64, 0x42F0E1EBA9EA3693ull, ~0ull, ~0ull, true, true>; + +struct record { + std::uint32_t id; + std::uint16_t flags; + char name[6]; +}; + +template +auto checksum_of(const record& r) { + Crc c; + c.process_bytes(&r, sizeof r); + return c.checksum(); +} + +// A user byte sink that feeds a CRC computer through its function-call form. +template +class crc_sink { +public: + void write(std::span bytes) { + for (std::byte b : bytes) crc_(static_cast(b)); + } + auto value() const { return crc_.checksum(); } +private: + Crc crc_; +}; +} // namespace user + +TEST_CASE("catalogue parameters the library does not predefine") { + const char data[] = "123456789"; + user::crc8 c8; + c8.process_bytes(data, 9); + CHECK(c8.checksum() == 0xF4u); + user::crc32c c32; + c32.process_bytes(data, 9); + CHECK(c32.checksum() == 0xE3069283u); + user::crc64_xz c64; + c64.process_bytes(data, 9); + CHECK(c64.checksum() == 0x995DC9BBDF1939FAull); +} + +TEST_CASE("crc_basic agrees with crc_optimal on user parameters") { + const char data[] = "The quick brown fox"; + boost::crc_basic<32> basic(0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true); + basic.process_bytes(data, sizeof data - 1); + user::crc32c optimal; + optimal.process_bytes(data, sizeof data - 1); + CHECK(basic.checksum() == optimal.checksum()); + CHECK(boost::crc<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true>(data, sizeof data - 1) == + optimal.checksum()); +} + +TEST_CASE("CRCs over user record types and a user sink") { + user::record a{7, 1, "alpha"}; + user::record b = a; + CHECK(user::checksum_of(a) == user::checksum_of(b)); + b.flags = 2; + CHECK(user::checksum_of(a) != user::checksum_of(b)); + user::crc_sink sink; + const std::string text = "123456789"; + sink.write(std::as_bytes(std::span(text))); + CHECK(sink.value() == 0xCBF43926u); +} diff --git a/tests/crc/examples.cpp b/tests/crc/examples.cpp new file mode 100644 index 00000000..24f69ca0 --- /dev/null +++ b/tests/crc/examples.cpp @@ -0,0 +1,100 @@ +// mcpplibs.mwrap.boost.crc — the official examples of Boost.CRC as test +// cases: libs/crc/crc_example.cpp (CRC-32 of files, read in blocks) and the +// three snippets of libs/crc/doc/crc_examples.cpp. is included for +// the va_* macros, which no module can export. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +import std; +import mcpplibs.mwrap.boost.crc; + +TEST_CASE("example: crc_example") { + const auto dir = std::filesystem::temp_directory_path() / + ("boost-m-crc-" + std::to_string(std::random_device{}())); + std::filesystem::create_directories(dir); + const auto a = dir / "a.bin", b = dir / "b.bin"; + std::ofstream(a, std::ios::binary) << "12345"; + std::ofstream(b, std::ios::binary) << "6789"; + constexpr std::streamsize buffer_size = 4; // smaller than a file: several reads + boost::crc_32_type result; + for (const auto& path : {a, b}) { + std::ifstream ifs(path, std::ios_base::binary); + REQUIRE(ifs); + do { + char buffer[buffer_size]; + ifs.read(buffer, buffer_size); + result.process_bytes(buffer, static_cast(ifs.gcount())); + } while (ifs); + } + std::ostringstream out; + out << std::hex << std::uppercase << result.checksum(); + CHECK(out.str() == "CBF43926"); // CRC-32 of "123456789" + std::filesystem::remove_all(dir); +} + +namespace { +std::pair crc_16_and_xmodem(void const* b, std::size_t l) { + std::pair result; + boost::crc_basic<16> crc1(0x8005u, 0u, 0u, true, true); + crc1.process_bytes(b, l); + result.first = crc1.checksum(); + crc1 = boost::crc_basic<16>(0x8408u, crc1.get_initial_remainder(), + crc1.get_final_xor_value(), crc1.get_reflect_input(), + crc1.get_reflect_remainder()); + crc1.process_bytes(b, l); + result.second = crc1.checksum(); + return result; +} + +unsigned combined_crc_16(unsigned block_count, ...) { + boost::crc_basic<16> crc1(0x8005u, 0u, 0u, true, true); + va_list ap; + va_start(ap, block_count); + while (block_count--) { + void const* const bs = va_arg(ap, void const*); + std::size_t const bl = va_arg(ap, std::size_t); + crc1.process_bytes(bs, bl); + } + va_end(ap); + return crc1.checksum(); +} + +unsigned combined_acrc_16(int block_count, ...) { + va_list ap; + unsigned result = 0u; + va_start(ap, block_count); + if (block_count > 0) { + void const* bs = va_arg(ap, void const*); + std::size_t bl = va_arg(ap, std::size_t); + result = boost::augmented_crc<16, 0x1021u>(bs, bl); + while (--block_count) { + bs = va_arg(ap, void const*); + bl = va_arg(ap, std::size_t); + result = boost::augmented_crc<16, 0x1021u>(bs, bl, result); + } + } + va_end(ap); + return result; +} +} // namespace + +TEST_CASE("example: crc_examples") { + static const char data[] = "123456789"; + // crc_basic_reuse: one object re-parameterized by assignment. + auto r = crc_16_and_xmodem(data, 9); + boost::crc_16_type crc16; + crc16.process_bytes(data, 9); + CHECK(r.first == crc16.checksum()); + boost::crc_xmodem_type xm; + xm.process_bytes(data, 9); + CHECK(r.second == xm.checksum()); + // crc_piecemeal_run: the data does not have to be in one block. + CHECK(combined_crc_16(3u, static_cast(data), std::size_t{2}, + static_cast(data + 2), std::size_t{3}, + static_cast(data + 5), std::size_t{4}) == crc16.checksum()); + // acrc_piecemeal_run: augmented_crc chained through its initial remainder. + unsigned whole = boost::augmented_crc<16, 0x1021u>(data, 9); + CHECK(combined_acrc_16(2, static_cast(data), std::size_t{4}, + static_cast(data + 4), std::size_t{5}) == whole); + CHECK(combined_acrc_16(0) == 0u); +} diff --git a/tests/crc.cpp b/tests/crc/smoke.cpp similarity index 95% rename from tests/crc.cpp rename to tests/crc/smoke.cpp index 373ce6aa..39527907 100644 --- a/tests/crc.cpp +++ b/tests/crc/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.crc smoke — CRC computation -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.crc; diff --git a/tests/decimal/coverage.toml b/tests/decimal/coverage.toml new file mode 100644 index 00000000..c87201b8 --- /dev/null +++ b/tests/decimal/coverage.toml @@ -0,0 +1,832 @@ +# tests/decimal/coverage.toml — official examples and documentation snippets of +# Boost.decimal 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "decimal" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/addition.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/adl.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/basic_arithmetic.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/basic_construction.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/binary_float_conversions.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/bit_conversions.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/charconv.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/charconv_cohort_preservation.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/debugger.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/decompose_frexp10_normalize.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/first_example.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/fmt_format.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/format.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/integral_conversions.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/literals.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/numerical_parsing.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/print.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/promotion.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/rounding_mode.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/rounding_mode_compile_time.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/statistics.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/doc/modules/ROOT/examples/to_from_file.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/addition.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/adl.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/basic_arithmetic.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/basic_construction.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/binary_float_conversions.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/bit_conversions.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/charconv.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/charconv_cohort_preservation.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/debugger.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/decompose_frexp10_normalize.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/first_example.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/fmt_format.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/format.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/integral_conversions.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/literals.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/numerical_parsing.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/print.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/promotion.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/rounding_mode.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/rounding_mode_compile_time.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/statistics.cpp" +status = "todo" + +[[example]] +path = "libs/decimal/example/to_from_file.cpp" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/decimal_float_and_sqrt.md#0-1-in-decimal32-exact" +title = "0.1 in decimal32 (exact)" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/decimal_float_and_sqrt.md#0-1-why-float-double-cannot-represent-it-exactly" +title = "0.1: Why float/double cannot represent it exactly" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/decimal_float_and_sqrt.md#1-numeric-meaning" +title = "1. Numeric meaning" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/decimal_float_and_sqrt.md#4-summary-comparison" +title = "4. Summary comparison" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/decimal_float_and_sqrt.md#5-2-shared-90-entry-table" +title = "5.2 Shared 90-Entry Table" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/decimal_float_and_sqrt.md#7-1-implementation-files" +title = "7.1 Implementation Files" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/decimal_float_and_sqrt.md#8-algorithm-flow-summary" +title = "8. Algorithm Flow Summary" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/decimal_float_and_sqrt.md#binary32-float-32-bits" +title = "Binary32 (float) — 32 bits" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/decimal_float_and_sqrt.md#decimal32-bid-32-bits-boost-decimal-internal-bits" +title = "Decimal32 (BID) — 32 bits (Boost.Decimal internal `bits_`)" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/basics.adoc#construction-from-binary-floating-point" +title = "Construction from Binary Floating Point" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/basics.adoc#construction-from-coefficient-and-exponent" +title = "Construction from Coefficient and Exponent" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/basics.adoc#construction-from-integer" +title = "Construction from Integer" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/basics.adoc#using-the-library" +title = "Using the Library" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cfenv.adoc#cfenv" +title = "" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/charconv.adoc#charconv_limits" +title = "Formatting Limits" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/charconv.adoc#chars_format" +title = "chars_format" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/charconv.adoc#from_chars" +title = "from_chars" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/charconv.adoc#from_chars_result" +title = "from_chars_result" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/charconv.adoc#to_chars" +title = "to_chars" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/charconv.adoc#to_chars_result" +title = "to_chars_result" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath" +title = "Basic Operations" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath_classification" +title = "Classification and Comparison" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath_error_funcs" +title = "Error and Gamma Functions" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath_exp" +title = "Exponential Functions" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath_hyperbolic_funcs" +title = "Hyperbolic Functions" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath_manipulation_funcs" +title = "Floating Point Manipulation Functions" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath_nearest_int" +title = "Nearest integer floating point operations" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath_power_funcs" +title = "Power Functions" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath_spec_fun" +title = "Special Functions" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#cmath_trig_funcs" +title = "Trigonometric Functions" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#comparetotal" +title = "`comparetotal`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#decompose" +title = "`decompose`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#frexp10" +title = "`frexp10`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#issignaling" +title = "`issignaling`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#normalize" +title = "`normalize`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#quantexp" +title = "`quantexp`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#quantized" +title = "`quantized`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#read-payload" +title = "`read_payload`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#rescale" +title = "`rescale`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cmath.adoc#samequantum" +title = "`samequantum`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/cohorts.adoc#cohorts" +title = "Cohort Preserving `` Example" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/config.adoc#configuration_user" +title = "User Configurable Macros" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/conversions.adoc#conversions" +title = "Bit Conversions Example" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/debugger.adoc#debug" +title = "LLDB" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/debugger.adoc#example" +title = "Example" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/debugger.adoc#gdb" +title = "GDB" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal128_t.adoc#compound-operators" +title = "Compound Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal128_t.adoc#construction-from-integral-type" +title = "Construction From Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal128_t.adoc#construction-from-string" +title = "Construction From String" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal128_t.adoc#construction-to-and-from-binary-floating-point-type" +title = "Construction to and from binary floating-point type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal128_t.adoc#conversion-to-integral-type" +title = "Conversion to Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal128_t.adoc#conversion-to-other-decimal-types" +title = "Conversion to Other Decimal Types" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal128_t.adoc#decimal128_t" +title = "Description" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal128_t.adoc#increment-and-decrement-operators" +title = "Increment and Decrement Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal32_t.adoc#compound-operators" +title = "Compound Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal32_t.adoc#construction-from-integral-type" +title = "Construction From Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal32_t.adoc#construction-from-string" +title = "Construction From String" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal32_t.adoc#construction-to-and-from-binary-floating-point-type" +title = "Construction to and from binary floating-point type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal32_t.adoc#conversion-to-integral-type" +title = "Conversion to Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal32_t.adoc#conversion-to-other-decimal-types" +title = "Conversion to Other Decimal Types" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal32_t.adoc#decimal32_t" +title = "Description" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal32_t.adoc#increment-and-decrement-operators" +title = "Increment and Decrement Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal64_t.adoc#compound-operators" +title = "Compound Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal64_t.adoc#construction-from-integral-type" +title = "Construction From Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal64_t.adoc#construction-from-string" +title = "Construction From String" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal64_t.adoc#construction-to-and-from-binary-floating-point-type" +title = "Construction to and from binary floating-point type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal64_t.adoc#conversion-to-integral-type" +title = "Conversion to Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal64_t.adoc#conversion-to-other-decimal-types" +title = "Conversion to Other Decimal Types" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal64_t.adoc#decimal64_t" +title = "Description" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal64_t.adoc#increment-and-decrement-operators" +title = "Increment and Decrement Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast128_t.adoc#compound-operators" +title = "Compound Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast128_t.adoc#construction-from-integral-type" +title = "Construction From Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast128_t.adoc#construction-from-string" +title = "Construction From String" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast128_t.adoc#construction-to-and-from-binary-floating-point-type" +title = "Construction to and from binary floating-point type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast128_t.adoc#conversion-to-integral-type" +title = "Conversion to Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast128_t.adoc#conversion-to-other-decimal-types" +title = "Conversion to Other Decimal Types" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast128_t.adoc#decimal_fast128_t" +title = "Description" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast128_t.adoc#increment-and-decrement-operators" +title = "Increment and Decrement Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast32_t.adoc#compound-operators" +title = "Compound Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast32_t.adoc#construction-from-integral-type" +title = "Construction From Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast32_t.adoc#construction-from-string" +title = "Construction From String" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast32_t.adoc#construction-to-and-from-binary-floating-point-type" +title = "Construction to and from binary floating-point type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast32_t.adoc#conversion-to-integral-type" +title = "Conversion to Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast32_t.adoc#conversion-to-other-decimal-types" +title = "Conversion to Other Decimal Types" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast32_t.adoc#decimal_fast32_t" +title = "Description" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast32_t.adoc#increment-and-decrement-operators" +title = "Increment and Decrement Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast64_t.adoc#compound-operators" +title = "Compound Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast64_t.adoc#construction-from-integral-type" +title = "Construction From Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast64_t.adoc#construction-from-string" +title = "Construction From String" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast64_t.adoc#construction-to-and-from-binary-floating-point-type" +title = "Construction to and from binary floating-point type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast64_t.adoc#conversion-to-integral-type" +title = "Conversion to Integral Type" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast64_t.adoc#conversion-to-other-decimal-types" +title = "Conversion to Other Decimal Types" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast64_t.adoc#decimal_fast64_t" +title = "Description" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/decimal_fast64_t.adoc#increment-and-decrement-operators" +title = "Increment and Decrement Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/design.adoc#floating-point-exception-flags-are-not-supported" +title = "Floating Point Exception Flags are not Supported" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/design.adoc#non-finite-deviation" +title = "`istream` of Non-finite Values is Allowed" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_basic_math" +title = "Basic Arithmetic" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_binary_floating_conversions" +title = "Binary Floating Point Conversions" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_charconv" +title = "``" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_construction" +title = "Basic Construction" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_cuda" +title = "CUDA Usage" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_file" +title = "Reading From and Writing To File" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_generic_programming" +title = "Generic Programming" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_integer_conversions" +title = "Integral Conversions" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_literals_constants" +title = "Literals and Constants" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_print" +title = "``" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_promotion" +title = "Promotion and Mixed Decimal Arithmetic" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/examples.adoc#examples_std_format" +title = "``" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/fast_types.adoc#addition" +title = "Addition" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/fast_types.adoc#comparison-operators" +title = "Comparison Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/fast_types.adoc#division" +title = "Division" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/fast_types.adoc#multiplication" +title = "Multiplication" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/fast_types.adoc#overview" +title = "Overview" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/fast_types.adoc#subtraction" +title = "Subtraction" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/fast_types.adoc#unary-minus" +title = "Unary Minus" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/fast_types.adoc#unary-plus" +title = "Unary Plus" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/financial_examples.adoc#examples_boost_math" +title = "Boost.Math to Calculate Bollinger Bands" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/financial_examples.adoc#examples_finance" +title = "Parsing Pricing Data from File" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/format.adoc#examples" +title = "Examples" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/format.adoc#format" +title = "Locale Modifier" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/generic_decimal.adoc#addition" +title = "Addition" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/generic_decimal.adoc#comparison-operators" +title = "Comparison Operators" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/generic_decimal.adoc#division" +title = "Division" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/generic_decimal.adoc#multiplication" +title = "Multiplication" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/generic_decimal.adoc#subtraction" +title = "Subtraction" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/generic_decimal.adoc#unary-minus" +title = "Unary Minus" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/generic_decimal.adoc#unary-plus" +title = "Unary Plus" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/strings.adoc#conversions-from-std-string" +title = "Conversions from `std::string`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/strings.adoc#strings" +title = "Construction from `std::string` and `std::string_view`" +status = "todo" + +[[snippet]] +doc = "libs/decimal/doc/modules/ROOT/pages/strings.adoc#to-string" +title = "`to_string`" +status = "todo" diff --git a/tests/decimal.cpp b/tests/decimal/smoke.cpp similarity index 96% rename from tests/decimal.cpp rename to tests/decimal/smoke.cpp index 8e5c795a..5fb36506 100644 --- a/tests/decimal.cpp +++ b/tests/decimal/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.decimal smoke — decimal floating point types -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.decimal; diff --git a/tests/describe/coverage.toml b/tests/describe/coverage.toml new file mode 100644 index 00000000..d6f3cdb7 --- /dev/null +++ b/tests/describe/coverage.toml @@ -0,0 +1,175 @@ +# tests/describe/coverage.toml — official examples and documentation snippets of +# Boost.describe 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "describe" + +[[example]] +path = "libs/describe/example/console.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/enum_to_string.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/equality.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/fmtlib_class_formatter.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/fmtlib_enum_formatter.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/from_json.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/hash_value.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/json_rpc.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/pm_to_string.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/print_function.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/printing_enums_ct.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/printing_enums_rt.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/serialization.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/string_to_enum.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/struct_to_tuple.cpp" +status = "todo" + +[[example]] +path = "libs/describe/example/to_json.cpp" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/classes.adoc#class-types-with-protected-or-private-members" +title = "Class Types with Protected or Private Members" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/classes.adoc#class-types-with-public-members" +title = "Class Types with Public Members" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/classes.adoc#overloaded-member-functions" +title = "Overloaded Member Functions" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/classes.adoc#retrieving-class-properties" +title = "Retrieving Class Properties" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/enums.adoc#enums" +title = "Describing Enumeration Types" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_console" +title = "Interactive Variable Console" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_enum_to_string" +title = "enum_to_string" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_equality" +title = "Implementing operator==" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_fmtlib_class_formatter" +title = "`fmtlib` Class Formatter" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_fmtlib_enum_formatter" +title = "`fmtlib` Enum Formatter" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_from_json" +title = "Automatic Conversion from JSON" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_hash_value" +title = "Implementing hash_value" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_json_rpc" +title = "Automatic JSON RPC" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_pm_to_string" +title = "Printing Pointers to Members" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_print_function" +title = "Defining a Universal Print Function" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_printing_enums_ct" +title = "Printing Enumerators with a Compile Time Loop" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_printing_enums_rt" +title = "Printing Enumerators with a Run Time Loop" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_serialization" +title = "Automatic Serialization" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_string_to_enum" +title = "string_to_enum" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_struct_to_tuple" +title = "struct_to_tuple" +status = "todo" + +[[snippet]] +doc = "libs/describe/doc/describe/examples.adoc#example_to_json" +title = "Automatic Conversion to JSON" +status = "todo" diff --git a/tests/describe.cpp b/tests/describe/smoke.cpp similarity index 97% rename from tests/describe.cpp rename to tests/describe/smoke.cpp index 967372d3..c94f863a 100644 --- a/tests/describe.cpp +++ b/tests/describe/smoke.cpp @@ -3,7 +3,7 @@ // never cross a module boundary — M10 rule — and gcc 16 ODR-conflicts on // include+import mixing in one TU, see the C1 plan §1.1). Same consumer rule // as the T3 macro libraries (mirror of tests/exception.cpp). -#include "test_assert.hpp" +#include "../test_assert.hpp" #include #include #include diff --git a/tests/dll/coverage.toml b/tests/dll/coverage.toml new file mode 100644 index 00000000..768a3f94 --- /dev/null +++ b/tests/dll/coverage.toml @@ -0,0 +1,125 @@ +# tests/dll/coverage.toml — official examples and documentation snippets of +# Boost.dll 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "dll" + +[[example]] +path = "libs/dll/example/getting_started.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/getting_started_library.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/mangled/import_class.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/mangled/smart_lib.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial1/my_plugin_sum.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial1/tutorial1.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial2/my_plugin_aggregator.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial2/tutorial2.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial3/tutorial3.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial4/load_self.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial4/static_plugin.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial5/load_all.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial6/on_unload_lib.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial6/tutorial6.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial7/library1.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial7/library2.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial7/tutorial7.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial8/refcounting_plugin.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial8/tutorial8.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial8/tutorial8_static.cpp" +status = "todo" + +[[example]] +path = "libs/dll/example/tutorial9/tutorial9.cpp" +status = "todo" + +[[snippet]] +doc = "libs/dll/doc/design_rationale.qbk#aliases-vs-mangling" +title = "Aliases vs Mangling" +status = "todo" + +[[snippet]] +doc = "libs/dll/doc/faq.qbk#f-a-q" +title = "F.A.Q." +status = "todo" + +[[snippet]] +doc = "libs/dll/doc/getting_started.qbk#getting_started" +title = "Getting started" +status = "todo" + +[[snippet]] +doc = "libs/dll/doc/limitations.qbk#multithread" +title = "Thread safe library loading (Fails on FreeBSD, MacOS, iOS and some other)" +status = "todo" + +[[snippet]] +doc = "libs/dll/doc/mangled.qbk#mangled-import" +title = "Mangled Import" +status = "todo" + +[[snippet]] +doc = "libs/dll/doc/mangled.qbk#overloading-qualifiers" +title = "Overloading qualifiers" +status = "todo" + +[[snippet]] +doc = "libs/dll/doc/missuses.qbk#missuses" +title = "Missuses" +status = "todo" diff --git a/tests/dll/mcpp.toml b/tests/dll/mcpp.toml new file mode 100644 index 00000000..436f7599 --- /dev/null +++ b/tests/dll/mcpp.toml @@ -0,0 +1,18 @@ +# Test project of the dll module (design .agents/docs/2026-11-09-module-test- +# hardening-design.md §5): the official tutorials load plugin shared +# libraries, which the root project cannot build. The root mcpp.toml keeps +# tests/dll/** out of its own [test] discover; CI runs `mcpp test` here. +[package] +name = "boost-m-tests-dll" +version = "0.0.0" +description = "Tests of mcpplibs.mwrap.boost.dll with plugin shared libraries" +license = "Apache-2.0" + +[dependencies] +"mcpplibs.mwrap.boost" = { path = "../..", default-features = false, features = ["dll"] } + +[dev-dependencies] +"compat.doctest" = "2.4.12" + +[test] +discover = ["*.cpp"] diff --git a/tests/dll.cpp b/tests/dll/smoke.cpp similarity index 91% rename from tests/dll.cpp rename to tests/dll/smoke.cpp index f641da45..788b5937 100644 --- a/tests/dll.cpp +++ b/tests/dll/smoke.cpp @@ -1,6 +1,6 @@ // mcpplibs.mwrap.boost.dll smoke — shared_library default state only (path conversion on // the MSVC flavor crashes in program_location — upstream issue, see M9 doc) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.dll; diff --git a/tests/dynamic_bitset/api.cpp b/tests/dynamic_bitset/api.cpp new file mode 100644 index 00000000..327c58df --- /dev/null +++ b/tests/dynamic_bitset/api.cpp @@ -0,0 +1,148 @@ +// mcpplibs.mwrap.boost.dynamic_bitset — the documented members and free +// functions: construction from integers/strings/blocks, element access, the +// bitwise and shift operators, set algebra (subset, intersects), searches +// (find_first/find_next), resizing, block conversion, comparison, hashing and +// string conversion. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.dynamic_bitset; + +using bits = boost::dynamic_bitset<>; + +TEST_CASE("construction") { + CHECK(bits().empty()); + CHECK(bits(8, 0xF0ul).count() == 4); + CHECK(bits(std::string("1011")).to_ulong() == 11ul); + CHECK(bits(std::string("xx1011"), 2).to_ulong() == 11ul); + CHECK(bits(std::string("101100"), 0, 3).to_ulong() == 5ul); + std::vector blocks{0x1ul, 0x2ul}; + bits fromBlocks(blocks.begin(), blocks.end()); + CHECK(fromBlocks.size() == 2 * bits::bits_per_block); + CHECK(fromBlocks.test(0)); + CHECK(fromBlocks.test(bits::bits_per_block + 1)); + bits copy(fromBlocks), moved(std::move(copy)); + CHECK(moved == fromBlocks); +} + +TEST_CASE("element access and modifiers") { + bits b(10); + b.set(2).set(4, true).set(6, 2, true); // bits 6 and 7 + CHECK(b.count() == 4); + CHECK(b.test(7)); + CHECK_FALSE(b.test_set(8)); // returns the previous value + CHECK(b.test_set(8)); + b.reset(2); + b.flip(0); + CHECK(b[0]); + b[0].flip(); + CHECK_FALSE(b[0]); + b.reset(6, 2); + CHECK_FALSE(b.test(6)); + b.flip(0, 3); + CHECK(b.test(0)); + CHECK(b.all() == false); + CHECK(bits(3, 7ul).all()); + CHECK_THROWS_AS(b.at(100), std::out_of_range); + CHECK(b.at(4)); + b.set(); + CHECK(b.all()); + b.reset(); + CHECK(b.none()); + b.flip(); + CHECK(b.count() == 10); +} + +TEST_CASE("bitwise operators and shifts") { + bits a(std::string("1100")), b(std::string("1010")); + CHECK((a & b) == bits(std::string("1000"))); + CHECK((a | b) == bits(std::string("1110"))); + CHECK((a ^ b) == bits(std::string("0110"))); + CHECK((a - b) == bits(std::string("0100"))); + CHECK(~a == bits(std::string("0011"))); + bits c = a; + c <<= 1; + CHECK(c == bits(std::string("1000"))); + c >>= 2; + CHECK(c == bits(std::string("0010"))); + c |= b; + c &= a; + c ^= b; + c -= a; + CHECK(c == bits(std::string("0010"))); +} + +TEST_CASE("set algebra and searches") { + bits a(std::string("0110")), b(std::string("1110")); + CHECK(a.is_subset_of(b)); + CHECK(a.is_proper_subset_of(b)); + CHECK(a.intersects(b)); + CHECK_FALSE(bits(std::string("0001")).intersects(a)); + bits s(16); + s.set(3).set(9).set(12); + CHECK(s.find_first() == 3); + CHECK(s.find_next(3) == 9); + CHECK(s.find_next(12) == bits::npos); + CHECK(s.find_first_off() == 0); + CHECK(s.find_next_off(2) == 4); + CHECK(s.find_first(10) == 12); +} + +TEST_CASE("size, blocks, conversions") { + bits b(5, 0x1Ful); + b.resize(8, true); + CHECK(b.count() == 8); + b.push_back(false); + b.push_front(true); + CHECK(b.size() == 10); + CHECK(b.test(0)); + b.pop_back(); + b.pop_front(); + CHECK(b.size() == 8); + b.append(0x3ul); // a whole block + CHECK(b.size() == 8 + bits::bits_per_block); + CHECK(b.num_blocks() == 2); + std::vector out; + boost::to_block_range(b, std::back_inserter(out)); + CHECK(out.size() == 2); + CHECK(out[1] == 0ul); + bits roundtrip(b.size()); + boost::from_block_range(out.begin(), out.end(), roundtrip); + CHECK(roundtrip == b); + b.clear(); + CHECK(b.empty()); + b.shrink_to_fit(); + CHECK(bits(4, 9ul).to_ulong() == 9ul); + std::string s; + boost::to_string(bits(4, 9ul), s); + CHECK(s == "1001"); + CHECK(bits(4).max_size() >= 4); +} + +TEST_CASE("iterators (1.92) and capacity") { + bits b(std::string("1011")); + std::vector seen(b.begin(), b.end()); // least significant bit first + CHECK(seen == std::vector{true, true, false, true}); + CHECK(std::count(b.cbegin(), b.cend(), true) == 3); + CHECK(*b.rbegin() == true); + for (auto ref : b) ref = !ref; + CHECK(b == bits(std::string("0100"))); + b.reserve(1000); + CHECK(b.capacity() >= 1000); + CHECK(b.size() == 4); +} + +TEST_CASE("comparison, swap, hash") { + bits a(std::string("0101")), b(std::string("0110")); + CHECK(a < b); + CHECK(a <= b); + CHECK(b > a); + CHECK(a != b); + swap(a, b); + CHECK(a == bits(std::string("0110"))); + // hash_value is a hidden friend (found by ADL); std::hash is specialized. + CHECK(hash_value(a) == hash_value(bits(std::string("0110")))); + CHECK(std::hash()(a) == std::hash()(bits(std::string("0110")))); + std::unordered_set set{a, b}; + CHECK(set.size() == 2); +} diff --git a/tests/dynamic_bitset/coverage.toml b/tests/dynamic_bitset/coverage.toml new file mode 100644 index 00000000..107c776b --- /dev/null +++ b/tests/dynamic_bitset/coverage.toml @@ -0,0 +1,18 @@ +# tests/dynamic_bitset/coverage.toml — official examples and documentation snippets of +# Boost.dynamic_bitset 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "dynamic_bitset" + +[[example]] +path = "libs/dynamic_bitset/example/example1.cpp" +case = "dynamic_bitset/examples:example: example1" + +[[example]] +path = "libs/dynamic_bitset/example/example2.cpp" +case = "dynamic_bitset/examples:example: example2" + +[[example]] +path = "libs/dynamic_bitset/example/example3.cpp" +case = "dynamic_bitset/examples:example: example3" diff --git a/tests/dynamic_bitset/custom_types.cpp b/tests/dynamic_bitset/custom_types.cpp new file mode 100644 index 00000000..74e5dbe2 --- /dev/null +++ b/tests/dynamic_bitset/custom_types.cpp @@ -0,0 +1,79 @@ +// mcpplibs.mwrap.boost.dynamic_bitset — the class template's customization +// points: user-chosen Block types (unsigned char, std::uint16_t, unsigned +// long long) and a user allocator (counting allocations), plus block-range +// round trips through a user container. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.dynamic_bitset; + +namespace user { +inline std::size_t allocations = 0; + +template +struct counting_allocator { + using value_type = T; + counting_allocator() = default; + template + counting_allocator(const counting_allocator&) noexcept {} + T* allocate(std::size_t n) { + ++allocations; + return std::allocator().allocate(n); + } + void deallocate(T* p, std::size_t n) noexcept { std::allocator().deallocate(p, n); } + template + bool operator==(const counting_allocator&) const noexcept { return true; } +}; +} // namespace user + +TEST_CASE_TEMPLATE("user Block types", Block, unsigned char, std::uint16_t, unsigned long long) { + using bits = boost::dynamic_bitset; + static_assert(bits::bits_per_block == std::numeric_limits::digits); + bits b(40, 0xA5ul); + CHECK(b.count() == 4); + CHECK(b.num_blocks() == (40 + bits::bits_per_block - 1) / bits::bits_per_block); + b.set(39); + CHECK(b.find_next(7) == 39); + std::vector blocks; + boost::to_block_range(b, std::back_inserter(blocks)); + bits back(blocks.begin(), blocks.end()); + back.resize(40); + CHECK(back == b); + std::ostringstream os; + os << bits(4, 6ul); + CHECK(os.str() == "0110"); +} + +TEST_CASE("user allocator") { + using bits = boost::dynamic_bitset>; + user::allocations = 0; + bits b(1000); + CHECK(user::allocations >= 1); + b.set(999); + bits c = b; + CHECK(c.test(999)); + CHECK(c.get_allocator() == user::counting_allocator()); + std::size_t before = user::allocations; + c.resize(5000); + CHECK(user::allocations > before); +} + +TEST_CASE("bitsets as keys and values in user structures") { + struct permission_set { + std::string role; + boost::dynamic_bitset<> perms; + }; + std::vector roles{{"reader", boost::dynamic_bitset<>(4, 0b0001ul)}, + {"editor", boost::dynamic_bitset<>(4, 0b0011ul)}, + {"admin", boost::dynamic_bitset<>(4, 0b1111ul)}}; + auto at_least = [&](const boost::dynamic_bitset<>& need) { + std::vector out; + for (auto& r : roles) + if (need.is_subset_of(r.perms)) out.push_back(r.role); + return out; + }; + CHECK(at_least(boost::dynamic_bitset<>(4, 0b0010ul)) == std::vector{"editor", "admin"}); + std::map, std::string> by_mask; + for (auto& r : roles) by_mask[r.perms] = r.role; + CHECK(by_mask.begin()->second == "reader"); +} diff --git a/tests/dynamic_bitset/examples.cpp b/tests/dynamic_bitset/examples.cpp new file mode 100644 index 00000000..55b497db --- /dev/null +++ b/tests/dynamic_bitset/examples.cpp @@ -0,0 +1,55 @@ +// mcpplibs.mwrap.boost.dynamic_bitset — libs/dynamic_bitset/example/ +// example1..3.cpp as test cases (example3 reads std::cin; it reads a string +// stream here). +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.dynamic_bitset; + +namespace { +template +std::string str(const B& b) { + std::ostringstream os; + os << b; + return os.str(); +} +} // namespace + +TEST_CASE("example: example1") { + boost::dynamic_bitset<> x(5); + x[0] = 1; + x[1] = 1; + x[4] = 1; + std::string by_index; + for (boost::dynamic_bitset<>::size_type i = 0; i < x.size(); ++i) by_index += x[i] ? '1' : '0'; + CHECK(by_index == "11001"); + CHECK(str(x) == "10011"); // operator<< prints the most significant bit first +} + +TEST_CASE("example: example2") { + CHECK(str(boost::dynamic_bitset<>(2, 0ul)) == "00"); + CHECK(str(boost::dynamic_bitset<>(2, 1ul)) == "01"); + CHECK(str(boost::dynamic_bitset<>(2, 2ul)) == "10"); + CHECK(str(boost::dynamic_bitset<>(2, 3ul)) == "11"); +} + +TEST_CASE("example: example3") { + boost::dynamic_bitset<> mask(12, 2730ul); + CHECK(str(mask) == "101010101010"); + boost::dynamic_bitset<> x; + CHECK(x.size() == 0); + std::istringstream in("100110"); + REQUIRE(static_cast(in >> x)); + const std::size_t sz = x.size(); + CHECK(sz == 6); + CHECK(x.to_ulong() == 38ul); + mask.resize(sz); + CHECK(str(mask) == "101010"); + CHECK(str(x & mask) == "100010"); + CHECK(str(x | mask) == "101110"); + CHECK(str(x << 1) == "001100"); + CHECK(str(x >> 1) == "010011"); + boost::dynamic_bitset<> huge(200); + huge.set(150); + CHECK_THROWS_AS(huge.to_ulong(), std::overflow_error); +} diff --git a/tests/dynamic_bitset.cpp b/tests/dynamic_bitset/smoke.cpp similarity index 95% rename from tests/dynamic_bitset.cpp rename to tests/dynamic_bitset/smoke.cpp index d13463a4..67544557 100644 --- a/tests/dynamic_bitset.cpp +++ b/tests/dynamic_bitset/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.dynamic_bitset smoke — dynamic bitset operations -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.dynamic_bitset; diff --git a/tests/geometry/coverage.toml b/tests/geometry/coverage.toml new file mode 100644 index 00000000..152ece4f --- /dev/null +++ b/tests/geometry/coverage.toml @@ -0,0 +1,1392 @@ +# tests/geometry/coverage.toml — official examples and documentation snippets of +# Boost.geometry 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "geometry" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/interprocess.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/iterative_query.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/mapped_file.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/polygons_shared_ptr.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/polygons_vector.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/quick_start.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/range_adaptors.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/value_index.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/value_shared_ptr.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/index/src/examples/rtree/variants_map.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/sample/src/examples/apple_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/append.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/area.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/area_with_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/assign.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/assign_2d_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/assign_3d_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/assign_box_corners.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/assign_inverse.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/assign_point_from_index.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/assign_point_to_index.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/assign_points.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/azimuth.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/azimuth_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/buffer_with_strategies.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/centroid.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/clear.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/closest_points.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/closest_points_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/comparable_distance.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/convert.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/convex_hull.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/correct.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/covered_by.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/crosses.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/densify.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/densify_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/difference.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/discrete_frechet_distance.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/discrete_frechet_distance_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/discrete_hausdorff_distance.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/discrete_hausdorff_distance_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/disjoint.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/distance.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/envelope.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/equals.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/expand.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/for_each_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/for_each_point_const.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/for_each_segment_const.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/intersection_ls_ls_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/intersection_poly_poly.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/intersection_segment.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/intersects_linestring.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/intersects_segment.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/is_empty.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/is_simple.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/is_valid.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/is_valid_failure.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/is_valid_message.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/length.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/length_with_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/line_interpolate.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/line_interpolate_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/make_2d_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/make_3d_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/make_inverse.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/make_with_range.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/num_geometries.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/num_interior_rings.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/num_points.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/num_segments.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/overlaps.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/perimeter.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/relate.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/relation.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/return_envelope.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/reverse.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/simplify.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/simplify_insert.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/sym_difference.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/touches_one_geometry.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/touches_two_geometries.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/transform.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/transform_with_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/union.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/unique.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/algorithms/within.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/arithmetic/cross_product.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/arithmetic/dot_product.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/closure.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/coordinate_dimension.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/coordinate_system.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/coordinate_type.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/degree_radian.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/get_box.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/get_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/interior_type.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/point_order.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/point_type.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/ring_type.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/rings.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/set_box.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/set_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/tag.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/core/tag_cast.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_array.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_fusion.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_polygon_box.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_polygon_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_polygon_polygon.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_polygon_ring.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_range/filtered.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_range/reversed.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_range/sliced.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_range/strided.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_range/uniqued.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/boost_tuple.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/c_array.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/adapted/std_array.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/box.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/linestring.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/multi_linestring.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/multi_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/multi_polygon.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/point_xy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/point_xyz.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/polygon.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/polyhedral_surface.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/box.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/box_2d_4values.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/box_templated.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/linestring.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/linestring_templated.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/multi_linestring.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/multi_point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/multi_point_templated.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/multi_polygon.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/point.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/ring.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/register/ring_templated.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/ring.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/geometries/segment.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/io/from_wkt.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/io/read_wkt.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/io/svg.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/io/svg_mapper_custom.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/io/to_wkt.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/io/wkt.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/quick_start.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/strategies/buffer_distance_asymmetric.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/strategies/buffer_distance_symmetric.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/strategies/buffer_end_flat.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/strategies/buffer_end_round.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/strategies/buffer_geographic_point_circle.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/strategies/buffer_join_miter.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/strategies/buffer_join_round.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/strategies/buffer_point_circle.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/strategies/buffer_point_square.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/views/box_view.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/doc/src/examples/views/segment_view.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/01_point_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/02_linestring_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/03_polygon_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/04_boost_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/05_a_overlay_polygon_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/05_b_overlay_linestring_polygon_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/06_a_transformation_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/06_b_transformation_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/07_a_graph_route_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/07_b_graph_route_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c01_custom_point_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c02_custom_box_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c03_custom_linestring_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c04_a_custom_triangle_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c04_b_custom_triangle_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c05_custom_point_pointer_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c06_custom_polygon_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c07_custom_ring_pointer_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c08_custom_non_std_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c09_custom_fusion_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c10_custom_cs_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/c11_custom_cs_transform_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/ml01_multipolygon_simplify.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/ml02_distance_strategy.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/ml03_distance_formula.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/with_external_libs/qt/qt_world_mapper.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/with_external_libs/wxwidgets/wxwidgets_world_mapper.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/with_external_libs/x02_gd_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/with_external_libs/x03_a_soci_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/with_external_libs/x03_b_soci_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/with_external_libs/x03_c_soci_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/with_external_libs/x03_d_soci_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/example/with_external_libs/x05_shapelib_example.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/index/example/3d_benchmark.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/index/example/benchmark.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/index/example/benchmark2.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/index/example/benchmark3.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/index/example/benchmark_experimental.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/index/example/benchmark_insert.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/index/example/glut_vis.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/index/example/random_test.cpp" +status = "todo" + +[[example]] +path = "libs/geometry/index/example/serialize.cpp" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/concept/point.qbk#concept_point" +title = "Point Concept" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/design_rationale.qbk#design" +title = "Design Rationale" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/example_adapting_a_legacy_geometry_object_model.qbk#example-adapting-a-legacy-geometry-object-model" +title = "Example: Adapting a legacy geometry object model" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/example_adapting_a_legacy_geometry_object_model.qbk#example-source-code-adapting-a-legacy-geometry-object-model" +title = "Example source code: Adapting a legacy geometry object model" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/area_cartesian.qbk#strategy_area_cartesian" +title = "strategy::area::cartesian" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/area_geographic.qbk#strategy_area_geographic" +title = "strategy::area::geographic" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/area_spherical.qbk#strategy_area_spherical" +title = "strategy::area::spherical" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/box.qbk#model_box" +title = "model::box" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/box_view.qbk#box_view" +title = "box_view" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_distance_asymmetric.qbk#strategy_buffer_distance_asymmetric" +title = "strategy::buffer::distance_asymmetric" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_distance_symmetric.qbk#strategy_buffer_distance_symmetric" +title = "strategy::buffer::distance_symmetric" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_end_flat.qbk#strategy_buffer_end_flat" +title = "strategy::buffer::end_flat" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_end_round.qbk#strategy_buffer_end_round" +title = "strategy::buffer::end_round" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_geographic_point_circle.qbk#strategy_buffer_geographic_point_circle" +title = "strategy::buffer::geographic_point_circle" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_join_miter.qbk#strategy_buffer_join_miter" +title = "strategy::buffer::join_miter" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_join_round.qbk#strategy_buffer_join_round" +title = "strategy::buffer::join_round" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_point_circle.qbk#strategy_buffer_point_circle" +title = "strategy::buffer::point_circle" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_point_square.qbk#strategy_buffer_point_square" +title = "strategy::buffer::point_square" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/buffer_side_straight.qbk#strategy_buffer_side_straight" +title = "strategy::buffer::side_straight" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/cartesian.qbk#cs_cartesian" +title = "cs::cartesian" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/centroid_average.qbk#strategy_centroid_average" +title = "strategy::centroid::average" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/centroid_bashein_detmer.qbk#strategy_centroid_bashein_detmer" +title = "strategy::centroid::bashein_detmer" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/centroid_exception.qbk#centroid_exception" +title = "centroid_exception" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/closeable_view.qbk#closeable_view" +title = "closeable_view" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/closing_iterator.qbk#closing_iterator" +title = "closing_iterator" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/closure.qbk#closure" +title = "closure" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/coordinate_system.qbk#coordinate_system" +title = "coordinate_system" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/coordinate_type.qbk#coordinate_type" +title = "coordinate_type" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/cs_tag.qbk#cs_tag" +title = "cs_tag" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/de9im_mask.qbk#de9im_mask" +title = "de9im::mask" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/de9im_matrix.qbk#de9im_matrix" +title = "de9im::matrix" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/de9im_static_mask.qbk#de9im_static_mask" +title = "de9im::static_mask" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/degree.qbk#degree" +title = "degree" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/densify_cartesian.qbk#strategy_densify_cartesian" +title = "strategy::densify::cartesian" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/densify_geographic.qbk#strategy_densify_geographic" +title = "strategy::densify::geographic" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/densify_spherical.qbk#strategy_densify_spherical" +title = "strategy::densify::spherical" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/dimension.qbk#dimension" +title = "dimension" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/distance_cross_track.qbk#strategy_distance_cross_track" +title = "strategy::distance::cross_track" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/distance_cross_track_point_box.qbk#strategy_distance_cross_track_point_box" +title = "strategy::distance::cross_track_point_box" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/distance_haversine.qbk#strategy_distance_haversine" +title = "strategy::distance::haversine" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/distance_projected_point.qbk#strategy_distance_projected_point" +title = "strategy::distance::projected_point" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/distance_pythagoras.qbk#strategy_distance_pythagoras" +title = "strategy::distance::pythagoras" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/distance_pythagoras_box_box.qbk#strategy_distance_pythagoras_box_box" +title = "strategy::distance::pythagoras_box_box" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/distance_pythagoras_point_box.qbk#strategy_distance_pythagoras_point_box" +title = "strategy::distance::pythagoras_point_box" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/ever_circling_iterator.qbk#ever_circling_iterator" +title = "ever_circling_iterator" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/exception.qbk#exception" +title = "exception" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/geographic.qbk#cs_geographic" +title = "cs::geographic" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/identity_view.qbk#identity_view" +title = "identity_view" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/interior_type.qbk#interior_type" +title = "interior_type" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/line_interpolate_cartesian.qbk#strategy_line_interpolate_cartesian" +title = "strategy::line_interpolate::cartesian" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/line_interpolate_geographic.qbk#strategy_line_interpolate_geographic" +title = "strategy::line_interpolate::geographic" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/line_interpolate_spherical.qbk#strategy_line_interpolate_spherical" +title = "strategy::line_interpolate::spherical" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/linestring.qbk#model_linestring" +title = "model::linestring" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/multi_linestring.qbk#model_multi_linestring" +title = "model::multi_linestring" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/multi_point.qbk#model_multi_point" +title = "model::multi_point" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/multi_polygon.qbk#model_multi_polygon" +title = "model::multi_polygon" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/point.qbk#model_point" +title = "model::point" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/point_order.qbk#point_order" +title = "point_order" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/point_type.qbk#point_type" +title = "point_type" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/point_xy.qbk#model_d2_point_xy" +title = "model::d2::point_xy" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/point_xyz.qbk#model_d3_point_xyz" +title = "model::d3::point_xyz" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/polar.qbk#cs_polar" +title = "cs::polar" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/polygon.qbk#model_polygon" +title = "model::polygon" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/polyhedral_surface.qbk#model_polyhedral_surface" +title = "model::polyhedral_surface" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/radian.qbk#radian" +title = "radian" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/referring_segment.qbk#model_referring_segment" +title = "model::referring_segment" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/reversible_view.qbk#reversible_view" +title = "reversible_view" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/ring.qbk#model_ring" +title = "model::ring" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/ring_type.qbk#ring_type" +title = "ring_type" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/segment.qbk#model_segment" +title = "model::segment" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/segment_view.qbk#segment_view" +title = "segment_view" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/side_geographic.qbk#strategy_side_geographic" +title = "strategy::side::geographic" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/side_side_by_cross_track.qbk#strategy_side_side_by_cross_track" +title = "strategy::side::side_by_cross_track" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/side_side_by_triangle.qbk#strategy_side_side_by_triangle" +title = "strategy::side::side_by_triangle" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/side_spherical_side_formula.qbk#strategy_side_spherical_side_formula" +title = "strategy::side::spherical_side_formula" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/simplify_douglas_peucker.qbk#strategy_simplify_douglas_peucker" +title = "strategy::simplify::douglas_peucker" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/spherical.qbk#cs_spherical" +title = "cs::spherical" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/spherical_equatorial.qbk#cs_spherical_equatorial" +title = "cs::spherical_equatorial" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/srs_spheroid.qbk#srs_spheroid" +title = "srs::spheroid" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/svg_mapper.qbk#svg_mapper" +title = "svg_mapper" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/tag.qbk#tag" +title = "tag" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/tag_cast.qbk#tag_cast" +title = "tag_cast" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/transform_inverse_transformer.qbk#strategy_transform_inverse_transformer" +title = "strategy::transform::inverse_transformer" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/transform_map_transformer.qbk#strategy_transform_map_transformer" +title = "strategy::transform::map_transformer" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/transform_matrix_transformer.qbk#strategy_transform_matrix_transformer" +title = "strategy::transform::matrix_transformer" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/transform_rotate_transformer.qbk#strategy_transform_rotate_transformer" +title = "strategy::transform::rotate_transformer" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/transform_scale_transformer.qbk#strategy_transform_scale_transformer" +title = "strategy::transform::scale_transformer" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/transform_translate_transformer.qbk#strategy_transform_translate_transformer" +title = "strategy::transform::translate_transformer" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/within_crossings_multiply.qbk#strategy_within_crossings_multiply" +title = "strategy::within::crossings_multiply" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/within_franklin.qbk#strategy_within_franklin" +title = "strategy::within::franklin" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/generated/within_winding.qbk#strategy_within_winding" +title = "strategy::within::winding" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/predicates.qbk#contains-geometry-const" +title = "contains(Geometry const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/predicates.qbk#covered-by-geometry-const" +title = "covered_by(Geometry const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/predicates.qbk#covers-geometry-const" +title = "covers(Geometry const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/predicates.qbk#disjoint-geometry-const" +title = "disjoint(Geometry const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/predicates.qbk#intersects-geometry-const" +title = "intersects(Geometry const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/predicates.qbk#nearest-geometry-const-std-size-t" +title = "nearest(Geometry const &, std::size_t)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/predicates.qbk#overlaps-geometry-const" +title = "overlaps(Geometry const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/predicates.qbk#satisfies-unarypredicate-const" +title = "satisfies(UnaryPredicate const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/predicates.qbk#within-geometry-const" +title = "within(Geometry const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree.qbk#begin" +title = "begin()" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree.qbk#end" +title = "end()" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree.qbk#qbegin-predicates-const" +title = "qbegin(Predicates const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree.qbk#qend" +title = "qend()" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree.qbk#query-predicates-const-outiter" +title = "query(Predicates const &, OutIter)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree_functions.qbk#begin-rtree-const" +title = "begin(rtree<...> const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree_functions.qbk#end-rtree-const" +title = "end(rtree<...> const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree_functions.qbk#qbegin-rtree-const-predicates-const" +title = "qbegin(rtree<...> const &, Predicates const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree_functions.qbk#qend-rtree-const" +title = "qend(rtree<...> const &)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/generated/rtree_functions.qbk#query-rtree-const-predicates-const-outiter" +title = "query(rtree<...> const &, Predicates const &, OutIter)" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/rtree/creation.qbk#creation-and-modification" +title = "Creation and Modification" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/rtree/experimental.qbk#experimental-features" +title = "Experimental Features" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/index/rtree/query.qbk#queries" +title = "Queries" +status = "todo" + +[[snippet]] +doc = "libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/sample/apple.qbk#fruit_apple" +title = "fruit::apple" +status = "todo" diff --git a/tests/geometry.cpp b/tests/geometry/smoke.cpp similarity index 96% rename from tests/geometry.cpp rename to tests/geometry/smoke.cpp index f372dbbd..624e1f02 100644 --- a/tests/geometry.cpp +++ b/tests/geometry/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.geometry smoke — cartesian point distance + area -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.geometry; diff --git a/tests/hash2/coverage.toml b/tests/hash2/coverage.toml new file mode 100644 index 00000000..14b60d95 --- /dev/null +++ b/tests/hash2/coverage.toml @@ -0,0 +1,145 @@ +# tests/hash2/coverage.toml — official examples and documentation snippets of +# Boost.hash2 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "hash2" + +[[example]] +path = "libs/hash2/example/compile_time.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/compile_time_2.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/hash2sum.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/hash_32_64.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/hash_with_any_seed.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/hash_with_byte_seed.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/hash_with_uint64_seed.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/hash_without_seed.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/json_value.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/md5sum.cpp" +status = "todo" + +[[example]] +path = "libs/hash2/example/xxh128_from_xxh64.cpp" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/examples.adoc#compile-time-hashing" +title = "Compile Time Hashing" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/examples.adoc#hash2sum" +title = "hash2sum" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/examples.adoc#md5sum" +title = "md5sum" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/examples.adoc#result-extension" +title = "Result Extension" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/examples.adoc#use-with-unordered-containers" +title = "Use with Unordered Containers" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#compile-time-hashing" +title = "Compile Time Hashing" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#hash-algorithm-requirements" +title = "Hash Algorithm Requirements" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#result" +title = "result" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#update" +title = "update" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#arrays" +title = "Arrays" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#described-classes" +title = "Described Classes" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#enumeration-types" +title = "Enumeration Types" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#floating-point-types" +title = "Floating Point Types" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#hashing_objects" +title = "Hashing {cpp} Objects" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#integral-types" +title = "Integral Types" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#pointers" +title = "Pointers" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#ranges" +title = "Ranges" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#tuples" +title = "Tuples" +status = "todo" + +[[snippet]] +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#user-defined-types" +title = "User Defined Types" +status = "todo" diff --git a/tests/hash2.cpp b/tests/hash2/smoke.cpp similarity index 97% rename from tests/hash2.cpp rename to tests/hash2/smoke.cpp index 41b294e4..6cae1c0d 100644 --- a/tests/hash2.cpp +++ b/tests/hash2/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.hash2 smoke — hashing algorithms -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.hash2; diff --git a/tests/hof.cpp b/tests/hof.cpp index 45caadf0..2f9ef2ca 100644 --- a/tests/hof.cpp +++ b/tests/hof.cpp @@ -4,7 +4,7 @@ // friends export through the mcpplibs.mwrap.boost.hof module face. The module import works // on all three compilers — CI (gcc 16.1 linux leg, 2026-09) verified that // importing mcpplibs.mwrap.boost.hof does NOT trip the gcc include+import ODR conflict -// (describe.cpp precedent does not apply here: hof's GMF carries no +// (describe/smoke.cpp precedent does not apply here: hof's GMF carries no // make_void/mp_list-style collision family) — so no __GNUC__ guard is // needed. Macro-heavy consumption (BOOST_HOF_STATIC_FUNCTION user-side) // stays include-side and is covered in tests/hof_include.cpp. diff --git a/tests/json/coverage.toml b/tests/json/coverage.toml new file mode 100644 index 00000000..9d5365a9 --- /dev/null +++ b/tests/json/coverage.toml @@ -0,0 +1,212 @@ +# tests/json/coverage.toml — official examples and documentation snippets of +# Boost.json 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "json" + +[[example]] +path = "libs/json/example/cbor.cpp" +status = "todo" + +[[example]] +path = "libs/json/example/parse_into.cpp" +status = "todo" + +[[example]] +path = "libs/json/example/parse_into_canada.cpp" +status = "todo" + +[[example]] +path = "libs/json/example/parse_into_citm_catalog.cpp" +status = "todo" + +[[example]] +path = "libs/json/example/path.cpp" +status = "todo" + +[[example]] +path = "libs/json/example/pretty.cpp" +status = "todo" + +[[example]] +path = "libs/json/example/proxy.cpp" +status = "todo" + +[[example]] +path = "libs/json/example/use_allocator.cpp" +status = "todo" + +[[example]] +path = "libs/json/example/validate.cpp" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/README.md#instructions-for-documentation" +title = "Instructions for Documentation" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/allocators/background.adoc#polymorphic-allocators" +title = "Polymorphic Allocators" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/allocators/storage_ptr.adoc#allocator-propagation" +title = "Allocator Propagation" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/allocators/storage_ptr.adoc#default-memory-resource" +title = "Default Memory Resource" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/allocators/storage_ptr.adoc#monotonic-resource" +title = "Monotonic Resource" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/allocators/storage_ptr.adoc#resource-lifetime" +title = "Resource Lifetime" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/allocators/storage_ptr.adoc#static-resource" +title = "Static Resource" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/allocators/storage_ptr.adoc#user-defined-resource" +title = "User-Defined Resource" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/comparison.adoc#comparison-to-nlohmann-json" +title = "Comparison to nlohmann JSON" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/comparison.adoc#comparison-to-rapidjson" +title = "Comparison to RapidJSON" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/comparison.adoc#comparison-to-simd-json" +title = "Comparison to SIMD JSON" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/conversion/context.adoc#combining-contexts" +title = "Combining Contexts" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/conversion/context.adoc#contexts-and-composite-types" +title = "Contexts and Composite Types" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/conversion/context.adoc#conversions-for-third-party-types" +title = "Conversions for Third-Party Types" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/conversion/context.adoc#passing-data-to-conversion-functions" +title = "Passing Data to Conversion Functions" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/conversion/custom.adoc#conversion-traits" +title = "Conversion Traits" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/conversion/custom.adoc#tag-invoke-overloads" +title = "`tag_invoke` Overloads" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/dom/string.adoc#accessing-storage-beyond-size" +title = "Accessing Storage Beyond `size()`" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/dom/value.adoc#dom_value" +title = "Working With Values" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/examples.adoc#allocator-aware-conversion" +title = "Allocator-Aware Conversion" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/examples.adoc#cbor" +title = "CBOR" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/examples.adoc#examples_validate" +title = "Validate" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/examples.adoc#pretty" +title = "Pretty" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/io/parsing.adoc#avoiding-dynamic-allocations" +title = "Avoiding Dynamic Allocations" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/io/parsing.adoc#controlling-memory" +title = "Controlling Memory" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/io/parsing.adoc#full-precision-number-parsing" +title = "Full Precision Number Parsing" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/io/parsing.adoc#non-standard-json" +title = "Non-Standard JSON" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/io/parsing.adoc#parser" +title = "Parser" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/io/parsing.adoc#streaming-parser" +title = "Streaming Parser" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/quick_look.adoc#quick_look_allocators" +title = "Allocators" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/quick_look.adoc#quick_look_conversion" +title = "Value Conversion" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/quick_look.adoc#quick_look_parsing" +title = "Parsing" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/quick_look.adoc#quick_look_serializing" +title = "Serializing" +status = "todo" + +[[snippet]] +doc = "libs/json/doc/pages/quick_look.adoc#quick_look_values" +title = "Values" +status = "todo" diff --git a/tests/json.cpp b/tests/json/smoke.cpp similarity index 97% rename from tests/json.cpp rename to tests/json/smoke.cpp index f5f31e01..ccd815b8 100644 --- a/tests/json.cpp +++ b/tests/json/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.json smoke — compiled lib linkage (parse/serialize/error) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.json; import mcpplibs.mwrap.boost.system; diff --git a/tests/math/coverage.toml b/tests/math/coverage.toml new file mode 100644 index 00000000..de13e816 --- /dev/null +++ b/tests/math/coverage.toml @@ -0,0 +1,1910 @@ +# tests/math/coverage.toml — official examples and documentation snippets of +# Boost.math 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "math" + +[[example]] +path = "libs/math/example/HSO3SO4.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/agm_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/airy_ulps_plot.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/airy_zeros_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/arcsine_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/autodiff_black_scholes.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/autodiff_black_scholes_brief.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/autodiff_fourth_power.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/autodiff_mixed_partials.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/autodiff_multiprecision.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/autodiff_reverse_black_scholes.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/barycentric_interpolation_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/barycentric_interpolation_example_2.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/bernoulli_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/bessel_errors_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/bessel_zeros_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/bessel_zeros_example_1.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/bessel_zeros_interator_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/big_seventh.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/binomial_coinflip_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/binomial_confidence_limits.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/binomial_example_nag.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/binomial_quiz_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/binomial_sample_sizes.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/brent_minimise_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/c_error_policy_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/calculate_fourier_transform_daubechies_constants.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/cardinal_cubic_b_spline_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/catmull_rom_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/centered_continued_fraction.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/chi_square_std_dev_test.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/cma_es_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/cohen_acceleration.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/color_maps_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/color_maps_sf_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/constants_eg1.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/continued_fractions.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/cstdfloat_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/bench.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/bootstrap_chebyshev.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/daubechies_coefficients.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/daubechies_scaling_integer_grid.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/daubechies_scaling_memory_occupation.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/daubechies_scaling_plots.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/daubechies_wavelet_plots.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/find_best_daubechies_interpolator.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/regress_daubechies_accuracy.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/daubechies_wavelets/wavelet_transform.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/differential_evolution.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/distribution_construction.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/dot_net_example/boost_math/AssemblyInfo.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/dot_net_example/boost_math/Stdafx.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/dot_net_example/boost_math/boost_math.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/double_exponential.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/error_handling_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/error_policies_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/error_policy_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/estrin_vs_horner_accuracy.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/f_test.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/factorial_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/fft_sines_table.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/find_location_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/find_mean_and_sd_normal.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/find_root_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/find_scale_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/float128_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/float_comparison_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/fourier_transform_daubechies_ulp_plot.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/gauss_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/geometric_examples.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/gradient_descent_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/hyperexponential_more_snips.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/hyperexponential_snips.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/inspect_fp.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/inverse_chi_squared_bayes_eg.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/inverse_chi_squared_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/inverse_chi_squared_find_df_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/inverse_gamma_distribution_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/inverse_gamma_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/inverse_gaussian_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/jacobi_zeta_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/jso_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lambert_w_basic_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lambert_w_diode.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lambert_w_diode_graph.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lambert_w_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lambert_w_graph.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lambert_w_precision_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lambert_w_simple_examples.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/laplace_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lbfgs_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/legendre_stieltjes_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lexical_cast_native.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/lexical_cast_nonfinite_facets.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/luroth.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/naive_monte_carlo_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nc_chi_sq_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/neg_binom_confidence_limits.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/neg_binomial_sample_sizes.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/negative_binomial_example1.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/negative_binomial_example2.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nesterov_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/neumann_zeros_example_1.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nonfinite_facet_simple.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nonfinite_facet_sstream.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nonfinite_legacy.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nonfinite_loopback_ok.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nonfinite_num_facet.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nonfinite_num_facet_serialization.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nonfinite_num_facet_trap.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nonfinite_serialization_archives.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/nonfinite_signaling_NaN.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/normal_misc_examples.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/normal_tables.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/numerical_derivative_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/ooura_fourier_integrals_cosine_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/ooura_fourier_integrals_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/ooura_fourier_integrals_multiprecision_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/owens_t_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_1.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_10.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_2.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_3.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_4.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_5.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_6.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_7.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_8.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_eg_9.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip1.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip10.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip11.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip12.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip13.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip2.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip3.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip4.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip5.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip6.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip7.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip8.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/policy_ref_snip9.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/polynomial_arithmetic.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/random_search_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/reciprocal_fibonacci_constant.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/reverse_mode_linear_regression_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/root_elliptic_finding.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/root_finding_algorithms.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/root_finding_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/root_finding_fifth.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/root_finding_multiprecision_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/root_finding_n_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/root_finding_start_locations.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/root_n_finding_algorithms.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/series.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/skew_normal_example.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/special_data.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/students_t_example1.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/students_t_example2.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/students_t_example3.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/students_t_single_sample.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/students_t_two_samples.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/test_cpp_float_close_fraction.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/test_nonfinite_loopback.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/to_continued_fraction.cpp" +status = "todo" + +[[example]] +path = "libs/math/example/trapezoidal_example.cpp" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/background/implementation.qbk#sf_implementation" +title = "Additional Implementation Notes" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/background/special_tut.qbk#special_tut_impl" +title = "Implementation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/background/special_tut.qbk#special_tut_test" +title = "Testing" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/concepts/concepts.qbk#archetypes" +title = "Conceptual Archetypes for Reals and Distributions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/concepts/concepts.qbk#use_mpfr" +title = "Using With MPFR or GMP - High-Precision Floating-Point Library" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/constants/constants.qbk#new_const" +title = "Defining New Constants" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/constants/constants.qbk#non_templ" +title = "Use in non-template code" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/constants/constants.qbk#templ" +title = "Use in template code" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/constants/constants.qbk#user_def" +title = "Use With User-Defined Types" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/cstdfloat/cstdfloat.qbk#float128" +title = "Implementation of Float128 type" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/cstdfloat/cstdfloat.qbk#overloading" +title = "Overloading template functions with float128_t" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/differentiation/autodiff.qbk#autodiff" +title = "Automatic Differentiation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/differentiation/autodiff_reverse.qbk#autodiff" +title = "Reverse Mode Automatic Differentiation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/differentiation/lanczos_smoothing.qbk#diff" +title = "Lanczos Smoothing Derivatives" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/differentiation/numerical_differentiation.qbk#diff" +title = "Numerical Differentiation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/arcsine.qbk#arcine_dist" +title = "Arcsine Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/bernoulli.qbk#bernoulli_dist" +title = "Bernoulli Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/beta.qbk#beta_dist" +title = "Beta Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/binomial.qbk#binomial_dist" +title = "Binomial Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/binomial_example.qbk#binom_conf" +title = "Calculating Confidence Limits on the Frequency of Occurrence for a Binomial Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/binomial_example.qbk#binom_size_eg" +title = "Estimating Sample Sizes for a Binomial Distribution." +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/cauchy.qbk#cauchy_dist" +title = "Cauchy-Lorentz Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/chi_squared.qbk#chi_squared_dist" +title = "Chi Squared Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/chi_squared_examples.qbk#chi_sq_intervals" +title = "Confidence Intervals on the Standard Deviation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/chi_squared_examples.qbk#chi_sq_size" +title = "Estimating the Required Sample Sizes for a Chi-Square Test for the Standard Deviation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/chi_squared_examples.qbk#chi_sq_test" +title = "Chi-Square Test for the Standard Deviation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/dist_reference.qbk#future" +title = "Extras/Future Directions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/dist_tutorial.qbk#complements" +title = "Complements are supported too - and when to use them" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/empirical_cdf.qbk#empirical_cdf" +title = "Empirical Cumulative Distribution Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/exponential.qbk#exp_dist" +title = "Exponential Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/extreme_value.qbk#extreme_dist" +title = "Extreme Value Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/f_dist_example.qbk#f_eg" +title = "F Distribution Examples" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/fisher.qbk#f_dist" +title = "F Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/gamma.qbk#gamma_dist" +title = "Gamma (and Erlang) Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/geometric.qbk#geometric_dist" +title = "Geometric Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/hyperexponential.qbk#hyperexponential_dist" +title = "Hyperexponential Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/hypergeometric.qbk#hypergeometric_dist" +title = "Hypergeometric Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/inverse_chi_squared.qbk#inverse_chi_squared_dist" +title = "Inverse Chi Squared Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/inverse_gamma.qbk#inverse_gamma_dist" +title = "Inverse Gamma Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/inverse_gaussian.qbk#inverse_gaussian_dist" +title = "Inverse Gaussian (or Inverse Normal) Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/kolmogorov_smirnov.qbk#kolmogorov_smirnov_dist" +title = "Kolmogorov-Smirnov Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/laplace.qbk#laplace_dist" +title = "Laplace Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/logistic.qbk#logistic_dist" +title = "Logistic Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/lognormal.qbk#lognormal_dist" +title = "Log Normal Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/nc_beta.qbk#nc_beta_dist" +title = "Noncentral Beta Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/nc_chi_squared.qbk#nc_chi_squared_dist" +title = "Noncentral Chi-Squared Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/nc_f.qbk#nc_f_dist" +title = "Noncentral F Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/nc_t.qbk#nc_t_dist" +title = "Noncentral T Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/negative_binomial.qbk#negative_binomial_dist" +title = "Negative Binomial Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/negative_binomial_example.qbk#neg_binom_size_eg" +title = "Estimating Sample Sizes for the Negative Binomial." +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/normal.qbk#normal_dist" +title = "Normal (Gaussian) Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/pareto.qbk#pareto" +title = "Pareto Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/poisson.qbk#poisson_dist" +title = "Poisson Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/rayleigh.qbk#rayleigh" +title = "Rayleigh Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/skew_normal.qbk#skew_normal_dist" +title = "Skew Normal Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/students_t.qbk#students_t_dist" +title = "Students t Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/students_t_examples.qbk#tut_mean_intervals" +title = "Calculating confidence intervals on the mean with the Students-t distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/students_t_examples.qbk#tut_mean_size" +title = "Estimating how large a sample size would have to become" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/students_t_examples.qbk#two_sample_students_t" +title = "Comparing the means of two samples with the Students-t test" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/triangular.qbk#triangular_dist" +title = "Triangular Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/uniform.qbk#uniform_dist" +title = "Uniform Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/distributions/weibull.qbk#weibull_dist" +title = "Weibull Distribution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/filters/daubechies.qbk#daubechies_filters" +title = "Daubechies Filters" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/condition_numbers.qbk#cond" +title = "Condition Numbers" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/float_comparison.qbk#float_comparison" +title = "Floating-point Comparison" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/float_next.qbk#float_advance" +title = "Advancing a floating-point Value by a Specific" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/float_next.qbk#float_distance" +title = "Calculating the Representation Distance" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/float_next.qbk#float_next" +title = "Finding the Next Greater Representable Value (float_next)" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/float_next.qbk#float_prior" +title = "Finding the Next Smaller Representable Value (float_prior)" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/float_next.qbk#nextafter" +title = "Finding the Next Representable Value in a Specific Direction (nextafter)" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/float_next.qbk#ulp" +title = "Obtaining the Size of a Unit In the Last Place - ULP" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/fp_facets.qbk#fp_facets" +title = "Facets for Floating-Point Infinities and NaNs" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/sign.qbk#sign_functions" +title = "Sign Manipulation Functions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/fp_utilities/ulps_plots.qbk#ulps_plots" +title = "ULPs Plots" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/agm.qbk#agm" +title = "Arithmetic-Geometric Mean" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/centered_continued_fraction.qbk#centered_continued_fraction" +title = "Centered Continued Fractions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/cohen_acceleration.qbk#cohen_acceleration" +title = "Cohen Acceleration" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/color_maps.qbk#color_maps" +title = "Color Maps" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/engel_expansion.qbk#engel_expansion" +title = "Engel Expansion" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/estrin.qbk#estrin" +title = "Estrin's method for polynomial evaluation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/fraction.qbk#cf" +title = "Continued Fraction Evaluation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/luroth_expansion.qbk#luroth_expansion" +title = "Luroth Expansions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/minimax.qbk#minimax" +title = "Minimax Approximations and the Remez Algorithm" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/polynomial.qbk#polynomials" +title = "Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/rational.qbk#rational" +title = "Polynomial and Rational Function Evaluation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/recurrence.qbk#recurrence" +title = "Tools For 3-Term Recurrence Relations" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/relative_error.qbk#error_test" +title = "Relative Error and Testing" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/series.qbk#series_evaluation" +title = "Series Evaluation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/simple_continued_fraction.qbk#simple_continued_fraction" +title = "Simple Continued Fractions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/test_data.qbk#test_data" +title = "Graphing, Profiling, and Generating Test Data for Special Functions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/internals/tuple.qbk#tuples" +title = "Tuples" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/barycentric_rational_interpolation.qbk#barycentric" +title = "Barycentric Rational Interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/bezier_polynomial.qbk#bezier_polynomial" +title = "Bezier Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/bilinear_uniform.qbk#bilinear_uniform" +title = "Bilinear Uniform Interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/cardinal_cubic_b_spline.qbk#cardinal_cubic_b" +title = "Cardinal Cubic B-spline interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/cardinal_quadratic_b_spline.qbk#cardinal_quadratic_b" +title = "Cardinal Quadratic B-spline interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/cardinal_quintic_b_spline.qbk#cardinal_quintic_b" +title = "Cardinal Quintic B-spline interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/cardinal_trigonometric.qbk#cardinal_trigonometric" +title = "Cardinal Trigonometric interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/catmull_rom.qbk#catmull_rom" +title = "Catmull-Rom Splines" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/cubic_hermite.qbk#cubic_hermite" +title = "Cubic Hermite interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/makima.qbk#makima" +title = "Modified Akima interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/pchip.qbk#pchip" +title = "PCHIP interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/quintic_hermite.qbk#quintic_hermite" +title = "Quintic Hermite interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/vector_barycentric_rational.qbk#vector_barycentric" +title = "Vector-valued Barycentric Rational Interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/interpolators/whittaker_shannon.qbk#whittaker_shannon" +title = "Whittaker-Shannon interpolation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/octonion/math-octonion.qbk#oct_non_mem" +title = "Octonion Non-Member Operators" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/octonion/math-octonion.qbk#oct_specialization" +title = "Octonion Specializations" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/octonion/math-octonion.qbk#octonion" +title = "Template Class octonion" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/optimization/cma_es.qbk#cma_es" +title = "Evolution Strategy with Covariance Matrix Adaptation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/optimization/differential_evolution.qbk#differential_evolution" +title = "Differential Evolution" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/optimization/gradient_optimizers.qbk#gradient_descent" +title = "Gradient Descent" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/optimization/gradient_optimizers.qbk#lbfgs" +title = "L-BFGS" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/optimization/gradient_optimizers.qbk#minimize" +title = "minimize" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/optimization/gradient_optimizers.qbk#nesterov" +title = "Nesterov Accelerated Gradient Descent" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/optimization/gradient_optimizers.qbk#policies" +title = "Reverse Mode autodiff policies" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/optimization/jso.qbk#jso" +title = "Algorithm jSO" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/optimization/random_search.qbk#random_search" +title = "Random Search" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/overview/gpu.qbk#gpu" +title = "Support for GPU programming in Boost.Math" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/policies/policy.qbk#pol_ref_ref" +title = "Policy Class Reference" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/policies/policy.qbk#policy_defaults" +title = "Using Macros to Change the Policy Defaults" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/policies/policy_tutorial.qbk#ad_hoc_sf_policies" +title = "Changing the Policy on an Ad Hoc Basis for the Special Functions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/double_exponential.qbk#de_caveats" +title = "Caveats" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/double_exponential.qbk#de_exp_sinh" +title = "exp_sinh" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/double_exponential.qbk#de_levels" +title = "Setting the Maximum Interval Halvings and Memory Requirements" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/double_exponential.qbk#de_overview" +title = "Overview" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/double_exponential.qbk#de_sinh_sinh" +title = "sinh_sinh" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/double_exponential.qbk#de_tanh_sinh" +title = "tanh_sinh" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/double_exponential.qbk#de_tanh_sinh_2_arg" +title = "Handling functions with large features near an endpoint with tanh-sinh quadrature" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/double_exponential.qbk#gpu_usage" +title = "GPU Usage" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/gauss.qbk#gauss" +title = "Gauss-Legendre quadrature" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/gauss_kronrod.qbk#gauss_kronrod" +title = "Gauss-Kronrod Quadrature" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/naive_monte_carlo.qbk#naive_monte_carlo" +title = "Naive Monte Carlo Integration" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/ooura_fourier_integrals.qbk#fourier_integrals" +title = "Fourier Integrals" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/trapezoidal.qbk#trapezoidal" +title = "Trapezoidal Quadrature" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quadrature/wavelet_transforms.qbk#wavelet_transforms" +title = "Wavelet Transforms" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quaternion/math-quaternion.qbk#quat" +title = "Template Class quaternion" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quaternion/math-quaternion.qbk#quat_non_mem" +title = "Quaternion Non-Member Operators" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/quaternion/math-quaternion.qbk#spec" +title = "Quaternion Specializations" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/roots/cubic_roots.qbk#cubic_roots" +title = "Roots of Cubic Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/roots/minima.qbk#brent_minima" +title = "Locating Function Minima using Brent's algorithm" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/roots/quartic_roots.qbk#quartic_roots" +title = "Roots of Quartic Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/roots/roots.qbk#roots_deriv" +title = "Root Finding With Derivatives: Newton-Raphson, Halley & Schroeder" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/roots/roots_without_derivatives.qbk#TOMS748" +title = "Algorithm TOMS 748: Alefeld, Potra and Shi: Enclosing zeros of continuous functions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/roots/roots_without_derivatives.qbk#bisect" +title = "Bisection" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/roots/roots_without_derivatives.qbk#bracket_solve" +title = "Bracket and Solve Root" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/roots/roots_without_derivatives.qbk#root_termination" +title = "Termination Condition Functors" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/roots/roots_without_derivatives.qbk#roots_noderiv" +title = "Root Finding Without Derivatives" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/airy.qbk#ai" +title = "Airy Ai Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/airy.qbk#aip" +title = "Airy Ai' Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/airy.qbk#bi" +title = "Airy Bi Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/airy.qbk#bip" +title = "Airy Bi' Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/bernoulli_numbers.qbk#bernoulli_numbers" +title = "Bernoulli Numbers" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/bernoulli_numbers.qbk#tangent_numbers" +title = "Tangent Numbers" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/beta.qbk#beta_function" +title = "Beta" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/beta_derivative.qbk#beta_derivative" +title = "Derivative of the Incomplete Beta Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/cardinal_b_splines.qbk#cardinal_b_splines" +title = "Cardinal B-splines" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ccmath.qbk#ccmath" +title = "Constexpr CMath" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/chebyshev.qbk#chebyshev" +title = "Chebyshev Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/daubechies.qbk#daubechies" +title = "Daubechies Wavelets and Scaling Functions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/digamma.qbk#digamma" +title = "Digamma" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ellint_carlson.qbk#ellint_carlson" +title = "Elliptic Integrals - Carlson Form" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ellint_legendre.qbk#ellint_1" +title = "Elliptic Integrals of the First Kind - Legendre Form" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ellint_legendre.qbk#ellint_2" +title = "Elliptic Integrals of the Second Kind - Legendre Form" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ellint_legendre.qbk#ellint_3" +title = "Elliptic Integrals of the Third Kind - Legendre Form" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ellint_legendre.qbk#ellint_d" +title = "Elliptic Integral D - Legendre Form" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ellint_legendre.qbk#heuman_lambda" +title = "Heuman Lambda Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ellint_legendre.qbk#jacobi_zeta" +title = "Jacobi Zeta Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/erf.qbk#error_function" +title = "Error Function erf and complement erfc" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/erf_inv.qbk#error_inv" +title = "Error Function Inverses" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/expint.qbk#expint_i" +title = "Exponential Integral Ei" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/expint.qbk#expint_n" +title = "Exponential Integral En" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/factorials.qbk#sf_binomial" +title = "Binomial Coefficients" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/factorials.qbk#sf_double_factorial" +title = "Double Factorial" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/factorials.qbk#sf_factorial" +title = "Factorial" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/factorials.qbk#sf_falling_factorial" +title = "Falling Factorial" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/factorials.qbk#sf_rising_factorial" +title = "Rising Factorial" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/gamma_derivatives.qbk#gamma_derivatives" +title = "Derivative of the Incomplete Gamma Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/gamma_ratios.qbk#gamma_ratios" +title = "Ratios of Gamma Functions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/gegenbauer.qbk#gegenbauer" +title = "Gegenbauer Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/hermite.qbk#hermite" +title = "Hermite Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/hypergeometric.qbk#hypergeometric_1f1" +title = "Hypergeometric [sub 1]/F/[sub 1]" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/hypergeometric.qbk#hypergeometric_pfq" +title = "Hypergeometric [sub p]F[sub q]" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ibeta.qbk#ibeta_function" +title = "Incomplete Beta Functions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/ibeta_inv.qbk#ibeta_inv_function" +title = "The Incomplete Beta Function Inverses" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/igamma.qbk#igamma" +title = "Incomplete Gamma Functions" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/igamma_inv.qbk#igamma_inv" +title = "Incomplete Gamma Function Inverses" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/inv_hyper.qbk#acosh" +title = "acosh" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/inv_hyper.qbk#asinh" +title = "asinh" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/inv_hyper.qbk#atanh" +title = "atanh" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi.qbk#jacobi" +title = "Jacobi Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_cd" +title = "Jacobi Elliptic Function cd" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_cn" +title = "Jacobi Elliptic Function cn" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_cs" +title = "Jacobi Elliptic Function cs" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_dc" +title = "Jacobi Elliptic Function dc" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_dn" +title = "Jacobi Elliptic Function dn" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_ds" +title = "Jacobi Elliptic Function ds" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_elliptic" +title = "Jacobi Elliptic SN, CN and DN" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_nc" +title = "Jacobi Elliptic Function nc" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_nd" +title = "Jacobi Elliptic Function nd" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_ns" +title = "Jacobi Elliptic Function ns" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_sc" +title = "Jacobi Elliptic Function sc" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_sd" +title = "Jacobi Elliptic Function sd" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_elliptic.qbk#jacobi_sn" +title = "Jacobi Elliptic Function sn" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_theta.qbk#jacobi_theta1" +title = "Jacobi Theta Function [theta][sub 1]" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_theta.qbk#jacobi_theta2" +title = "Jacobi Theta Function [theta][sub 2]" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_theta.qbk#jacobi_theta3" +title = "Jacobi Theta Function [theta][sub 3]" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/jacobi_theta.qbk#jacobi_theta4" +title = "Jacobi Theta Function [theta][sub 4]" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/laguerre.qbk#laguerre" +title = "Laguerre (and Associated) Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/lambert_w.qbk#lambert_w" +title = "Lambert /W/ function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/legendre.qbk#legendre" +title = "Legendre (and Associated) Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/legendre_stieltjes.qbk#legendre_stieltjes" +title = "Legendre-Stieltjes Polynomials" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/lgamma.qbk#lgamma" +title = "Log Gamma" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/logaddexp.qbk#logaddexp" +title = "logaddexp and logsumexp" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/logistic_sigmoid.qbk#logistic_sigmoid" +title = "logistic_sigmoid" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/logit.qbk#logit" +title = "logit" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/number_series.qbk#bernoulli_numbers" +title = "Bernoulli Numbers" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/number_series.qbk#fibonacci_numbers" +title = "Fibonacci Numbers" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/number_series.qbk#primes" +title = "Prime Numbers" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/number_series.qbk#tangent_numbers" +title = "Tangent Numbers" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/owens_t.qbk#owens_t" +title = "Owen's T function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/polygamma.qbk#polygamma" +title = "Polygamma" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/pow.qbk#ct_pow" +title = "Compile Time Power of a Runtime Base" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/powers.qbk#cbrt" +title = "cbrt" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/powers.qbk#cos_pi" +title = "cos_pi" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/powers.qbk#expm1" +title = "expm1" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/powers.qbk#log1p" +title = "log1p" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/powers.qbk#powm1" +title = "powm1" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/powers.qbk#sin_pi" +title = "sin_pi" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/powers.qbk#sqrt1pm1" +title = "sqrt1pm1" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/rsqrt.qbk#rsqrt" +title = "Reciprocal square root" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/sinc.qbk#sinc-pi" +title = "sinc_pi" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/sinc.qbk#sinhc-pi" +title = "sinhc_pi" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/spherical_harmonic.qbk#sph_harm" +title = "Spherical Harmonics" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/tgamma.qbk#tgamma" +title = "Gamma" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/trigamma.qbk#trigamma" +title = "Trigamma" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/sf/zeta.qbk#zeta" +title = "Riemann Zeta Function" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/anderson_darling.qbk#anderson_darling" +title = "The Anderson-Darling Test" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/bivariate_statistics.qbk#bivariate_statistics" +title = "Bivariate Statistics" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/chatterjee_correlation.qbk#chatterjee_correlation" +title = "Chatterjee Correlation" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/linear_regression.qbk#linear_regression" +title = "Linear Regression" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/ljung_box.qbk#ljung_box" +title = "The Ljung-Box Test" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/runs_test.qbk#runs_test" +title = "Runs tests" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/signal_statistics.qbk#signal_statistics" +title = "Signal Statistics" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/t_test.qbk#t_test" +title = "/t/-tests" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/univariate_statistics.qbk#univariate_statistics" +title = "Univariate Statistics" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/statistics/z_test.qbk#z_test" +title = "/z/-tests" +status = "todo" + +[[snippet]] +doc = "libs/math/doc/vector_functionals/norms.qbk#norms" +title = "Norms" +status = "todo" + +[[snippet]] +doc = "libs/math/tools/doc/cstdfloat.qbk#overview" +title = "Overview" +status = "todo" diff --git a/tests/math.cpp b/tests/math/smoke.cpp similarity index 95% rename from tests/math.cpp rename to tests/math/smoke.cpp index 5c3f79ba..4269ebf3 100644 --- a/tests/math.cpp +++ b/tests/math/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.math smoke — header-only module face (src/tr1 excluded, M11 doc §2) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.math; diff --git a/tests/mqtt5/coverage.toml b/tests/mqtt5/coverage.toml new file mode 100644 index 00000000..8ccb8fca --- /dev/null +++ b/tests/mqtt5/coverage.toml @@ -0,0 +1,85 @@ +# tests/mqtt5/coverage.toml — official examples and documentation snippets of +# Boost.mqtt5 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "mqtt5" + +[[example]] +path = "libs/mqtt5/example/hello_world_in_coro_multithreaded_env.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/hello_world_in_multithreaded_env.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/hello_world_over_tcp.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/hello_world_over_tls.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/hello_world_over_websocket_tcp.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/hello_world_over_websocket_tls.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/multiflight_client.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/publisher.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/receiver.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/timeout_with_awaitable_operators.cpp" +status = "todo" + +[[example]] +path = "libs/mqtt5/example/timeout_with_parallel_group.cpp" +status = "todo" + +[[snippet]] +doc = "libs/mqtt5/doc/qbk/01_intro.qbk#intro" +title = "Introduction" +status = "todo" + +[[snippet]] +doc = "libs/mqtt5/doc/qbk/02_getting_started.qbk#transport_protocol" +title = "Choosing a Suitable Transport Protocol" +status = "todo" + +[[snippet]] +doc = "libs/mqtt5/doc/qbk/03_auto_reconnect.qbk#auto_reconnect" +title = "Built-in Auto-Reconnect and Retry Mechanism" +status = "todo" + +[[snippet]] +doc = "libs/mqtt5/doc/qbk/06_disconnecting_the_client.qbk#disconnecting_the_client" +title = "Disconnecting the Client" +status = "todo" + +[[snippet]] +doc = "libs/mqtt5/doc/qbk/06_disconnecting_the_client.qbk#reusing_the_client" +title = "Restarting the Client After Disconnection" +status = "todo" + +[[snippet]] +doc = "libs/mqtt5/doc/qbk/09_per_op_cancellation.qbk#per_op_cancellation" +title = "Per-Operation Cancellation" +status = "todo" + +[[snippet]] +doc = "libs/mqtt5/doc/qbk/10_executors.qbk#executors" +title = "Executors" +status = "todo" diff --git a/tests/mqtt5.cpp b/tests/mqtt5/smoke.cpp similarity index 98% rename from tests/mqtt5.cpp rename to tests/mqtt5/smoke.cpp index 3a2f3c5a..2c859c4f 100644 --- a/tests/mqtt5.cpp +++ b/tests/mqtt5/smoke.cpp @@ -2,7 +2,7 @@ // The prop::xxx named constants used to be constexpr (internal-linkage) // objects; the vendored `inline constexpr` patch makes them external, so the // module exports the named-constant spelling (no integral_constant workaround). -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.mqtt5; diff --git a/tests/openmethod.cpp b/tests/openmethod.cpp index ed1840d2..be97898c 100644 --- a/tests/openmethod.cpp +++ b/tests/openmethod.cpp @@ -1,6 +1,6 @@ // boost.openmethod smoke — pure include (C1 downgrade, 2026-09-06: the module // is gone; the public API is the BOOST_OPENMETHOD* macro family — M10 rule — -// and gcc 16 ODR-conflicts on include+import mixing, see describe.cpp / the +// and gcc 16 ODR-conflicts on include+import mixing, see describe/smoke.cpp / the // C1 plan §1.1). initialize()/finalize() live in a separate header the old // module GMF included; include it explicitly here. #include "test_assert.hpp" diff --git a/tests/regex/coverage.toml b/tests/regex/coverage.toml new file mode 100644 index 00000000..a87c5330 --- /dev/null +++ b/tests/regex/coverage.toml @@ -0,0 +1,219 @@ +# tests/regex/coverage.toml — official examples and documentation snippets of +# Boost.regex 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "regex" + +[[example]] +path = "libs/regex/example/grep/grep.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/captures_example.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/credit_card_example.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/icu_example.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/mfc_example.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/partial_regex_grep.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/partial_regex_iterate.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/partial_regex_match.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_grep_example_1.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_grep_example_2.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_grep_example_3.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_grep_example_4.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_iterator_example.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_match_example.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_merge_example.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_replace_example.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_search_example.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_split_example_1.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_split_example_2.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_token_iterator_eg_1.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/snippets/regex_token_iterator_eg_2.cpp" +status = "todo" + +[[example]] +path = "libs/regex/example/timer/regex_timer.cpp" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/bad_expression.qbk#bad_expression" +title = "bad_expression" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/basic_regex.qbk#basic_regex" +title = "basic_regex" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/captures.qbk#captures" +title = "Understanding Marked Sub-Expressions and Captures" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/icu_strings.qbk#unicode_algo" +title = "Unicode Regular Expression Algorithms" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/icu_strings.qbk#unicode_iter" +title = "Unicode Aware Regex Iterators" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/icu_strings.qbk#unicode_types" +title = "Unicode regular expression types" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/introduction.qbk#intro" +title = "Introduction and Overview" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/match_result.qbk#match_results" +title = "match_results" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/mfc_strings.qbk#mfc_algo" +title = "Overloaded Algorithms For MFC String Types" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/mfc_strings.qbk#mfc_iter" +title = "Iterating Over the Matches Within An MFC String" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/mfc_strings.qbk#mfc_regex_create" +title = "Regular Expression Creation From an MFC String" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/modules.qbk#modules" +title = "Module Support (Experimental)" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/partial_matches.qbk#partial_matches" +title = "Partial Matches" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/regex_format.qbk#regex_format" +title = "regex_format (Deprecated)" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/regex_grep.qbk#regex_grep" +title = "regex_grep (Deprecated)" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/regex_iterator.qbk#regex_iterator" +title = "regex_iterator" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/regex_match.qbk#regex_match" +title = "regex_match" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/regex_replace.qbk#regex_replace" +title = "regex_replace" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/regex_search.qbk#regex_search" +title = "regex_search" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/regex_split.qbk#regex_split" +title = "regex_split (deprecated)" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/regex_token_iterator.qbk#regex_token_iterator" +title = "regex_token_iterator" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/regex_traits.qbk#regex_traits" +title = "regex_traits" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/sub_match.qbk#sub_match" +title = "sub_match" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/syntax_perl.qbk#perl_syntax" +title = "Perl Regular Expression Syntax" +status = "todo" + +[[snippet]] +doc = "libs/regex/doc/unicode_iterators.qbk#uni_iter" +title = "Unicode Iterators" +status = "todo" diff --git a/tests/regex.cpp b/tests/regex/smoke.cpp similarity index 97% rename from tests/regex.cpp rename to tests/regex/smoke.cpp index bd404deb..150ffce2 100644 --- a/tests/regex.cpp +++ b/tests/regex/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.regex smoke — compiled lib linkage (match/search/replace/iterator) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.regex; diff --git a/tests/scope_exit.cpp b/tests/scope_exit.cpp index bd077266..4b49aef6 100644 --- a/tests/scope_exit.cpp +++ b/tests/scope_exit.cpp @@ -1,7 +1,7 @@ // boost.scope_exit smoke — pure include (C1 downgrade, 2026-09-06: the module // is gone; the library is macro-driven — BOOST_SCOPE_EXIT etc. are the public // API and macros never cross module boundaries, M10 rule; gcc 16 also -// ODR-conflicts on include+import mixing, see describe.cpp / C1 plan §1.1). +// ODR-conflicts on include+import mixing, see describe/smoke.cpp / C1 plan §1.1). // mcpplibs.mwrap.boost.core has no include counterpart for these names, so its import stays // (ignore_unused). Version constants now come from mcpplibs.mwrap.boost.version (C5 2026-09-09); // is already pulled transitively by diff --git a/tests/statechart.cpp b/tests/statechart.cpp index 8dc7b597..f31d3acf 100644 --- a/tests/statechart.cpp +++ b/tests/statechart.cpp @@ -1,6 +1,6 @@ // mcpplibs.mwrap.boost.statechart smoke — simple state machine (mpl::list is include-only; // mixing include + import is standard-compliant, but gcc 16 ODR-conflicts on -// it — same redefinition pattern as describe.cpp — so the import is skipped on +// it — same redefinition pattern as describe/smoke.cpp — so the import is skipped on // gcc, keeping the test pure-header) #include "test_assert.hpp" #include diff --git a/tests/test/coverage.toml b/tests/test/coverage.toml new file mode 100644 index 00000000..9709e8a8 --- /dev/null +++ b/tests/test/coverage.toml @@ -0,0 +1,1019 @@ +# tests/test/coverage.toml — official examples and documentation snippets of +# Boost.test 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "test" + +[[example]] +path = "libs/test/doc/examples/boost_runtime_list_content.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_bitwise.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_container_default.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_container_lex.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_container_lex_default.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_macro2.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_macro3.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_macro_container_c_array.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_macro_overview.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_macro_workaround.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_message.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_sequence_per_element.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/boost_test_string.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/custom_init.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/custom_main.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/custom_obsolete_init.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/dataset_example59.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/dataset_example61.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/dataset_example62.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/dataset_example63.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/dataset_example64.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/dataset_example65.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/dataset_example66.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/dataset_example67.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/dataset_example68.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_00.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_01.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_02.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_03.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_04.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_05.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_06.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_07.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_08.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_09.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_10.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_11.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_12.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_13.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_20.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_21.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_22.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/decorator_23.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example01.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example03.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example06.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example07.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example08.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example09.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example10.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example11.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example12.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example13.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example14.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example15.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example16.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example17.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example18.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example20.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example21.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example22.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example23.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example24.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example25.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example26.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example28.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example29.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example30.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example31.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example32.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example33.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example34.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example35.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example36.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example38.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example40.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example41.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example42.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example43.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example44.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example46.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example47.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example48.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example49.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example50.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example51.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example52.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example53.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example54.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example55.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example56.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example57.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example58.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example80_contexts.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example81_contexts.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example82_contexts.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example83_contexts.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/example84_contexts.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/exception_api.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/exception_check.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/exception_check_predicate.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/exception_nothrow.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/exception_uncaught.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/fixture_02.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/fixture_03.run.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/fixture_04.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/logger-customization-point.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/runtime-configuration_1.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/runtime-configuration_2.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/runtime-configuration_3.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/runtime-configuration_4.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/tolerance_01.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/tolerance_02.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/tolerance_03.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/tolerance_04.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/tolerance_05.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/doc/examples/tolerance_06.run-fail.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/const_string_test.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/exec_mon_example.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/external_main_example_1.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/external_main_example_2.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/external_main_example_3.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/filtering_example.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/named_param_example.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/prg_exec_example.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/test_case_template_example.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_01.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_02.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_03.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_04.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_05.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_06.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_07.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_08.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_09_1.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_09_2.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_10.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_11.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_12.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_13.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_15.cpp" +status = "todo" + +[[example]] +path = "libs/test/example/unit_test_example_16.cpp" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/building_utf.qbk#build_utf" +title = "Building the __UTF__" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/entry_point_overview.qbk#entry_point_overview" +title = "Test module's entry point" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/link_reference.qbk#link_boost_test_no_main" +title = "`BOOST_TEST_NO_MAIN`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/obsolete_init_func.qbk#obsolete_init_func" +title = "The obsolete initialization function" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/shared_lib_customizations.qbk#entry_point" +title = "Customizing the module's entry point" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/shared_lib_customizations.qbk#init_func" +title = "Customizing the module's initialization function" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/static_lib_customizations.qbk#entry_point" +title = "Customizing the module's entry point" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/static_lib_customizations.qbk#init_func" +title = "Customizing the module's initialization function" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/test_module_init_overview.qbk#test_module_init_overview" +title = "Test module's initialization" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/adv_scenarios/test_module_runner_overview.qbk#test_module_runner_overview" +title = "Test module runner" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/closing_chapters/faq.qbk#section_faq" +title = "Frequently Asked Questions" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/introduction/introduction.qbk#intro" +title = "Introduction" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/prod_use/program_execution_monitor.qbk#section_pem_compilation" +title = "Compilation" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/runtime_configuration/runtime_config_reference.qbk#list_labels" +title = "`list_labels`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/runtime_configuration/runtime_config_reference.qbk#log_level" +title = "`log_level`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/runtime_configuration/runtime_config_reference.qbk#logger" +title = "`logger`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/runtime_configuration/runtime_config_reference.qbk#run_test" +title = "`run_test`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/runtime_configuration/runtime_config_reference.qbk#save_pattern" +title = "`save_pattern`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/runtime_configuration/runtime_custom.qbk#custom-command-line-arguments" +title = "Custom command line arguments" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/runtime_configuration/test_unit_filtering.qbk#test_unit_filtering" +title = "Test unit filtering" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/decorators.qbk#explicit-decorator-declaration" +title = "Explicit decorator declaration" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/fixtures.qbk#case" +title = "Test case fixture" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/fixtures.qbk#fixtures" +title = "Fixtures" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/fixtures.qbk#global" +title = "Global fixture" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/fixtures.qbk#models" +title = "Fixture models" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/master_test_suite.qbk#master_test_suite" +title = "Master test suite" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/nullary_tests.qbk#test_organization_nullary" +title = "Test cases without parameters" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/parametric_test_case_generation.qbk#dataset-creation-and-delayed-creation" +title = "Dataset creation and delayed creation" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/parametric_test_case_generation.qbk#datasets_auto_registration" +title = "Declaring and registering test cases with datasets" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/parametric_test_case_generation.qbk#generators" +title = "Datasets generators" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/parametric_test_case_generation.qbk#grid-cartesian-products" +title = "Grid (Cartesian products)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/parametric_test_case_generation.qbk#joins" +title = "Joins" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/parametric_test_case_generation.qbk#test_case_generation" +title = "Data-driven test cases" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/parametric_test_case_generation.qbk#zips" +title = "Zips" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/test_suites.qbk#test_suite" +title = "Test suite" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/testorg_reference.qbk#decorator_depends_on" +title = "depends_on (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/testorg_reference.qbk#decorator_description" +title = "description (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/testorg_reference.qbk#decorator_enable_if" +title = "enable_if (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/testorg_reference.qbk#decorator_enabled" +title = "enabled / disabled (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/testorg_reference.qbk#decorator_fixture" +title = "fixture (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/testorg_reference.qbk#decorator_label" +title = "label (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/testorg_reference.qbk#decorator_precondition" +title = "precondition (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/testorg_reference.qbk#test_org_boost_test_dataset" +title = "`BOOST_DATA_TEST_CASE`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/testorg_reference.qbk#test_org_boost_test_dataset_fixture" +title = "`BOOST_DATA_TEST_CASE_F`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/typed_parametrized_tests.qbk#test_organization_templates" +title = "Template test cases" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_organization/unary_tests.qbk#param_test" +title = "Parametrized test cases" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_output/checkpoints.qbk#checkpoints" +title = "Checkpoints for accurate failure location" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_output/contexts.qbk#contexts" +title = "Contexts" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_output/logger_api.qbk#log_ct_log_format" +title = "Predefined log format selection" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_output/logger_api.qbk#log_ct_output_stream_redirection" +title = "Log output stream redirection" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_output/logger_api.qbk#logging_api" +title = "Logging API" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_output/progress_display.qbk#test_output_progress" +title = "Progress display" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_output/report_format.qbk#report_human_readable_format" +title = "Human readable report format" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/test_output/test_tools_support.qbk#testing_tool_output_disable" +title = "Logging user defined types" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/boost_test_collection_comparison.qbk#collections" +title = "Collections comparison" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/boost_test_technical_details.qbk#internal_details" +title = "`BOOST_TEST`: details on expressions" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/boost_test_universal_macro.qbk#boost_test_universal_macro" +title = "BOOST_TEST: universal and general purpose assertions" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/expected_failures.qbk#expected_failures" +title = "Expected failures specification" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_floating_points.qbk#customizing_for_tolerance" +title = "Enabling tolerance for user-defined types" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_floating_points.qbk#floating_point" +title = "Floating point comparison" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_floating_points.qbk#floating_points_comparison_impl" +title = "Tolerance-based comparisons" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_floating_points.qbk#floating_points_comparison_theory" +title = "Theory behind floating point comparisons" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_error" +title = "`BOOST_ERROR`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_fail" +title = "`BOOST_FAIL`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_is_defined" +title = "`BOOST_IS_DEFINED`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_bitwise_eq" +title = "`BOOST__BITWISE_EQUAL`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_close" +title = "`BOOST__CLOSE`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_close_fraction" +title = "`BOOST__CLOSE_FRACTION`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_eq" +title = "`BOOST__EQUAL`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_eq_collections" +title = "`BOOST__EQUAL_COLLECTIONS`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_exception" +title = "`BOOST__EXCEPTION`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_ge" +title = "`BOOST__GE`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_gt" +title = "`BOOST__GT`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_le" +title = "`BOOST__LE`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_lt" +title = "`BOOST__LT`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_message" +title = "`BOOST__MESSAGE`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_ne" +title = "`BOOST__NE`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_no_throw" +title = "`BOOST__NO_THROW`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_predicate" +title = "`BOOST__PREDICATE`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_small" +title = "`BOOST__SMALL`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_level_throw" +title = "`BOOST__THROW`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#assertion_boost_test_universal_macro" +title = "`BOOST_TEST`" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#decorator_expected_failures" +title = "expected_failures (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#decorator_timeout" +title = "timeout (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/testing_tools/testing_tools_reference.qbk#decorator_tolerance" +title = "tolerance (decorator)" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/tutorials/hello_world.qbk#hello" +title = "A testing framework, what for?" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/tutorials/new_year_resolution.qbk#bt_and_tdd" +title = "Test driven development with Boost.Test" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/usage_recommendations.qbk#general" +title = "General" +status = "todo" + +[[snippet]] +doc = "libs/test/doc/usage_variants.qbk#usage_variants" +title = "Usage variants" +status = "todo" diff --git a/tests/test_included.cpp b/tests/test/included/smoke.cpp similarity index 88% rename from tests/test_included.cpp rename to tests/test/included/smoke.cpp index af333e7a..100c63ed 100644 --- a/tests/test_included.cpp +++ b/tests/test/included/smoke.cpp @@ -8,10 +8,10 @@ // into every test program — the aggregate's inline definitions would // double-define those symbols (M11 §3: the two forms must never link // together), so this file compiles to a passing stub in that run; -// tests/test_utf.cpp carries the real test there. +// tests/test/utf/smoke.cpp carries the real test there. #ifdef MCPP_FEATURE_UNIT_TEST_FRAMEWORK -// Feature on: the compiled-form run (see test_utf.cpp) — the framework TUs +// Feature on: the compiled-form run (see tests/test/utf/smoke.cpp) — the framework TUs // linked into this program would collide with the aggregate below. int main() { return 0; } diff --git a/tests/test/utf/mcpp.toml b/tests/test/utf/mcpp.toml new file mode 100644 index 00000000..d7a448ef --- /dev/null +++ b/tests/test/utf/mcpp.toml @@ -0,0 +1,17 @@ +# Test project of the compiled Boost.Test form (design .agents/docs/2026-11-09- +# module-test-hardening-design.md §5): the unit-test-framework feature links +# the framework TUs into every test program, which the header-only form +# (tests/test/included/, root project) must never meet. The root mcpp.toml +# keeps tests/test/utf/** out of its own [test] discover; CI runs `mcpp test` +# here. +[package] +name = "boost-m-tests-test-utf" +version = "0.0.0" +description = "Tests of the compiled Boost.Test framework (unit-test-framework feature)" +license = "Apache-2.0" + +[dependencies] +"mcpplibs.mwrap.boost" = { path = "../../..", default-features = false, features = ["unit-test-framework"] } + +[test] +discover = ["*.cpp"] diff --git a/tests/test/utf/smoke.cpp b/tests/test/utf/smoke.cpp new file mode 100644 index 00000000..3db97c74 --- /dev/null +++ b/tests/test/utf/smoke.cpp @@ -0,0 +1,30 @@ +// boost.test smoke — compiled Unit Test Framework form (the official +// recommended configuration). boost.test is compiled include-only — the +// unit-test-framework feature ships the framework TUs but NO module; there is +// no `import boost.test`. The consumer TU owns main + the unit_test_main +// runner: BOOST_TEST_NO_MAIN pulls the runner without its main wrapper, +// BOOST_TEST_MODULE enables the auto-registration init glue +// (unit_test_suite.hpp) and the BOOST_TEST_* macros stay include-side. +// +// This project (tests/test/utf/mcpp.toml) always enables the feature, so the +// file no longer switches on MCPP_FEATURE_UNIT_TEST_FRAMEWORK; the +// header-only form lives in the root project (tests/test/included/), and the +// two never link together. +#define BOOST_TEST_MODULE boost_test_module_smoke +#define BOOST_TEST_NO_MAIN +#include +#include + +BOOST_AUTO_TEST_CASE(arithmetic) { + BOOST_TEST(1 + 1 == 2); + BOOST_CHECK_EQUAL(3 * 3, 9); +} + +BOOST_AUTO_TEST_CASE(strings) { + std::string s = "boost"; + BOOST_TEST(s == "boost"); +} + +int main(int argc, char* argv[]) { + return boost::unit_test::unit_test_main(&init_unit_test_suite, argc, argv); +} diff --git a/tests/test_utf.cpp b/tests/test_utf.cpp deleted file mode 100644 index 54542da3..00000000 --- a/tests/test_utf.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// boost.test smoke — compiled Unit Test Framework form (the official -// recommended configuration). C1 (2026-09-06): boost.test is compiled -// include-only — the feature (renamed unit_test_framework, aligned with the -// upstream CMake target) ships the framework TUs but NO module; there is no -// `import boost.test`. The consumer TU owns main + the unit_test_main runner: -// BOOST_TEST_NO_MAIN pulls the runner without its main wrapper, -// BOOST_TEST_MODULE enables the auto-registration init glue -// (unit_test_suite.hpp) and the BOOST_TEST_* macros stay include-side (M10 -// pattern). Exit code 0 == all checks passed. -// -// The two Unit Test Framework forms must never link together (the framework -// TUs and the aggregate double-define the framework -// symbols, M11 §3), so this file compiles to a passing stub when the feature -// is off — tests/test_included.cpp carries the real test in that run -// (MCPP_FEATURE_ is defined for every active feature, verified 2026-09-06). -#ifdef MCPP_FEATURE_UNIT_TEST_FRAMEWORK - -#define BOOST_TEST_MODULE boost_test_module_smoke -#define BOOST_TEST_NO_MAIN -#include -#include - -BOOST_AUTO_TEST_CASE(arithmetic) { - BOOST_TEST(1 + 1 == 2); - BOOST_CHECK_EQUAL(3 * 3, 9); -} - -BOOST_AUTO_TEST_CASE(strings) { - std::string s = "boost"; - BOOST_TEST(s == "boost"); -} - -int main(int argc, char* argv[]) { - return boost::unit_test::unit_test_main(&init_unit_test_suite, argc, argv); -} - -#else - -// Feature off: the header-only form's run (see test_included.cpp). The -// framework TUs are not compiled in this build, so there is nothing to -// exercise here — pass and stay out of the way. -int main() { return 0; } - -#endif diff --git a/tests/units.cpp b/tests/units.cpp index 60092527..08449fde 100644 --- a/tests/units.cpp +++ b/tests/units.cpp @@ -4,7 +4,7 @@ // through the mcpplibs.mwrap.boost.units module face alongside the quantity/class surface. // The module import works on all three compilers — CI (gcc 16.1 linux leg, // 2026-09) verified that importing mcpplibs.mwrap.boost.units does NOT trip the gcc -// include+import ODR conflict (describe.cpp precedent does not apply: the +// include+import ODR conflict (describe/smoke.cpp precedent does not apply: the // units GMF carries no collision-prone entity family, despite the large // macro face M10 own-283 — macros and the module face coexist fine). The // BOOST_UNITS_* macro API stays include-side and is covered in diff --git a/tests/unordered/coverage.toml b/tests/unordered/coverage.toml new file mode 100644 index 00000000..c2ed60bb --- /dev/null +++ b/tests/unordered/coverage.toml @@ -0,0 +1,75 @@ +# tests/unordered/coverage.toml — official examples and documentation snippets of +# Boost.unordered 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "unordered" + +[[example]] +path = "libs/unordered/examples/case_insensitive_test.cpp" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/compliance.adoc#concurrent-containers" +title = "Concurrent Containers" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/compliance.adoc#piecewise-pair-emplacement" +title = "Piecewise Pair Emplacement" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#bulk-visitation" +title = "Bulk visitation" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#interoperability-with-non-concurrent-containers" +title = "Interoperability with non-concurrent containers" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#visitation-based-api" +title = "Visitation-based API" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#whole-table-visitation" +title = "Whole-Table Visitation" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/debuggability.adoc#using-in-your-project" +title = "Using in your project" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/debuggability.adoc#visualization-structure" +title = "Visualization structure" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/hash_equality.adoc#hash_equality" +title = "Custom Types" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/hash_quality.adoc#container-statistics" +title = "Container Statistics" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/hash_quality.adoc#hash-post-mixing-and-the-avalanching-property" +title = "Hash Post-mixing and the Avalanching Property" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/regular.adoc#iterator-invalidation" +title = "Iterator Invalidation" +status = "todo" + +[[snippet]] +doc = "libs/unordered/doc/modules/ROOT/pages/structures.adoc#closed-addressing-containers" +title = "Closed-addressing Containers" +status = "todo" diff --git a/tests/unordered.cpp b/tests/unordered/smoke.cpp similarity index 96% rename from tests/unordered.cpp rename to tests/unordered/smoke.cpp index 55ed6481..bed6d293 100644 --- a/tests/unordered.cpp +++ b/tests/unordered/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.unordered smoke — boost unordered containers -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.unordered; diff --git a/tests/url/coverage.toml b/tests/url/coverage.toml new file mode 100644 index 00000000..8de24252 --- /dev/null +++ b/tests/url/coverage.toml @@ -0,0 +1,221 @@ +# tests/url/coverage.toml — official examples and documentation snippets of +# Boost.url 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "url" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/example/file_router.cpp" +status = "todo" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/example/finicky.cpp" +status = "todo" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/example/magnet.cpp" +status = "todo" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/example/mailto.cpp" +status = "todo" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/example/qrcode.cpp" +status = "todo" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/example/router.cpp" +status = "todo" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/example/sanitize.cpp" +status = "todo" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/unit/doc_3_urls.cpp" +status = "todo" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/unit/doc_grammar.cpp" +status = "todo" + +[[example]] +path = "libs/url/doc/modules/ROOT/examples/unit/snippets.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/file_router/file_router.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/finicky/finicky.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/magnet/magnet.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/mailto/mailto.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/qrcode/qrcode.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/router/detail/impl/router.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/router/impl/matches.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/router/router.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/sanitize/sanitize.cpp" +status = "todo" + +[[example]] +path = "libs/url/example/suffix_list/suffix_list.cpp" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/README.md#instructions-for-documentation" +title = "Instructions for Documentation" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/grammar/charset.adoc#the-lut-chars-type" +title = "The lut_chars Type" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/grammar/combinators.adoc#optional-rule" +title = "Optional Rule" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/grammar/combinators.adoc#tuple-rule" +title = "Tuple Rule" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/grammar/combinators.adoc#variant-rule" +title = "Variant Rule" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/grammar/index.adoc#grammar" +title = "Syntax Notation" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/grammar/rfc3986.adoc#percent-encoding" +title = "Percent Encoding" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/grammar/rfc3986.adoc#url-rules" +title = "URL Rules" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/quicklook.adoc#accessing" +title = "Accessing" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/quicklook.adoc#compound-elements" +title = "Compound elements" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/quicklook.adoc#integration" +title = "Integration" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/quicklook.adoc#modifying" +title = "Modifying" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/quicklook.adoc#parsing" +title = "Parsing" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/quicklook.adoc#percent-encoding" +title = "Percent-Encoding" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/components.adoc#api-reference" +title = "API Reference" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/components.adoc#authority" +title = "Authority" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/components.adoc#scheme" +title = "Scheme" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/normalization.adoc#scheme-and-protocol-based-normalization" +title = "Scheme and Protocol-Based Normalization" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/normalization.adoc#simple-string-comparison" +title = "Simple String Comparison" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/normalization.adoc#syntax-based-normalization" +title = "Syntax-Based Normalization" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/normalization.adoc#syntax-based-normalization-procedure" +title = "Syntax-Based Normalization Procedure" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/parsing.adoc#constexpr-parsing" +title = "Constexpr Parsing" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/parsing.adoc#parsing-urls" +title = "Parsing URLs" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/parsing.adoc#result-type" +title = "Result Type" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/percent-encoding.adoc#decode" +title = "Decode" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/percent-encoding.adoc#encoding" +title = "Encoding" +status = "todo" + +[[snippet]] +doc = "libs/url/doc/modules/ROOT/pages/urls/percent-encoding.adoc#validating" +title = "Validating" +status = "todo" diff --git a/tests/url.cpp b/tests/url/smoke.cpp similarity index 98% rename from tests/url.cpp rename to tests/url/smoke.cpp index 551dd8b9..f6fb31af 100644 --- a/tests/url.cpp +++ b/tests/url/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.url smoke — compiled lib linkage (parse/components/encode/decode) -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.url; import mcpplibs.mwrap.boost.system; diff --git a/tests/uuid/coverage.toml b/tests/uuid/coverage.toml new file mode 100644 index 00000000..1331ed4f --- /dev/null +++ b/tests/uuid/coverage.toml @@ -0,0 +1,221 @@ +# tests/uuid/coverage.toml — official examples and documentation snippets of +# Boost.uuid 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "uuid" + +[[snippet]] +doc = "libs/uuid/doc/uuid/basic_random_generator.adoc#basic-random-generator" +title = "basic_random_generator" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/constants.adoc#constants" +title = "Constants" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/examples.adoc#byte-extraction" +title = "Byte Extraction" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/examples.adoc#examples" +title = "Tagging" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/invalid_uuid.adoc#invalid-uuid" +title = "invalid_uuid" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/name_generator_md5.adoc#name-generator-md5" +title = "name_generator_md5" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/name_generator_sha1.adoc#name-generator-sha1" +title = "name_generator_sha1" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/namespaces.adoc#namespaces" +title = "Namespaces" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/nil_generator.adoc#nil-generator" +title = "nil_generator" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/nil_generator.adoc#nil-uuid" +title = "nil_uuid" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/random_generator.adoc#random-generator" +title = "random_generator" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/string_generator.adoc#string-generator" +title = "string_generator" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/time_generator_v1.adoc#constructors" +title = "Constructors" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/time_generator_v1.adoc#operator" +title = "operator()" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/time_generator_v7.adoc#assignment" +title = "Assignment" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/time_generator_v7.adoc#constructors" +title = "Constructors" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/time_generator_v7.adoc#operator" +title = "operator()" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#constructors" +title = "Constructors" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#data" +title = "Data" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#free-swap" +title = "Free Swap" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#hash-value" +title = "hash_value" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#is-nil" +title = "is_nil" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#iteration" +title = "Iteration" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#operators" +title = "Operators" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#serialization" +title = "Serialization" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#size" +title = "Size" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#std-hash" +title = "std::hash" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#swap" +title = "Swap" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#time-based-fields" +title = "Time-based Fields" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#variant" +title = "Variant" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid.adoc#version" +title = "Version" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#from-sys" +title = "from_sys" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#from-timestamp" +title = "from_timestamp" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#now" +title = "now" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#to-sys" +title = "to_sys" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#to-timestamp" +title = "to_timestamp" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_io.adoc#from-chars" +title = "from_chars" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_io.adoc#from-chars-result" +title = "from_chars_result" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_io.adoc#stream-extraction" +title = "Stream Extraction" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_io.adoc#stream-insertion" +title = "Stream Insertion" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_io.adoc#to-chars" +title = "to_chars" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_io.adoc#to-string" +title = "to_string" +status = "todo" + +[[snippet]] +doc = "libs/uuid/doc/uuid/uuid_io.adoc#uuid-from-string" +title = "uuid_from_string" +status = "todo" diff --git a/tests/uuid.cpp b/tests/uuid/smoke.cpp similarity index 95% rename from tests/uuid.cpp rename to tests/uuid/smoke.cpp index c046dbc9..12fdcd1f 100644 --- a/tests/uuid.cpp +++ b/tests/uuid/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.uuid smoke — uuid generation and comparison -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.uuid; diff --git a/tests/variant2/api.cpp b/tests/variant2/api.cpp new file mode 100644 index 00000000..7bc27715 --- /dev/null +++ b/tests/variant2/api.cpp @@ -0,0 +1,95 @@ +// mcpplibs.mwrap.boost.variant2 — the reference surface: constructors +// (in_place_type / in_place_index), emplace, swap, relational operators, +// get / get_if / unsafe_get / holds_alternative, visit, visit_by_index, +// variant_size / variant_alternative, subset<>(), operator<< and hashing. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.variant2; +import mcpplibs.mwrap.boost.container_hash; // the definition of boost::hash + +namespace v2 = boost::variant2; +using V = v2::variant; + +TEST_CASE("construction and emplace") { + V a; + CHECK(a.index() == 0); + V b(v2::in_place_type, 3, 'x'); + CHECK(v2::get(b) == "xxx"); + V c(v2::in_place_index<2>, 1.5); + CHECK(v2::get<2>(c) == 1.5); + auto& s = c.emplace("abc"); + CHECK(s == "abc"); + c.emplace<0>(5); + CHECK(v2::get(c) == 5); + V d(v2::in_place_type, {'a', 'b'}); + CHECK(v2::get<1>(d) == "ab"); + static_assert(v2::variant_size_v == 3); + static_assert(std::is_same_v, std::string>); + static_assert(v2::variant_size::value == 3); +} + +TEST_CASE("access") { + V v = std::string("hi"); + CHECK(v2::holds_alternative(v)); + CHECK(v2::get_if(&v) == nullptr); + CHECK(*v2::get_if<1>(&v) == "hi"); + CHECK(v2::unsafe_get<1>(v) == "hi"); + v2::get(v) += "!"; + CHECK(v2::get<1>(std::as_const(v)) == "hi!"); + std::string moved = v2::get<1>(std::move(v)); + CHECK(moved == "hi!"); + CHECK_THROWS_WITH_AS(v2::get(v), doctest::Contains("bad"), v2::bad_variant_access); +} + +TEST_CASE("relational operators and swap") { + V a = 1, b = 2, s = std::string("a"); + CHECK(a < b); + CHECK(b < s); // ordered by index first + CHECK(a != b); + CHECK(a <= a); + CHECK(s >= b); + CHECK(s > a); + swap(a, s); + CHECK(v2::holds_alternative(a)); + a.swap(s); + CHECK(v2::get(a) == 1); + CHECK(v2::variant() == v2::variant()); +} + +TEST_CASE("visit and visit_by_index") { + V v = 2.5; + auto r = v2::visit([](auto const& x) -> std::string { + if constexpr (std::is_same_v, std::string>) return x; + else return std::to_string(int(x * 2)); + }, v); + CHECK(r == "5"); + // visit converts each result to R. + long size = v2::visit([](auto const& x) { return sizeof(x); }, v); + CHECK(size == long(sizeof(double))); + std::string which; + v2::visit_by_index(v, + [&](int) { which = "int"; }, + [&](std::string const&) { which = "string"; }, + [&](double) { which = "double"; }); + CHECK(which == "double"); + v2::variant a = 1, b = 'x'; + CHECK(v2::visit([](auto x, auto y) { return int(x) + int(y); }, a, b) == 1 + 'x'); +} + +TEST_CASE("subset, streaming, hashing") { + V v = 7; + auto sub = v.subset(); + CHECK(v2::get(sub) == 7); + V s = std::string("x"); + auto narrow = [&] { return s.subset(); }; + CHECK_THROWS_AS(narrow(), v2::bad_variant_access); + std::ostringstream os; + os << v << ' ' << s; + CHECK(os.str() == "7 x"); + CHECK(std::hash()(v) == std::hash()(V(7))); + CHECK(boost::hash()(v) == boost::hash()(V(7))); + CHECK(std::hash()(v) != std::hash()(V(8))); + std::unordered_set set{V(1), V(std::string("a")), V(1.0)}; + CHECK(set.size() == 3); +} diff --git a/tests/variant2/coverage.toml b/tests/variant2/coverage.toml new file mode 100644 index 00000000..7871420c --- /dev/null +++ b/tests/variant2/coverage.toml @@ -0,0 +1,31 @@ +# tests/variant2/coverage.toml — official examples and documentation snippets of +# Boost.variant2 1.92.0 mapped to test cases (tools/ci/test_coverage.py; design +# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6). +# `scan` adds and drops entries; status, case and reason are maintained by hand. +upstream = "1.92.0" +library = "variant2" + +[[snippet]] +doc = "libs/variant2/doc/variant2/overview.adoc#construction-and-assignment" +title = "Construction and Assignment" +case = "variant2/examples:doc: construction-and-assignment" + +[[snippet]] +doc = "libs/variant2/doc/variant2/overview.adoc#description" +title = "Description" +case = "variant2/examples:doc: description" + +[[snippet]] +doc = "libs/variant2/doc/variant2/overview.adoc#inspecting-the-value" +title = "Inspecting the Value" +case = "variant2/examples:doc: inspecting-the-value" + +[[snippet]] +doc = "libs/variant2/doc/variant2/overview.adoc#usage-examples" +title = "Usage Examples" +case = "variant2/examples:doc: usage-examples" + +[[snippet]] +doc = "libs/variant2/doc/variant2/overview.adoc#visitation" +title = "Visitation" +case = "variant2/examples:doc: visitation" diff --git a/tests/variant2/custom_types.cpp b/tests/variant2/custom_types.cpp new file mode 100644 index 00000000..d71d9fce --- /dev/null +++ b/tests/variant2/custom_types.cpp @@ -0,0 +1,144 @@ +// mcpplibs.mwrap.boost.variant2 — user alternatives: types whose construction +// throws (variant2's never-valueless guarantee, with double storage when an +// alternative is not nothrow move constructible), non-movable types built in +// place, user visitors (overload sets, visitors with state, multi-variant +// visits), user ordering/streaming/hashing used by the variant's own +// operators, and recursive data through an indirection. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.variant2; +import mcpplibs.mwrap.boost.container_hash; // the definition of boost::hash + +namespace v2 = boost::variant2; + +namespace user { +struct thrower { + static inline bool fail = false; + int v = 0; + thrower() = default; + explicit thrower(int x) : v(x) { + if (fail) throw std::runtime_error("ctor"); + } + thrower(const thrower& o) : v(o.v) { + if (fail) throw std::runtime_error("copy"); + } + thrower(thrower&& o) : v(o.v) { // not noexcept + if (fail) throw std::runtime_error("move"); + } + thrower& operator=(const thrower&) = default; +}; + +struct pinned { + explicit pinned(int x) : v(x) {} + pinned(const pinned&) = delete; + pinned& operator=(const pinned&) = delete; + int v; +}; + +struct money { + long cents; + auto operator<=>(const money&) const = default; + friend std::ostream& operator<<(std::ostream& os, const money& m) { + return os << m.cents / 100 << '.' << std::setw(2) << std::setfill('0') << m.cents % 100; + } + friend std::size_t hash_value(const money& m) { return std::hash()(m.cents); } +}; + +struct counter { // a stateful visitor, passed by std::ref + int calls = 0; + void operator()(const auto&) { ++calls; } +}; + +template +struct overloaded : Fs... { + using Fs::operator()...; +}; +template +overloaded(Fs...) -> overloaded; + +// A JSON-like recursive value through std::vector / std::map indirection. +struct node; +using array = std::vector; +struct node { + v2::variant value; +}; +std::size_t count_leaves(const node& n) { + return v2::visit(overloaded{[](const array& a) { + std::size_t s = 0; + for (auto& x : a) s += count_leaves(x); + return s; + }, + [](const auto&) -> std::size_t { return 1; }}, + n.value); +} +} // namespace user + +template <> +struct std::hash { + std::size_t operator()(const user::money& m) const noexcept { return std::hash()(m.cents); } +}; + +TEST_CASE("never valueless: a throwing alternative keeps the old value") { + using V = v2::variant; + V v = 5; + user::thrower::fail = true; + CHECK_THROWS_AS(v.emplace(1), std::runtime_error); + user::thrower::fail = false; + // variant2 never becomes valueless: v still holds a valid alternative. + CHECK(!v.valueless_by_exception()); + CHECK((v2::holds_alternative(v) || v2::holds_alternative(v))); + v = user::thrower(3); + CHECK(v2::get(v).v == 3); + V copy = v; + CHECK(v2::get<1>(copy).v == 3); +} + +TEST_CASE("non-movable alternatives are built in place") { + v2::variant v; + v.emplace(42); + CHECK(v2::get(v).v == 42); + v2::variant w(v2::in_place_index<0>, 7); + CHECK(v2::get<0>(w).v == 7); +} + +TEST_CASE("user visitors") { + using V = v2::variant; + std::vector items{1, std::string("two"), user::money{350}}; + std::string log; + for (auto& x : items) + v2::visit(user::overloaded{[&](int i) { log += "i" + std::to_string(i); }, + [&](const std::string& s) { log += "s" + s; }, + [&](const user::money& m) { log += "m" + std::to_string(m.cents); }}, + x); + CHECK(log == "i1stwom350"); + user::counter c; + for (auto& x : items) v2::visit(std::ref(c), x); + CHECK(c.calls == 3); + auto same_kind = [](const auto& a, const auto& b) { + return std::is_same_v, std::decay_t>; + }; + CHECK(v2::visit(same_kind, items[2], V(user::money{1}))); + CHECK_FALSE(v2::visit(same_kind, items[0], items[1])); +} + +TEST_CASE("user ordering, streaming and hashing through the variant's operators") { + using V = v2::variant; + V a = user::money{199}, b = user::money{250}; + CHECK(a < b); + CHECK(a == V(user::money{199})); + std::ostringstream os; + os << a; + CHECK(os.str() == "1.99"); + CHECK(std::hash()(a) == std::hash()(V(user::money{199}))); + CHECK(boost::hash()(a) == boost::hash()(V(user::money{199}))); + std::set sorted{V(std::string("x")), b, a}; + CHECK(v2::get<0>(*sorted.begin()).cents == 199); +} + +TEST_CASE("recursive user data") { + using user::node; + node doc{user::array{node{1.0}, node{std::string("s")}, + node{user::array{node{true}, node{nullptr}}}}}; + CHECK(user::count_leaves(doc) == 4); +} diff --git a/tests/variant2/examples.cpp b/tests/variant2/examples.cpp new file mode 100644 index 00000000..f07478ef --- /dev/null +++ b/tests/variant2/examples.cpp @@ -0,0 +1,145 @@ +// mcpplibs.mwrap.boost.variant2 — the overview examples of Boost.Variant2 +// (libs/variant2/doc/variant2/overview.adoc) as test cases. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.variant2; + +using namespace boost::variant2; + +namespace shapes_virtual { +class Shape { +public: + virtual ~Shape() = default; + virtual double area() const = 0; +}; +class Rectangle : public Shape { + double width_, height_; +public: + Rectangle(double width, double height) : width_(width), height_(height) {} + double area() const override { return width_ * height_; } +}; +class Circle : public Shape { + double radius_; +public: + explicit Circle(double radius) : radius_(radius) {} + double area() const override { return std::numbers::pi * radius_ * radius_; } +}; +double total_area(std::vector> const& v) { + double s = 0.0; + for (auto const& p : v) s += p->area(); + return s; +} +} // namespace shapes_virtual + +namespace shapes_variant { +struct Rectangle { + double width_, height_; + double area() const { return width_ * height_; } +}; +struct Circle { + double radius_; + double area() const { return std::numbers::pi * radius_ * radius_; } +}; +double total_area(std::vector> const& v) { + double s = 0.0; + for (auto const& x : v) s += visit([](auto const& y) { return y.area(); }, x); + return s; +} +} // namespace shapes_variant + +TEST_CASE("doc: description") { + // A configuration file of key=value lines as map>. + std::map> config; + std::istringstream file("server.host=test.example.com\nserver.port=9174\ncache.max_load=0.7\n"); + for (std::string line; std::getline(file, line);) { + auto eq = line.find('='); + std::string key = line.substr(0, eq), value = line.substr(eq + 1); + if (value.find_first_not_of("0123456789") == std::string::npos) + config[key] = std::int64_t(std::stoll(value)); + else if (value.find_first_not_of("0123456789.") == std::string::npos) + config[key] = std::stod(value); + else + config[key] = value; + } + CHECK(get(config["server.host"]) == "test.example.com"); + CHECK(get(config["server.port"]) == 9174); + CHECK(get(config["cache.max_load"]) == doctest::Approx(0.7)); +} + +TEST_CASE("doc: usage-examples") { + std::vector> v1; + v1.push_back(std::make_unique(1.0)); + v1.push_back(std::make_unique(2.0, 3.0)); + std::vector> v2; + v2.push_back(shapes_variant::Circle{1.0}); + v2.push_back(shapes_variant::Rectangle{2.0, 3.0}); + CHECK(shapes_variant::total_area(v2) == doctest::Approx(std::numbers::pi + 6.0)); + CHECK(shapes_variant::total_area(v2) == doctest::Approx(shapes_virtual::total_area(v1))); +} + +TEST_CASE("doc: construction-and-assignment") { + variant v = shapes_variant::Circle{1.0}; + CHECK(v.index() == 1); + v = shapes_variant::Rectangle{2.0, 3.0}; + CHECK(v.index() == 0); + variant w((short)1); // overload resolution picks int + CHECK(holds_alternative(w)); +} + +namespace { +std::string f_index(variant const& v) { + switch (v.index()) { + case 0: return "int " + std::to_string(get<0>(v)); + case 1: return "float " + std::to_string(int(get<1>(v))); + default: return "never"; + } +} +std::string f_get_if(variant const& v) { + if (int const* p = get_if(&v)) return "int " + std::to_string(*p); + if (float const* p = get_if(&v)) return "float " + std::to_string(int(*p)); + return "never"; +} +} // namespace + +TEST_CASE("doc: inspecting-the-value") { + variant a = 3, b = 2.0f; + CHECK(f_index(a) == "int 3"); + CHECK(f_index(b) == "float 2"); + CHECK(f_get_if(a) == "int 3"); + CHECK(f_get_if(b) == "float 2"); + CHECK_THROWS_AS(get<0>(b), bad_variant_access); + CHECK_THROWS_AS(get(a), bad_variant_access); +} + +namespace { +double f(double x) { return x; } +double g1(variant const& v) { return visit(f, v); } + +struct F { + std::string* out; + void operator()(int x) const { *out = "int " + std::to_string(x); } + void operator()(float) const { *out = "float"; } +}; +} // namespace + +TEST_CASE("doc: visitation") { + CHECK(g1(variant(4)) == 4.0); + CHECK(g1(variant(1.5f)) == 1.5); + std::string out; + visit(F{&out}, variant(7)); + CHECK(out == "int 7"); + std::ostringstream os; + visit([&](auto const& x) { os << x; }, variant(9)); + CHECK(os.str() == "9"); + variant v1 = 2, v2 = 0.5f; + CHECK(visit([](auto x1, auto x2) { return double(x1) * double(x2); }, v1, v2) == 1.0); + // Default construction value-initializes the first alternative; monostate + // avoids constructing anything. + CHECK(get(variant{}) == 0); + CHECK(get(variant{}) == 0.0f); + variant m; + CHECK(m.index() == 0); + m.emplace(); + CHECK(holds_alternative(m)); +} diff --git a/tests/variant2.cpp b/tests/variant2/smoke.cpp similarity index 97% rename from tests/variant2.cpp rename to tests/variant2/smoke.cpp index b79d4bf9..fe1c358b 100644 --- a/tests/variant2.cpp +++ b/tests/variant2/smoke.cpp @@ -1,5 +1,5 @@ // mcpplibs.mwrap.boost.variant2 smoke — variant, visit, get_if, holds_alternative -#include "test_assert.hpp" +#include "../test_assert.hpp" import std; import mcpplibs.mwrap.boost.variant2; diff --git a/tools/boost_common.py b/tools/boost_common.py index b4f56204..5e44a80a 100644 --- a/tools/boost_common.py +++ b/tools/boost_common.py @@ -49,7 +49,7 @@ def _boost_version(): # C1 downgrade (2026-09-06): scope_exit is include-only now — its public API # is the BOOST_SCOPE_EXIT_* macro family (macros never cross module # boundaries, M10 rule), and gcc 16.1 ODR-conflicts on include+import mixing -# (describe.cpp precedent), so consumers #include . +# (describe/smoke.cpp precedent), so consumers #include . LIBS_M3 = [ "optional", "variant", "variant2", "any", "core", "container_hash", "mp11", "static_string", "scope", "type_traits", @@ -240,12 +240,13 @@ def _boost_version(): # the official header-only configuration ( # aggregate,自带 main). The two forms must never link together (the # included aggregate and the framework TUs double-define the framework -# symbols, M11 §3) — tests/test_utf.cpp vs tests/test_included.cpp are -# gated on MCPP_FEATURE_UNIT_TEST_FRAMEWORK. +# symbols, M11 §3): tests/test/utf/ is a project of its own that always +# enables the feature; tests/test/included/ (root project) is gated on +# MCPP_FEATURE_UNIT_TEST_FRAMEWORK. # The feature names live in gen_features.py (test is renamed # `unit_test_framework` there, aligned with the upstream CMake target # boost_unit_test_framework); the internal keys here stay `log` / `test` -# (COMPILED_TU_GLOBS keys, tests/test_utf.cpp naming). +# (COMPILED_TU_GLOBS keys). LIBS_COMPILED_INCLUDE_ONLY = ["log", "test"] # Target triple for the bundle AST snapshot. The committed .inc files are a diff --git a/tools/ci/test_coverage.py b/tools/ci/test_coverage.py new file mode 100644 index 00000000..2bb50d71 --- /dev/null +++ b/tools/ci/test_coverage.py @@ -0,0 +1,473 @@ +#!/usr/bin/env python3 +"""Official-example coverage of the hardened library tests. + +Each library under test has ``tests//coverage.toml``. It lists every +example program (``libs//**/example*/**.cpp``) and every documentation +section that carries a C++ code block, as shipped in the Boost release +archive, and maps each one to a test case or states why it is not covered. +The vendored tree under ``upstream/boost/`` is pruned of ``example/`` and +``doc/``, so the inventory is committed and refreshed when Boost is upgraded. + + scan read the release archive (no extraction; its SHA-256 must match + upstream/boost/SOURCE.md) and write or refresh the inventories. + Existing entries keep their status; new ones are ``todo``; entries + the archive no longer has are reported and dropped. + link fill in `case` for the ``todo`` entries whose test case follows the + naming convention below; prints what is still ``todo``. + check consistency of the committed inventories, without the archive: + no ``todo``; a ``case`` names an existing test file that declares + that test case; every other status carries a reason; libraries + with customization points have tests//custom_types.cpp. + +Entry status: + + covered case = ":" (status may be omitted) + excluded the example needs what CI cannot provide (§4.5 of the design) + gap the module does not export what the example needs + unsupported the library is not part of the package (context, coroutine2) + +A is the path under tests/ without ``.cpp`` (``json/examples``); +for the Boost.Test projects it is the path of the source under tests/. +A is the doctest ``TEST_CASE("...")`` string, or the +``BOOST_AUTO_TEST_CASE(name)`` identifier in tests/test/. + +Naming convention (what ``link`` matches, in any tests//**/*.cpp): + + libs//**/.cpp TEST_CASE("example: ") + libs//**/.qbk#
TEST_CASE("doc: #
"), or + TEST_CASE("doc:
") when no + other page of the library has
+ +Documentation sections are the granularity for snippets: a qbk +``[section ...]`` or an AsciiDoc / Markdown heading whose body has a code +block. Code a qbk page imports from an example file (``[import ...]``) is +covered by that example's entry. Reference, change-log and history pages are +API listings, not usage, and are skipped, as are sections titled as a +synopsis or a header (````): they declare, they do not use. + +Usage: + python3 tools/ci/test_coverage.py scan --archive .tmp/boost-ref/boost_1_92_0.tar.gz + python3 tools/ci/test_coverage.py scan --archive ... --libs json url + python3 tools/ci/test_coverage.py check +""" + +import argparse +import hashlib +import re +import sys +import tarfile +import tomllib +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +TESTS = ROOT / "tests" +SOURCE_MD = ROOT / "upstream" / "boost" / "SOURCE.md" + +# The libraries of the hardening (design §2), in the order of the design. +LIBRARIES = [ + "asio", "beast", "bloom", "cobalt", "container", "decimal", "geometry", + "describe", "hash2", "json", "math", "mqtt5", "regex", "test", + "unordered", "url", "uuid", "variant2", "algorithm", "any", "bimap", + "callable_traits", "charconv", "circular_buffer", "compat", + "container_hash", "context", "conversion", "convert", "core", + "coroutine2", "crc", "dll", "dynamic_bitset", +] +# Not part of the package (docs/usage.md §2.5): every entry is `unsupported`. +UNSUPPORTED = { + "context": "Boost.Context needs an assembler toolchain; the package does " + "not declare it (docs/usage.md, unsupported libraries)", + "coroutine2": "Boost.Coroutine2 is built on Boost.Context, which the " + "package does not declare (docs/usage.md)", +} +# Libraries with customization points: tests//custom_types.cpp is +# mandatory (design §4.3). charconv has none. +CUSTOM_TYPES = set(LIBRARIES) - {"charconv", "context", "coroutine2"} +# Projects of their own (design §5): custom_types lives in the project dir. +SUBPROJECTS = {"dll", "test"} + +STATUSES = {"covered", "excluded", "gap", "unsupported", "todo"} + +_SKIP_DOC = re.compile( + r"(^|/)(reference|ref|changes?|changelog|release[-_]?notes|history|" + r"copyright|acknowledg\w*|credits|news)(/|[._-]|$)", re.I) +# Sections that list declarations rather than use them. +_SKIP_TITLE = re.compile( + r"synopsis|^header\b|/... + if "test" in rest[:-1]: + continue + if (member.name.endswith(".cpp") + and (any(p.startswith("example") for p in rest[:-1]) + or "example" in rest[-1])): + found[lib]["examples"].append(rel) + continue + if "doc" not in rest[:-1] or not rel.endswith((".qbk", ".adoc", ".md")): + continue + if _SKIP_DOC.search("/".join(rest[rest.index("doc") + 1:])): + continue + text = tar.extractfile(member).read().decode("utf-8", "replace") + secs = (qbk_sections(text) if rel.endswith(".qbk") + else md_sections(text)) + seen = {} + for sid, title in secs: + if _SKIP_TITLE.search(title): + continue + seen[sid] = seen.get(sid, 0) + 1 + key = sid if seen[sid] == 1 else "{}-{}".format(sid, seen[sid]) + found[lib]["snippets"].append((rel + "#" + key, title)) + for lib in found: + found[lib]["examples"].sort() + found[lib]["snippets"].sort() + return found + + +# --------------------------------------------------------------------------- +# inventory files +# --------------------------------------------------------------------------- + +def coverage_path(lib): + return TESTS / lib / "coverage.toml" + + +def load(lib): + path = coverage_path(lib) + if not path.is_file(): + return None + with open(path, "rb") as f: + return tomllib.load(f) + + +def _q(value): + return '"' + str(value).replace("\\", "\\\\").replace('"', '\\"') + '"' + + +_FIELD_ORDER = ["path", "doc", "title", "status", "case", "reason"] + + +def dump(lib, data): + lines = [ + "# tests/{}/coverage.toml — official examples and documentation " + "snippets of".format(lib), + "# Boost.{} {} mapped to test cases (tools/ci/test_coverage.py; " + "design".format(lib, data["upstream"]), + "# .agents/docs/2026-11-09-module-test-hardening-design.md §4.6).", + "# `scan` adds and drops entries; status, case and reason are " + "maintained by hand.", + "upstream = " + _q(data["upstream"]), + "library = " + _q(lib), + ] + for table in ("example", "snippet"): + for entry in data.get(table, []): + lines += ["", "[[{}]]".format(table)] + keys = [k for k in _FIELD_ORDER if k in entry] + keys += sorted(k for k in entry if k not in _FIELD_ORDER) + width = max(len(k) for k in keys) + for k in keys: + lines.append("{} = {}".format(k.ljust(width), _q(entry[k]))) + text = "\n".join(lines) + "\n" + path = coverage_path(lib) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8", newline="\n") + + +def merge(lib, scanned, version): + old = load(lib) or {} + old_ex = {e["path"]: e for e in old.get("example", [])} + old_sn = {e["doc"]: e for e in old.get("snippet", [])} + default = ({"status": "unsupported", "reason": UNSUPPORTED[lib]} + if lib in UNSUPPORTED else {"status": "todo"}) + examples, snippets = [], [] + for path in scanned["examples"]: + examples.append(old_ex.pop(path, None) or dict(path=path, **default)) + for doc, title in scanned["snippets"]: + entry = old_sn.pop(doc, None) or dict(doc=doc, **default) + entry["title"] = title + snippets.append(entry) + for key in list(old_ex) + list(old_sn): + print(" {}: dropped {} (not in the archive)".format(lib, key)) + dump(lib, {"upstream": version, "example": examples, "snippet": snippets}) + todo = sum(1 for e in examples + snippets if e.get("status") == "todo") + print("{:16} examples {:4} snippets {:4} todo {:4}".format( + lib, len(examples), len(snippets), todo)) + + +def cmd_scan(args): + archive = Path(args.archive) + want, got = recorded_sha256(), sha256_of(archive) + if want != got: + raise SystemExit("archive SHA-256 {} != {} recorded in {}".format( + got, want, SOURCE_MD.relative_to(ROOT))) + libs = args.libs or LIBRARIES + unknown = sorted(set(libs) - set(LIBRARIES)) + if unknown: + raise SystemExit("not a library of the hardening: " + ", ".join(unknown)) + found = scan_archive(archive, libs) + for lib in libs: + merge(lib, found[lib], recorded_version()) + + +# --------------------------------------------------------------------------- +# check +# --------------------------------------------------------------------------- + +_DOCTEST_CASE = re.compile(r'\bTEST_CASE(?:_TEMPLATE|_FIXTURE)?\s*\(\s*(?:\w+\s*,\s*)?"((?:[^"\\]|\\.)*)"') +_BOOST_CASE = re.compile(r"\bBOOST_(?:AUTO|DATA|FIXTURE)_TEST_CASE(?:_TEMPLATE)?\s*\(\s*(\w+)") + + +def test_cases(test): + path = TESTS / (test + ".cpp") + if not path.is_file(): + return None + text = path.read_text(encoding="utf-8", errors="replace") + return set(_DOCTEST_CASE.findall(text)) | set(_BOOST_CASE.findall(text)) + + +def check_entry(lib, entry, key, errors, cache): + status = entry.get("status", "covered" if "case" in entry else "todo") + where = "tests/{}/coverage.toml: {}".format(lib, entry.get(key, "?")) + if status not in STATUSES: + errors.append("{}: unknown status {!r}".format(where, status)) + return status + if status == "todo": + errors.append("{}: todo".format(where)) + return status + if status in ("excluded", "gap", "unsupported") and not entry.get("reason"): + errors.append("{}: {} without a reason".format(where, status)) + if status == "covered" and "case" not in entry: + errors.append("{}: covered without a case".format(where)) + if "case" in entry: + test, sep, case = entry["case"].partition(":") + if not sep or not case: + errors.append("{}: case {!r} is not :".format( + where, entry["case"])) + return status + if test not in cache: + cache[test] = test_cases(test) + if cache[test] is None: + errors.append("{}: no test file tests/{}.cpp".format(where, test)) + elif case not in cache[test]: + errors.append("{}: tests/{}.cpp declares no test case {!r}".format( + where, test, case)) + return status + + +def _cases_under(lib): + """{test case name: [test name]} over every source under tests//.""" + found = {} + base = TESTS / lib + if not base.is_dir(): + return found + for path in sorted(base.rglob("*.cpp")): + test = path.relative_to(TESTS).with_suffix("").as_posix() + for case in test_cases(test) or (): + found.setdefault(case, []).append(test) + return found + + +def cmd_link(args): + libs = args.libs or LIBRARIES + for lib in libs: + data = load(lib) + if data is None: + continue + cases = _cases_under(lib) + sids = {} + for e in data.get("snippet", []): + sid = e["doc"].partition("#")[2] + sids[sid] = sids.get(sid, 0) + 1 + linked, todo = 0, [] + for table, key in (("example", "path"), ("snippet", "doc")): + for e in data.get(table, []): + if e.get("status", "todo") != "todo" or "case" in e: + continue + if table == "example": + stem = Path(e["path"]).stem + wanted = ["example: " + stem] + else: + doc, _, sid = e["doc"].partition("#") + wanted = ["doc: {}#{}".format(Path(doc).stem, sid)] + if sids[sid] == 1: + wanted.append("doc: " + sid) + hit = next((w for w in wanted if w in cases), None) + if hit is None: + todo.append(e[key]) + continue + e.pop("status", None) + e["case"] = "{}:{}".format(cases[hit][0], hit) + linked += 1 + if linked: + dump(lib, data) + print("{:16} linked {:4} todo {:4}".format(lib, linked, len(todo))) + if args.verbose: + for t in todo: + print(" todo " + t) + + +def cmd_check(_args): + errors, cache, totals = [], {}, {} + for lib in LIBRARIES: + data = load(lib) + if data is None: + errors.append("tests/{}/coverage.toml is missing".format(lib)) + continue + if data.get("library") != lib: + errors.append("tests/{}/coverage.toml: library = {!r}".format( + lib, data.get("library"))) + counts = {} + for table, key in (("example", "path"), ("snippet", "doc")): + for entry in data.get(table, []): + st = check_entry(lib, entry, key, errors, cache) + counts[st] = counts.get(st, 0) + 1 + totals[lib] = counts + if lib in CUSTOM_TYPES: + base = TESTS / lib + hits = list(base.rglob("custom_types*.cpp")) if base.is_dir() else [] + if not hits: + errors.append("tests/{}/custom_types.cpp is missing (the " + "library has customization points)".format(lib)) + for lib, counts in totals.items(): + print("{:16} {}".format(lib, " ".join( + "{} {}".format(k, counts[k]) for k in sorted(counts)))) + if errors: + print("\n{} problem(s):".format(len(errors)), file=sys.stderr) + for e in errors: + print(" " + e, file=sys.stderr) + return 1 + print("coverage OK") + return 0 + + +def main(argv=None): + ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) + sub = ap.add_subparsers(dest="cmd", required=True) + s = sub.add_parser("scan", help="refresh the inventories from the archive") + s.add_argument("--archive", required=True) + s.add_argument("--libs", nargs="*") + s.set_defaults(func=cmd_scan) + k = sub.add_parser("link", help="link todo entries to conventionally named cases") + k.add_argument("--libs", nargs="*") + k.add_argument("-v", "--verbose", action="store_true") + k.set_defaults(func=cmd_link) + c = sub.add_parser("check", help="check the committed inventories") + c.set_defaults(func=cmd_check) + args = ap.parse_args(argv) + return args.func(args) or 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/gen_features.py b/tools/gen_features.py index 32fb187a..3303b5f7 100644 --- a/tools/gen_features.py +++ b/tools/gen_features.py @@ -30,7 +30,7 @@ deliberately NOT put into the base set. In test mode the base compiles unconditionally and every TU links into every test program (no archive pull-on-demand), so the framework TUs and the official header-only aggregate -(, tests/test_included.cpp) would double-define the +(, tests/test/included/smoke.cpp) would double-define the framework symbols in one program (M11 §3 constraint: the two consumption forms must never link together). Feature-only sources compile only when the feature is active — in build AND test mode (M8 §1.1) — which makes the two @@ -220,7 +220,7 @@ def module_name(lib): # links every TU into each test program, so they collide there (no archive # pull-on-demand in that mode). Consumers own main + the unit_test_main # runner (the runner comes from #including impl/unit_test_main.ipp with - # BOOST_TEST_NO_MAIN, cf. tests/test_utf.cpp); all framework services are + # BOOST_TEST_NO_MAIN, cf. tests/test/utf/smoke.cpp); all framework services are # linked from the remaining TUs. C1: these TUs ship under the module-less # unit_test_framework feature (FEATURE_ONLY_SOURCES — kept out of the # base set so the included/* aggregate test can link without them). @@ -276,7 +276,7 @@ def module_name(lib): # the base [build].sources union. In test mode the base compiles # unconditionally and every TU links into every test program, so the # unit_test_framework TUs and the official header-only aggregate -# (, consumed by tests/test_included.cpp) would +# (, consumed by tests/test/included/smoke.cpp) would # double-define the framework symbols when both land in one program (M11 §3). # Feature-only sources compile only when the feature is active (M8 §1.1), # making the two test consumption forms mutually exclusive. log's TUs stay in From e0762d4da3fb09d36f4901e04f0e843057f52d8a Mon Sep 17 00:00:00 2001 From: Zhe Feng Date: Mon, 9 Nov 2026 14:07:50 +0000 Subject: [PATCH 4/9] convert: export the converters and throw_on_failure; tests for uuid, container_hash, convert The converter headers (stream, lexical_cast, strtol, printf, charconv) are not self-contained, so the standalone-compile filter had dropped them and the convert module exported convert() without a converter. They now follow in an explicit GMF ([convert] in export_policy.toml); spirit.hpp stays header-only (it would make Spirit's whole surface convert's). Four boost::parameter internals that graph and heap exported move to convert, which now reaches Boost.Parameter first. patches/convert.patch makes boost::throw_on_failure inline constexpr: as a namespace-scope const object it had internal linkage and could not be exported. New suites: uuid (every documented component), container_hash (official examples; described classes through the module and as headers), convert (examples grouped by the default converter they define, user converters). --- generated/exports/convert.deps | 5 + generated/exports/convert.inc | 149 +++++++- generated/exports/graph.inc | 3 +- generated/exports/heap.inc | 8 +- generated/meta/api-surface.json | 20 +- generated/meta/libs.json | 7 +- generated/meta/skipped.txt | 81 ---- mcpp.toml | 2 +- patches/README.md | 4 + patches/convert.patch | 16 + src/convert.cppm | 13 +- tests/container_hash/coverage.toml | 32 +- tests/container_hash/examples.cpp | 135 +++++++ tests/container_hash/include_describe.cpp | 52 +++ tests/container_hash/mixed_describe.cpp | 28 ++ tests/convert/coverage.toml | 60 +-- tests/convert/custom_types.cpp | 107 ++++++ tests/convert/examples_default_converter.cpp | 19 + tests/convert/examples_lexical_cast.cpp | 84 ++++ tests/convert/examples_stream.cpp | 189 +++++++++ tests/convert/include_spirit.cpp | 52 +++ tests/uuid/api.cpp | 382 +++++++++++++++++++ tests/uuid/coverage.toml | 258 ++++++------- tests/uuid/custom_types.cpp | 103 +++++ tools/apply_patches.py | 5 +- tools/export_policy.toml | 18 + upstream/boost/boost/convert.hpp | 5 +- 27 files changed, 1561 insertions(+), 276 deletions(-) create mode 100644 patches/convert.patch create mode 100644 tests/container_hash/examples.cpp create mode 100644 tests/container_hash/include_describe.cpp create mode 100644 tests/container_hash/mixed_describe.cpp create mode 100644 tests/convert/custom_types.cpp create mode 100644 tests/convert/examples_default_converter.cpp create mode 100644 tests/convert/examples_lexical_cast.cpp create mode 100644 tests/convert/examples_stream.cpp create mode 100644 tests/convert/include_spirit.cpp create mode 100644 tests/uuid/api.cpp create mode 100644 tests/uuid/custom_types.cpp diff --git a/generated/exports/convert.deps b/generated/exports/convert.deps index 482487c6..d44c3843 100644 --- a/generated/exports/convert.deps +++ b/generated/exports/convert.deps @@ -1,4 +1,9 @@ mcpplibs.mwrap.boost.assert +mcpplibs.mwrap.boost.config mcpplibs.mwrap.boost.core +mcpplibs.mwrap.boost.lexical_cast +mcpplibs.mwrap.boost.math +mcpplibs.mwrap.boost.mp11 mcpplibs.mwrap.boost.optional mcpplibs.mwrap.boost.type_traits +mcpplibs.mwrap.boost.utility diff --git a/generated/exports/convert.inc b/generated/exports/convert.inc index 4b46443e..6e0feabd 100644 --- a/generated/exports/convert.inc +++ b/generated/exports/convert.inc @@ -1,25 +1,78 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=convert boost=1.92.0 target=x86_64-w64-mingw32 entities=12 +// lib=convert boost=1.92.0 target=x86_64-w64-mingw32 entities=144 export namespace boost { using boost::convert; + using boost::make_default; + using boost::throw_on_failure; } export namespace boost { namespace cnv { + using boost::cnv::adjust; using boost::cnv::apply; + using boost::cnv::base; + using boost::cnv::basic_stream; using boost::cnv::by_default; using boost::cnv::char_cptr; + using boost::cnv::char_type; + using boost::cnv::charconv; using boost::cnv::check_functor; + using boost::cnv::cnvbase; + using boost::cnv::cstream; + using boost::cnv::is_char; using boost::cnv::is_fun; + using boost::cnv::is_range; + using boost::cnv::is_space; + using boost::cnv::is_string; + using boost::cnv::iterator; + using boost::cnv::lexical_cast; using boost::cnv::no_type; + using boost::cnv::notation; + using boost::cnv::printf; + using boost::cnv::range; + using boost::cnv::range_base; using boost::cnv::reference; + using boost::cnv::strtol; + using boost::cnv::to_upper; + using boost::cnv::uchar_type; + using boost::cnv::wchar_type; + using boost::cnv::wstream; using boost::cnv::yes_type; }} export namespace boost { namespace cnv { namespace detail { using boost::cnv::detail::delayed_resolution; + using boost::cnv::detail::is_char; + using boost::cnv::detail::is_range; + using boost::cnv::detail::is_string; }}} +export namespace boost { namespace cnv { namespace parameter { + using boost::cnv::parameter::adjust; + using boost::cnv::parameter::base; + using boost::cnv::parameter::fill; + using boost::cnv::parameter::locale; + using boost::cnv::parameter::lowercase; + using boost::cnv::parameter::notation; + using boost::cnv::parameter::precision; + using boost::cnv::parameter::skipws; + using boost::cnv::parameter::uppercase; + using boost::cnv::parameter::width; +}}} + +export namespace boost { namespace cnv { namespace parameter { namespace type { + using boost::cnv::parameter::type::adjust; + using boost::cnv::parameter::type::base; + using boost::cnv::parameter::type::fill; + using boost::cnv::parameter::type::locale; + using boost::cnv::parameter::type::lowercase; + using boost::cnv::parameter::type::notation; + using boost::cnv::parameter::type::precision; + using boost::cnv::parameter::type::skipws; + using boost::cnv::parameter::type::uppercase; + using boost::cnv::parameter::type::width; +}}}} + export namespace boost { namespace detail { using boost::detail::throw_on_failure; }} @@ -27,3 +80,97 @@ export namespace boost { namespace detail { export namespace boost { namespace function_types { using boost::function_types::function_arity; }} + +export namespace boost { namespace mpl { + using boost::mpl::count; + using boost::mpl::count_impl; + using boost::mpl::key_type; + using boost::mpl::key_type_impl; + using boost::mpl::order; + using boost::mpl::order_impl; + using boost::mpl::value_type; + using boost::mpl::value_type_impl; +}} + +export namespace boost { namespace parameter { + using boost::parameter::binding; + using boost::parameter::binding0; + using boost::parameter::binding1; + using boost::parameter::consume_reference; + using boost::parameter::forward_reference; + using boost::parameter::in_out_reference; + using boost::parameter::in_reference; + using boost::parameter::is_argument_pack; + using boost::parameter::keyword; + using boost::parameter::lazy_binding; + using boost::parameter::lazy_value_type; + using boost::parameter::move_from_reference; + using boost::parameter::out_reference; + using boost::parameter::template_keyword; + using boost::parameter::value_type; + using boost::parameter::value_type0; + using boost::parameter::value_type1; + using boost::parameter::void_; +}} + +export namespace boost { namespace parameter { namespace aux { + using boost::parameter::aux::arg_list; + using boost::parameter::aux::arg_list_cons; + using boost::parameter::aux::arg_list_iterator; + using boost::parameter::aux::arg_list_tag; + using boost::parameter::aux::augment_predicate; + using boost::parameter::aux::augment_predicate_check_consume_ref; + using boost::parameter::aux::augment_predicate_check_consume_ref_mp11; + using boost::parameter::aux::augment_predicate_check_out_ref; + using boost::parameter::aux::augment_predicate_check_out_ref_mp11; + using boost::parameter::aux::augment_predicate_mp11; + using boost::parameter::aux::augment_predicate_mp11_impl; + using boost::parameter::aux::default_; + using boost::parameter::aux::default_r_; + using boost::parameter::aux::empty_arg_list; + using boost::parameter::aux::error_const_lvalue_bound_to_out_parameter; + using boost::parameter::aux::error_lvalue_bound_to_consume_parameter; + using boost::parameter::aux::error_rvalue_bound_to_out_parameter; + using boost::parameter::aux::flat_like_arg_list; + using boost::parameter::aux::flat_like_arg_tuple; + using boost::parameter::aux::get_reference; + using boost::parameter::aux::has_nested_template_fn; + using boost::parameter::aux::has_nested_template_fn_impl; + using boost::parameter::aux::has_nested_template_fn_variadic; + using boost::parameter::aux::is_cv_reference_wrapper; + using boost::parameter::aux::is_cv_reference_wrapper_check; + using boost::parameter::aux::is_maybe; + using boost::parameter::aux::is_mp11_placeholder; + using boost::parameter::aux::is_mpl_placeholder; + using boost::parameter::aux::is_name_tag; + using boost::parameter::aux::is_tagged_argument; + using boost::parameter::aux::is_tagged_argument_mp11; + using boost::parameter::aux::is_template_keyword; + using boost::parameter::aux::lambda_tag; + using boost::parameter::aux::lazy_default; + using boost::parameter::aux::maybe_base; + using boost::parameter::aux::name_tag; + using boost::parameter::aux::name_tag_base; + using boost::parameter::aux::no_tag; + using boost::parameter::aux::parameter_requirements; + using boost::parameter::aux::result_of0; + using boost::parameter::aux::tag; + using boost::parameter::aux::tag_if_lvalue_reference; + using boost::parameter::aux::tag_if_otherwise; + using boost::parameter::aux::tag_if_scalar; + using boost::parameter::aux::tagged_argument; + using boost::parameter::aux::tagged_argument_base; + using boost::parameter::aux::tagged_argument_list_of_1; + using boost::parameter::aux::tagged_argument_rref; + using boost::parameter::aux::tagged_argument_rref_key; + using boost::parameter::aux::tagged_argument_type; + using boost::parameter::aux::template_keyword_base; + using boost::parameter::aux::to_yesno; + using boost::parameter::aux::unwrap_cv_reference; + using boost::parameter::aux::unwrap_cv_reference_impl; + using boost::parameter::aux::use_default_tag; + using boost::parameter::aux::value_type_is_not_void; + using boost::parameter::aux::value_type_is_void; + using boost::parameter::aux::void_reference; + using boost::parameter::aux::yes_tag; +}}} diff --git a/generated/exports/graph.inc b/generated/exports/graph.inc index 6c8733fc..f7c37125 100644 --- a/generated/exports/graph.inc +++ b/generated/exports/graph.inc @@ -1,5 +1,5 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=graph boost=1.92.0 target=x86_64-w64-mingw32 entities=3070 +// lib=graph boost=1.92.0 target=x86_64-w64-mingw32 entities=3069 export namespace boost { using boost::AStarHeuristicConcept; @@ -1942,7 +1942,6 @@ export namespace boost { namespace louvain_detail { export namespace boost { namespace parameter { using boost::parameter::are_tagged_arguments; using boost::parameter::compose; - using boost::parameter::is_argument_pack; }} export namespace boost { namespace parameter { namespace aux { diff --git a/generated/exports/heap.inc b/generated/exports/heap.inc index fabba87d..d84697a1 100644 --- a/generated/exports/heap.inc +++ b/generated/exports/heap.inc @@ -1,5 +1,5 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=heap boost=1.92.0 target=x86_64-w64-mingw32 entities=92 +// lib=heap boost=1.92.0 target=x86_64-w64-mingw32 entities=89 export namespace boost { namespace heap { using boost::heap::MergablePriorityQueue; @@ -98,9 +98,3 @@ export namespace boost { namespace heap { namespace tag { using boost::heap::tag::stable; using boost::heap::tag::store_parent_pointer; }}} - -export namespace boost { namespace parameter { namespace aux { - using boost::parameter::aux::is_name_tag; - using boost::parameter::aux::name_tag; - using boost::parameter::aux::name_tag_base; -}}} diff --git a/generated/meta/api-surface.json b/generated/meta/api-surface.json index 17991c74..dc405a5b 100644 --- a/generated/meta/api-surface.json +++ b/generated/meta/api-surface.json @@ -525,13 +525,23 @@ "convert": { "deps": [ "assert", + "config", "core", + "lexical_cast", + "math", + "mp11", "optional", - "type_traits" + "type_traits", + "utility" ], - "exported": 12, + "exported": 144, "gmf": [ - "boost/convert.hpp" + "boost/convert.hpp", + "boost/convert/charconv.hpp", + "boost/convert/lexical_cast.hpp", + "boost/convert/printf.hpp", + "boost/convert/stream.hpp", + "boost/convert/strtol.hpp" ] }, "core": { @@ -938,7 +948,7 @@ "utility", "version" ], - "exported": 3070, + "exported": 3069, "gmf": [ "boost/graph/adj_list_serialize.hpp", "boost/graph/adjacency_list_io.hpp", @@ -1084,7 +1094,7 @@ "mp11", "type_traits" ], - "exported": 92, + "exported": 89, "gmf": [ "boost/heap/binomial_heap.hpp", "boost/heap/d_ary_heap.hpp", diff --git a/generated/meta/libs.json b/generated/meta/libs.json index 5a4500b4..14078751 100644 --- a/generated/meta/libs.json +++ b/generated/meta/libs.json @@ -961,7 +961,12 @@ "boost/polymorphic_pointer_cast.hpp" ], "convert": [ - "boost/convert.hpp" + "boost/convert.hpp", + "boost/convert/charconv.hpp", + "boost/convert/lexical_cast.hpp", + "boost/convert/printf.hpp", + "boost/convert/stream.hpp", + "boost/convert/strtol.hpp" ], "core": [ "boost/core/addressof.hpp", diff --git a/generated/meta/skipped.txt b/generated/meta/skipped.txt index 0d654e40..f1166396 100644 --- a/generated/meta/skipped.txt +++ b/generated/meta/skipped.txt @@ -137,72 +137,31 @@ flyweight drop boost::detail::allocator::partial_std_allocator_wrapper -- The bu flyweight drop boost::detail::allocator::rebind_to -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::detail::allocator::rebinder -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::detail::if_true -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::mpl::count -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::mpl::count_impl -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::mpl::key_type -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::mpl::key_type_impl -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::mpl::order -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::mpl::order_impl -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::mpl::value_type -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::mpl::value_type_impl -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::always_true_predicate -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::append_to_make_arg_list -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::arg_list -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::arg_list_cons -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::arg_list_factory -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::arg_list_iterator -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::arg_list_tag -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::as_parameter_requirements -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::augment_predicate -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::augment_predicate_check_consume_ref -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::augment_predicate_check_consume_ref_mp11 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::augment_predicate_check_out_ref -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::augment_predicate_check_out_ref_mp11 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::augment_predicate_mp11 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::augment_predicate_mp11_impl -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::deduce_tag -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::deduce_tag0 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::deduce_tag_condition -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::deduce_tag_condition_mp11 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::deduce_tag_condition_mpl -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::deduced_item -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::default_ -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::default_r_ -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::empty_arg_list -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::error_const_lvalue_bound_to_out_parameter -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::error_lvalue_bound_to_consume_parameter -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::error_rvalue_bound_to_out_parameter -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::flat_like_arg_list -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::flat_like_arg_tuple -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::get_predicate -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::get_predicate_or_default -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::get_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::get_tag_type -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::get_tag_type0 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::has_default -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::has_key_ -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::has_nested_template_fn -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::has_nested_template_fn_impl -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::has_nested_template_fn_variadic -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::insert_ -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::insert_tagged -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::is_cv_reference_wrapper -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::is_cv_reference_wrapper_check -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::is_deduced -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::is_deduced0 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::is_deduced_aux -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::is_maybe -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::is_mp11_placeholder -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::is_mpl_placeholder -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::is_named_argument -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::is_optional -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::is_required -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::is_tagged_argument -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::is_tagged_argument_mp11 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::is_template_keyword -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::item -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::lambda_tag -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::lazy_default -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::make_arg_list -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::make_arg_list0 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::make_arg_list_aux -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. @@ -216,60 +175,20 @@ flyweight drop boost::parameter::aux::make_parameter_spec_items -- The bundle pu flyweight drop boost::parameter::aux::make_parameter_spec_items_helper -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::match_parameters_base_cond -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::match_parameters_base_cond_helper -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::maybe_base -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::no_tag -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::parameter_requirements -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::predicate -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::result_of0 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::satisfies -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::satisfies_requirements_of -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::set0 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tag -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::tag_deduced -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tag_if_lvalue_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tag_if_otherwise -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tag_if_scalar -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::tag_keyword_arg -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::tag_template_keyword_arg -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::tag_type -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tagged_argument -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tagged_argument_base -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tagged_argument_list_of_1 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tagged_argument_rref -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tagged_argument_rref_key -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::tagged_argument_type -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::template_keyword_base -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::to_yesno -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::unmatched_argument -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::unwrap_cv_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::unwrap_cv_reference_impl -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::aux::use_default -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::use_default_tag -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::value_type_is_not_void -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::value_type_is_void -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::void_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::aux::yes_tag -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::binding -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::binding0 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::binding1 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::consume_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::deduced -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::forward_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::in_out_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::in_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::keyword -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::lazy_binding -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::lazy_value_type -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::move_from_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::optional -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::out_reference -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::parameters -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. flyweight drop boost::parameter::required -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::template_keyword -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::value_type -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::value_type0 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::value_type1 -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. -flyweight drop boost::parameter::void_ -- The bundle pulls the transitive container/interprocess/parameter surface into shared namespaces; boost.flyweight exports its own namespaces and boost:: entry points only. functional guard boost::mpl::v_at_impl if defined(__GNUC__) -- M9 platform guard: gcc-preprocessed mpl vector headers only. functional guard boost::mpl::v_item if defined(__GNUC__) -- M9 platform guard: gcc-preprocessed mpl vector headers only. functional guard boost::mpl::v_mask if defined(__GNUC__) -- M9 platform guard: gcc-preprocessed mpl vector headers only. diff --git a/mcpp.toml b/mcpp.toml index 52204ab6..2cf3c8fd 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -424,7 +424,7 @@ all-modules = { implies = ["optional", "variant", "variant2", "any", "core", "co implies = ["assert", "smart-ptr", "throw-exception"] [features.convert] sources = ["src/convert.cppm"] - implies = ["assert", "core", "optional", "type-traits"] + implies = ["assert", "config", "core", "lexical-cast", "math", "mp11", "optional", "type-traits", "utility"] [features.crc] sources = ["src/crc.cppm"] [features.decimal] diff --git a/patches/README.md b/patches/README.md index 4189cafa..5a74ae75 100644 --- a/patches/README.md +++ b/patches/README.md @@ -34,6 +34,10 @@ second one. The algorithm module's GMF includes them in that order, and `boost::algorithm::transform_reduce` was missing from its export list; plain `#include` consumers hit the same bug. The patch gives the header its own guard. +`convert.patch` is the TU-local-object case: `boost::throw_on_failure` was a +namespace-scope `const` object (internal linkage), which a module cannot +export; it becomes `inline constexpr`. + Every patch is reviewed against the 1.92.0 tag it was written for; `upstream/boost/SOURCE.md` records the archive and checksum the tree comes from. A patch that neither applies nor is already applied fails the replay. diff --git a/patches/convert.patch b/patches/convert.patch new file mode 100644 index 00000000..37830c4a --- /dev/null +++ b/patches/convert.patch @@ -0,0 +1,16 @@ +diff --git a/upstream/boost/boost/convert.hpp b/upstream/boost/boost/convert.hpp +index b022ccb7..a7d988dd 100644 +--- a/upstream/boost/boost/convert.hpp ++++ b/upstream/boost/boost/convert.hpp +@@ -34,7 +34,10 @@ namespace boost + + /// @details boost::throw_on_failure is the 'tag' object + /// to request the exception-throwing behavior. +- detail::throw_on_failure const throw_on_failure = detail::throw_on_failure(0); ++ // boost-m vendored edit: `const` gave the tag internal linkage, so the ++ // module could not export it (TU-local); `inline constexpr` has external ++ // linkage and the same value. Replay after re-running import_boost. ++ inline constexpr detail::throw_on_failure throw_on_failure = detail::throw_on_failure(0); + + namespace cnv + { diff --git a/src/convert.cppm b/src/convert.cppm index 950a625b..e55c813d 100644 --- a/src/convert.cppm +++ b/src/convert.cppm @@ -1,14 +1,25 @@ // mcpplibs.mwrap.boost.convert — module facade over generated/exports/convert.inc // (hand-maintained; tools/gen_exports.py --check verifies its GMF include set) +// The converters follow : they are not self-contained +// (tools/export_policy.toml [convert]). module; #include +#include +#include +#include +#include +#include export module mcpplibs.mwrap.boost.convert; export import mcpplibs.mwrap.boost.assert; +export import mcpplibs.mwrap.boost.config; export import mcpplibs.mwrap.boost.core; +export import mcpplibs.mwrap.boost.lexical_cast; +export import mcpplibs.mwrap.boost.math; +export import mcpplibs.mwrap.boost.mp11; export import mcpplibs.mwrap.boost.optional; export import mcpplibs.mwrap.boost.type_traits; +export import mcpplibs.mwrap.boost.utility; #include "../generated/exports/convert.inc" - diff --git a/tests/container_hash/coverage.toml b/tests/container_hash/coverage.toml index 70be4c71..9af81cfb 100644 --- a/tests/container_hash/coverage.toml +++ b/tests/container_hash/coverage.toml @@ -6,31 +6,31 @@ upstream = "1.92.0" library = "container_hash" [[example]] -path = "libs/container_hash/examples/books.cpp" -status = "todo" +path = "libs/container_hash/examples/books.cpp" +case = "container_hash/examples:example: books" [[example]] -path = "libs/container_hash/examples/point.cpp" -status = "todo" +path = "libs/container_hash/examples/point.cpp" +case = "container_hash/examples:example: point" [[example]] -path = "libs/container_hash/examples/point2.cpp" -status = "todo" +path = "libs/container_hash/examples/point2.cpp" +case = "container_hash/include_describe:example: point2" [[example]] -path = "libs/container_hash/examples/portable.cpp" -status = "todo" +path = "libs/container_hash/examples/portable.cpp" +case = "container_hash/examples:example: portable" [[example]] -path = "libs/container_hash/examples/template.cpp" -status = "todo" +path = "libs/container_hash/examples/template.cpp" +case = "container_hash/examples:example: template" [[snippet]] -doc = "libs/container_hash/doc/hash/notes.adoc#hash-combine" -title = "hash_combine" -status = "todo" +doc = "libs/container_hash/doc/hash/notes.adoc#hash-combine" +title = "hash_combine" +case = "container_hash/examples:doc: hash-combine" [[snippet]] -doc = "libs/container_hash/doc/hash/notes.adoc#hash-range" -title = "hash_range" -status = "todo" +doc = "libs/container_hash/doc/hash/notes.adoc#hash-range" +title = "hash_range" +case = "container_hash/examples:doc: hash-range" diff --git a/tests/container_hash/examples.cpp b/tests/container_hash/examples.cpp new file mode 100644 index 00000000..866f5c03 --- /dev/null +++ b/tests/container_hash/examples.cpp @@ -0,0 +1,135 @@ +// mcpplibs.mwrap.boost.container_hash — the official examples +// (libs/container_hash/examples/*.cpp) as test cases. point2.cpp, which +// describes its class with BOOST_DESCRIBE_CLASS, is include_describe.cpp. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.container_hash; +import mcpplibs.mwrap.boost.unordered; + +namespace library { +struct book { + int id; + std::string author; + std::string title; + book(int i, std::string const& a, std::string const& t) : id(i), author(a), title(t) {} +}; +bool operator==(book const& a, book const& b) { return a.id == b.id; } +std::size_t hash_value(book const& b) { + boost::hash hasher; + return hasher(b.id); +} +} // namespace library + +TEST_CASE("example: books") { + library::book knife(3458, "Zane Grey", "The Hash Knife Outfit"); + library::book dandelion(1354, "Paul J. Shanley", "Hash & Dandelion Greens"); + boost::hash book_hasher; + CHECK(book_hasher(knife) == boost::hash()(3458)); + std::unordered_set> books; + books.insert(knife); + books.insert(library::book(2443, "Lindgren, Torgny", "Hash")); + books.insert(library::book(1953, "Snyder, Bernadette M.", + "Heavenly Hash: A Tasty Mix of a Mother's Meditations")); + CHECK(books.find(knife) != books.end()); + CHECK(books.find(dandelion) == books.end()); +} + +namespace ex_point { +class point { + int x; + int y; +public: + point() : x(0), y(0) {} + point(int x, int y) : x(x), y(y) {} + bool operator==(point const& other) const { return x == other.x && y == other.y; } + friend std::size_t hash_value(point const& p) { + std::size_t seed = 0; + boost::hash_combine(seed, p.x); + boost::hash_combine(seed, p.y); + return seed; + } +}; +} // namespace ex_point + +TEST_CASE("example: point") { + using ex_point::point; + boost::hash point_hasher; + point p1(0, 0), p2(1, 2), p3(4, 1), p4 = p1; + CHECK(point_hasher(p1) == point_hasher(p4)); + CHECK(point_hasher(p1) != point_hasher(p2)); + CHECK(point_hasher(p1) != point_hasher(p3)); +} + +namespace foo { +template +class custom_type { + T value; +public: + custom_type(T x) : value(x) {} + std::size_t hash() const { + boost::hash hasher; + return hasher(value); + } +}; +template +std::size_t hash_value(foo::custom_type x) { + return x.hash(); +} +} // namespace foo + +TEST_CASE("example: portable") { + foo::custom_type x(1), y(2), z(1); + boost::hash> hasher; + CHECK(hasher(x) == hasher(x)); + CHECK(hasher(x) != hasher(y)); + CHECK(hasher(x) == hasher(z)); +} + +template +class my_pair { + A value1; + B value2; +public: + my_pair(A const& v1, B const& v2) : value1(v1), value2(v2) {} + bool operator==(my_pair const& other) const { return value1 == other.value1 && value2 == other.value2; } + friend std::size_t hash_value(my_pair const& p) { + std::size_t seed = 0; + boost::hash_combine(seed, p.value1); + boost::hash_combine(seed, p.value2); + return seed; + } +}; + +TEST_CASE("example: template") { + using pair = my_pair; + boost::unordered_set pair_set; + pair_set.emplace(10, 0.5f); + CHECK(pair_set.find(pair(10, 0.5f)) != pair_set.end()); + CHECK(pair_set.find(pair(10, 0.6f)) == pair_set.end()); +} + +TEST_CASE("doc: hash-combine") { + // Order matters, and combining is equivalent to hashing the sequence. + std::size_t a = 0, b = 0; + boost::hash_combine(a, 1); + boost::hash_combine(a, 2); + boost::hash_combine(b, 2); + boost::hash_combine(b, 1); + CHECK(a != b); + std::vector v{1, 2}; + CHECK(boost::hash_value(v) == boost::hash_range(v.begin(), v.end())); +} + +TEST_CASE("doc: hash-range") { + std::vector v{"a", "b", "c"}; + std::size_t seed = 0; + boost::hash_range(seed, v.begin(), v.end()); + CHECK(seed == boost::hash_range(v.begin(), v.end())); + std::size_t s2 = 0; + for (auto& x : v) boost::hash_combine(s2, x); + CHECK(s2 == seed); + boost::hash_unordered_range(seed, v.begin(), v.end()); + std::vector r{"c", "a", "b"}; + CHECK(boost::hash_unordered_range(v.begin(), v.end()) == boost::hash_unordered_range(r.begin(), r.end())); +} diff --git a/tests/container_hash/include_describe.cpp b/tests/container_hash/include_describe.cpp new file mode 100644 index 00000000..2344a2ab --- /dev/null +++ b/tests/container_hash/include_describe.cpp @@ -0,0 +1,52 @@ +// boost.container_hash + boost.describe — pure include. describe has no +// module (its API is BOOST_DESCRIBE_*, docs/usage.md §2.3), so a described +// class is hashed in a TU that includes container_hash too. Covers +// libs/container_hash/examples/point2.cpp; mixed_describe.cpp checks the same +// with the container_hash module imported. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include +#include +#include + +namespace ex_point2 { +class point { + int x; + int y; + BOOST_DESCRIBE_CLASS(point, (), (), (), (x, y)) +public: + point() : x(0), y(0) {} + point(int x, int y) : x(x), y(y) {} +}; +using boost::describe::operators::operator==; +using boost::describe::operators::operator!=; + +struct base { + int b = 1; +}; +BOOST_DESCRIBE_STRUCT(base, (), (b)) +struct derived : base { + std::string s = "x"; +}; +BOOST_DESCRIBE_STRUCT(derived, (base), (s)) +} // namespace ex_point2 + +TEST_CASE("example: point2") { + using ex_point2::point; + boost::hash point_hasher; + point p1(0, 0), p2(1, 2), p3(4, 1), p4 = p1; + CHECK(point_hasher(p1) == point_hasher(p4)); + CHECK(point_hasher(p1) != point_hasher(p2)); + CHECK(point_hasher(p1) != point_hasher(p3)); + CHECK(p1 == p4); + CHECK(p1 != p2); +} + +TEST_CASE("described bases are hashed too") { + ex_point2::derived a, b; + b.b = 2; + CHECK(boost::hash()(a) != boost::hash()(b)); + static_assert(boost::container_hash::is_described_class::value); +} diff --git a/tests/container_hash/mixed_describe.cpp b/tests/container_hash/mixed_describe.cpp new file mode 100644 index 00000000..9c82622e --- /dev/null +++ b/tests/container_hash/mixed_describe.cpp @@ -0,0 +1,28 @@ +// boost.container_hash module + boost.describe headers in one TU: the +// BOOST_DESCRIBE_* macros come from #include (no module can carry them), the +// hashing from `import mcpplibs.mwrap.boost.container_hash;`, which exports +// boost::container_hash::is_described_class. This is the include+import mix +// of design §4.4; the includes precede every import. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +import std; +import mcpplibs.mwrap.boost.container_hash; + +namespace mixed { +struct point { + int x = 0, y = 0; +}; +BOOST_DESCRIBE_STRUCT(point, (), (x, y)) +} // namespace mixed + +TEST_CASE("described struct hashed through the module") { + mixed::point a{1, 2}, b{1, 2}, c{2, 1}; + boost::hash h; + CHECK(h(a) == h(b)); + CHECK(h(a) != h(c)); + std::size_t seed = 0; + boost::hash_combine(seed, 1); + boost::hash_combine(seed, 2); + CHECK(h(a) == seed); // members combined in declaration order +} diff --git a/tests/convert/coverage.toml b/tests/convert/coverage.toml index 3c184afe..46cc3af0 100644 --- a/tests/convert/coverage.toml +++ b/tests/convert/coverage.toml @@ -6,59 +6,61 @@ upstream = "1.92.0" library = "convert" [[example]] -path = "libs/convert/example/algorithms.cpp" -status = "todo" +path = "libs/convert/example/algorithms.cpp" +case = "convert/examples_stream:example: algorithms" [[example]] -path = "libs/convert/example/default_converter.cpp" -status = "todo" +path = "libs/convert/example/default_converter.cpp" +case = "convert/examples_default_converter:example: default_converter" [[example]] path = "libs/convert/example/default_converter_fail.cpp" -status = "todo" +status = "excluded" +reason = "compile-failure demonstration: boost::convert without a defined boost::cnv::by_default must not compile" [[example]] -path = "libs/convert/example/getting_serious.cpp" -status = "todo" +path = "libs/convert/example/getting_serious.cpp" +case = "convert/examples_stream:example: getting_serious" [[example]] -path = "libs/convert/example/getting_started.cpp" -status = "todo" +path = "libs/convert/example/getting_started.cpp" +case = "convert/examples_lexical_cast:example: getting_started" [[example]] -path = "libs/convert/example/lexical_cast.cpp" -status = "todo" +path = "libs/convert/example/lexical_cast.cpp" +case = "convert/examples_lexical_cast:example: lexical_cast" [[example]] -path = "libs/convert/example/stream.cpp" -status = "todo" +path = "libs/convert/example/stream.cpp" +case = "convert/examples_stream:example: stream" [[snippet]] -doc = "libs/convert/doc/design_notes.qbk#converter_signature" -title = "Converter Signature" -status = "todo" +doc = "libs/convert/doc/design_notes.qbk#converter_signature" +title = "Converter Signature" +case = "convert/examples_stream:doc: converter_signature" [[snippet]] -doc = "libs/convert/doc/design_notes.qbk#requirements" -title = "Requirements" -status = "todo" +doc = "libs/convert/doc/design_notes.qbk#requirements" +title = "Requirements" +case = "convert/examples_stream:doc: requirements" [[snippet]] -doc = "libs/convert/doc/design_notes.qbk#user-interface-signature" -title = "User Interface Signature" -status = "todo" +doc = "libs/convert/doc/design_notes.qbk#user-interface-signature" +title = "User Interface Signature" +case = "convert/examples_stream:doc: user-interface-signature" [[snippet]] -doc = "libs/convert/doc/introduction.qbk#introduction" -title = "Introduction" -status = "todo" +doc = "libs/convert/doc/introduction.qbk#introduction" +title = "Introduction" +case = "convert/examples_lexical_cast:doc: introduction" [[snippet]] doc = "libs/convert/doc/performance.qbk#boost-convert-overhead" title = "Boost.Convert Overhead" -status = "todo" +status = "excluded" +reason = "benchmark (timing of convert() against direct converter calls)" [[snippet]] -doc = "libs/convert/doc/stream_converter.qbk#the-default-constructible-type-requirement" -title = "The ['Default Constructible] Type Requirement" -status = "todo" +doc = "libs/convert/doc/stream_converter.qbk#the-default-constructible-type-requirement" +title = "The ['Default Constructible] Type Requirement" +case = "convert/include_spirit:doc: the-default-constructible-type-requirement" diff --git a/tests/convert/custom_types.cpp b/tests/convert/custom_types.cpp new file mode 100644 index 00000000..6502087a --- /dev/null +++ b/tests/convert/custom_types.cpp @@ -0,0 +1,107 @@ +// mcpplibs.mwrap.boost.convert — user types and user converters: a user type +// converted through its stream operators (cstream) and through user +// conversion operators of the converter signature; user converters as a +// class, a plain function and a lambda (both documented signatures); a type +// without a default constructor through boost::make_default; boost::cnv::apply +// with a user converter in an algorithm. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.convert; + +namespace user { +struct rgb { + std::uint8_t r = 0, g = 0, b = 0; + bool operator==(const rgb&) const = default; +}; +std::ostream& operator<<(std::ostream& os, const rgb& c) { + return os << '#' << std::hex << std::setfill('0') << std::setw(2) << int(c.r) << std::setw(2) << int(c.g) + << std::setw(2) << int(c.b) << std::dec; +} +std::istream& operator>>(std::istream& is, rgb& c) { + std::string s; + is >> s; + if (s.size() != 7 || s[0] != '#') { + is.setstate(std::ios_base::failbit); + return is; + } + unsigned v = std::stoul(s.substr(1), nullptr, 16); + c = {std::uint8_t(v >> 16), std::uint8_t(v >> 8), std::uint8_t(v)}; + return is; +} + +// A converter class with the library's signature #2. +struct rgb_converter { + void operator()(std::string const& in, boost::optional& out) const { + if (in == "red") out = rgb{255, 0, 0}; + else if (in == "green") out = rgb{0, 255, 0}; + } + void operator()(rgb const& in, boost::optional& out) const { + if (in == rgb{255, 0, 0}) out = std::string("red"); + } +}; + +// A plain function converter. +void parse_bool(std::string const& in, boost::optional& out) { + if (in == "yes" || in == "true") out = true; + else if (in == "no" || in == "false") out = false; +} + +// No default constructor. +struct port { + explicit port(int n) : n(n) {} + int n; +}; +std::istream& operator>>(std::istream& is, port& p) { + int n = -1; + if (is >> n && n >= 0 && n < 65536) p = port(n); + else is.setstate(std::ios_base::failbit); + return is; +} +} // namespace user + +namespace boost { +template <> +inline user::port make_default() { + return user::port(0); +} +} // namespace boost + +TEST_CASE("a user type through its stream operators") { + boost::cnv::cstream cnv; + CHECK(boost::convert(user::rgb{1, 2, 255}, cnv).value() == "#0102ff"); + CHECK(boost::convert("#0a0b0c", cnv).value() == user::rgb{10, 11, 12}); + CHECK_FALSE(boost::convert("0a0b0c", cnv).has_value()); + boost::cnv::lexical_cast lc; + CHECK(boost::convert("#ffffff", lc).value() == user::rgb{255, 255, 255}); +} + +TEST_CASE("user converters: class, function, lambda") { + user::rgb_converter rc; + CHECK(boost::convert(std::string("red"), rc).value() == user::rgb{255, 0, 0}); + CHECK_FALSE(boost::convert(std::string("blue"), rc).has_value()); + CHECK(boost::convert(user::rgb{255, 0, 0}, rc).value() == "red"); + CHECK(boost::convert(std::string("yes"), &user::parse_bool).value()); + CHECK_FALSE(boost::convert(std::string("maybe"), &user::parse_bool).has_value()); + auto twice = [](int in, boost::optional& out) { out = in * 2; }; + CHECK(boost::convert(21, twice).value() == 42); +} + +TEST_CASE("a type without a default constructor") { + boost::cnv::cstream cnv; + CHECK(boost::convert("8080", cnv).value().n == 8080); + CHECK(boost::convert("70000", cnv, user::port(1)).n == 1); +} + +TEST_CASE("apply with a user converter in an algorithm") { + std::vector in{"true", "nope", "no"}; + std::vector out; + std::transform(in.begin(), in.end(), std::back_inserter(out), + boost::cnv::apply(&user::parse_bool).value_or(false)); + CHECK(out == std::vector{true, false, false}); + std::vector n; + std::vector nums{"1", "x", "3"}; + std::transform(nums.begin(), nums.end(), std::back_inserter(n), + boost::cnv::apply(boost::cnv::strtol()).value_or(0)); + CHECK(n == std::vector{1, 0, 3}); +} diff --git a/tests/convert/examples_default_converter.cpp b/tests/convert/examples_default_converter.cpp new file mode 100644 index 00000000..124f4706 --- /dev/null +++ b/tests/convert/examples_default_converter.cpp @@ -0,0 +1,19 @@ +// mcpplibs.mwrap.boost.convert — libs/convert/example/default_converter.cpp: +// a user-defined default converter (a cstream configured for uppercase hex). +// default_converter_fail.cpp is a compile-failure demonstration (no default +// converter defined) and is excluded in coverage.toml. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.convert; + +struct boost::cnv::by_default : boost::cnv::cstream { + by_default() { (*this)(std::uppercase)(std::hex); } +}; + +TEST_CASE("example: default_converter") { + int i = boost::convert("F").value_or(-1); + std::string s = boost::convert(255).value_or("bad"); + CHECK(i == 15); + CHECK(s == "FF"); +} diff --git a/tests/convert/examples_lexical_cast.cpp b/tests/convert/examples_lexical_cast.cpp new file mode 100644 index 00000000..434f2535 --- /dev/null +++ b/tests/convert/examples_lexical_cast.cpp @@ -0,0 +1,84 @@ +// mcpplibs.mwrap.boost.convert — the official examples whose default +// converter is boost::cnv::lexical_cast (libs/convert/example/ +// getting_started.cpp, lexical_cast.cpp). A program defines +// boost::cnv::by_default once, so the examples are grouped by the default +// they define: examples_stream.cpp and examples_default_converter.cpp hold +// the others. The spirit converter of getting_started is include_spirit.cpp. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.convert; + +using std::string; +using boost::convert; +using boost::lexical_cast; + +struct boost::cnv::by_default : boost::cnv::lexical_cast {}; + +namespace { +void log(char const*) {} +int fallback_fun(char const* msg, int fallback_value) { + log(msg); + return fallback_value; +} +} // namespace + +TEST_CASE("example: getting_started") { + // example1 + auto cnv = boost::cnv::lexical_cast(); + CHECK(lexical_cast("123") == 123); + CHECK(convert("123").value() == 123); + CHECK(convert("123", cnv).value() == 123); + CHECK(lexical_cast(123) == "123"); + CHECK(convert(123).value() == "123"); + CHECK(convert(123, cnv).value() == "123"); + // example2 + CHECK(convert("uhm", boost::cnv::lexical_cast()).value_or(-1) == -1); + // example3 (lexical_cast and strtol; spirit is include_spirit.cpp) + CHECK(convert("123", boost::cnv::lexical_cast()).value() == 123); + CHECK(convert("123", boost::cnv::strtol()).value() == 123); + // example4 + CHECK_THROWS(lexical_cast(" 123")); + auto ccnv = boost::cnv::cstream(); + CHECK(convert(" 123", ccnv(std::skipws)).value() == 123); + CHECK(lexical_cast(12.34567) == "12.34567"); + CHECK(convert(12.34567, ccnv(std::fixed)(std::setprecision(3))).value() == "12.346"); + CHECK(convert(12.34567, ccnv(std::scientific)(std::setprecision(3))).value() == "1.235e+01"); + // example5 + boost::cnv::cstream cs; + CHECK(convert("123", cs).value() == 123); + CHECK(convert("uhm", cs).value_or(-1) == -1); + // example6 / example7 + auto hex = boost::cnv::cstream(); + CHECK(convert(std::string("123"), hex(std::hex)).value_or(-1) == 291); + CHECK(convert(std::string("456"), hex(std::dec)).value_or(-1) == 456); + // example8 + int i1 = 12; + try { + i1 = lexical_cast(std::string("123")); + } catch (...) { + log("bad i1"); + } + CHECK(i1 == 123); + // example9 + std::string const s1 = "123", s2 = "nan"; + CHECK(convert(s1).value_or_eval(std::bind(fallback_fun, "bad i1", 11)) == 123); + CHECK(convert(s2).value_or_eval(std::bind(fallback_fun, "bad i2", 12)) == 12); +} + +TEST_CASE("example: lexical_cast") { + CHECK(lexical_cast("123") == 123); + CHECK(convert("123").value() == 123); + CHECK(convert("uhm").value_or(-1) == -1); + CHECK(lexical_cast(123) == "123"); + CHECK(convert(123).value() == "123"); + CHECK_THROWS_AS(convert("uhm").value(), boost::bad_optional_access); +} + +TEST_CASE("doc: introduction") { + // The doc's opening example: a lexical_cast-based default converter. + boost::optional r = convert("12"); + REQUIRE(r); + CHECK(*r == 12); + CHECK(convert("1.5").value() == 1.5); +} diff --git a/tests/convert/examples_stream.cpp b/tests/convert/examples_stream.cpp new file mode 100644 index 00000000..e5909ca5 --- /dev/null +++ b/tests/convert/examples_stream.cpp @@ -0,0 +1,189 @@ +// mcpplibs.mwrap.boost.convert — the official examples whose default +// converter is boost::cnv::cstream (libs/convert/example/getting_serious.cpp, +// stream.cpp) and the converter-agnostic algorithms.cpp, plus the design-note +// snippets of libs/convert/doc/design_notes.qbk. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.convert; + +using std::string; +using boost::convert; +using boost::lexical_cast; +using boost::optional; + +struct boost::cnv::by_default : boost::cnv::cstream {}; + +namespace cnv = boost::cnv; +namespace arg = boost::cnv::parameter; + +namespace { +int fallback_function() { return -1; } +struct fallback_func { + int operator()() const { return 42; } +}; + +// libs/convert/test/test.hpp: change, with stream operators. +struct change { + enum value_type { no, up, dn }; + change(value_type v = no) : value_(v) {} + bool operator==(change v) const { return value_ == v.value_; } + value_type value() const { return value_; } +private: + value_type value_; +}; +std::istream& operator>>(std::istream& stream, change& chg) { + std::string str; + stream >> str; + if (str == "up") chg = change::up; + else if (str == "dn") chg = change::dn; + else if (str == "no") chg = change::no; + else stream.setstate(std::ios_base::failbit); + return stream; +} +std::ostream& operator<<(std::ostream& stream, change const& chg) { + return stream << (chg == change::up ? "up" : chg == change::dn ? "dn" : "no"); +} +} // namespace + +TEST_CASE("example: getting_serious") { + boost::cnv::cstream cnv; + std::string const str = "123"; + int const fallback_value = -1; + // example1 + CHECK(convert("not an int", cnv).value_or(-1) == -1); + CHECK(lexical_cast(str) == 123); + CHECK(convert(str, cnv).value() == 123); + optional r1 = convert(str, cnv); + optional r2 = convert("x", cnv); + CHECK(r1.value() == 123); + CHECK_THROWS_AS(r2.value(), boost::bad_optional_access); + CHECK((r1 ? r1.value() : fallback_value) == 123); + CHECK(r2.value_or(fallback_value) == -1); + CHECK(convert("x", cnv).value_or_eval(fallback_function) == -1); + // example4 + CHECK(convert("x", cnv).value_or_eval(fallback_func()) == 42); + CHECK(convert("x", cnv, fallback_func()) == 42); + // example7 + CHECK(convert(str, cnv, fallback_value) == 123); + CHECK(convert("x", cnv, fallback_value) == -1); + CHECK_THROWS_AS(convert("x", cnv, boost::throw_on_failure), boost::bad_optional_access); + CHECK(convert(str).value_or(fallback_value) == 123); + CHECK(convert(str).value() == 123); +} + +TEST_CASE("example: stream") { + // example1 + CHECK(convert("123").value() == 123); + CHECK(convert("uhm").value_or(-1) == -1); + CHECK(convert(123).value() == "123"); + // example2 + boost::cnv::cstream ccnv; + boost::cnv::wstream wcnv; + CHECK(convert(" FF", ccnv(std::hex)(std::skipws)).value_or(0) == 255); + CHECK(convert(L" F", wcnv(std::hex)(std::skipws)).value_or(0) == 15); + CHECK(convert(" FF", ccnv(std::dec)(std::skipws)).value_or(-5) == -5); + CHECK(convert(L" F", wcnv(std::dec)(std::skipws)).value_or(-5) == -5); + ccnv(std::showbase)(std::uppercase)(std::hex); + CHECK(convert(255, ccnv, "bad") == "0XFF"); + CHECK(convert(15, ccnv, "bad") == "0XF"); + ccnv(arg::base = cnv::base::dec)(arg::uppercase = true)(arg::notation = cnv::notation::scientific); + CHECK(convert(1234.5, ccnv, "bad").find('E') != string::npos); + // example6 + boost::cnv::cstream cnv1; + boost::cnv::lexical_cast cnv2; + change chg = change::up; + CHECK(convert(chg, cnv1, "bad") == "up"); + CHECK(convert(change::dn, cnv1, "bad") == "dn"); + CHECK(convert("up", cnv1, change::no) == change::up); + CHECK(convert("up", cnv2, change::no) == change::up); +} + +TEST_CASE("example: algorithms") { + std::array strs = {{" 5", "0XF", "not an int"}}; + // introduction + { + std::vector ints; + boost::cnv::cstream cnv; + cnv(std::hex)(std::skipws); + std::transform(strs.begin(), strs.end(), std::back_inserter(ints), + boost::cnv::apply(std::cref(cnv)).value_or(-1)); + CHECK(ints == std::vector{5, 15, -1}); + } + // example1: lexical_cast throws on the first failure + { + std::vector ints; + CHECK_THROWS(std::transform(strs.begin(), strs.end(), std::back_inserter(ints), + [](char const* s) { return boost::lexical_cast(string(s)); })); + CHECK(ints.empty()); + } + // example2 + { + std::vector ints; + std::transform(strs.begin(), strs.end(), std::back_inserter(ints), + boost::cnv::apply(boost::cnv::lexical_cast()).value_or(-1)); + CHECK(ints == std::vector{-1, -1, -1}); + } + // example3 + { + std::vector ints; + boost::cnv::cstream cnv; + CHECK_THROWS_AS(std::transform(strs.begin(), strs.end(), std::back_inserter(ints), + boost::cnv::apply(std::cref(cnv(std::hex)(std::skipws)))), + boost::bad_optional_access); + CHECK(ints == std::vector{5, 15}); + } + // example4 + { + std::vector ints; + boost::cnv::cstream cnv; + std::transform(strs.begin(), strs.end(), std::back_inserter(ints), + boost::cnv::apply(std::cref(cnv(std::hex)(std::skipws))).value_or(-1)); + CHECK(ints == std::vector{5, 15, -1}); + } + // example5 + { + std::array ints = {{15, 16, 17}}; + std::vector out; + boost::cnv::cstream cnv; + cnv(std::hex)(std::uppercase)(std::showbase); + std::transform(ints.begin(), ints.end(), std::back_inserter(out), + boost::cnv::apply(std::cref(cnv))); + CHECK(out == std::vector{"0XF", "0X10", "0X11"}); + } +} + +namespace { +// design_notes.qbk#converter_signature: the strtol-based converter shape #2. +struct strtol_like { + int base_ = 10; + void operator()(char const* str_in, boost::optional& result_out) const { + char const* str_end = str_in + std::strlen(str_in); + char* cnv_end = nullptr; + long int result = std::strtol(str_in, &cnv_end, base_); + if (INT_MIN <= result && result <= INT_MAX && cnv_end == str_end) result_out = int(result); + } +}; +} // namespace + +TEST_CASE("doc: converter_signature") { + CHECK(convert("42", strtol_like()).value() == 42); + CHECK_FALSE(convert("42x", strtol_like()).has_value()); +} + +TEST_CASE("doc: user-interface-signature") { + boost::optional out = boost::convert("17"); + bool out_success = out ? true : false; + int out_value = out.value_or(-1); + CHECK(out_success); + CHECK(out_value == 17); + CHECK(boost::convert("z", boost::cnv::cstream(), -1) == -1); // #2 with a fallback +} + +TEST_CASE("doc: requirements") { + // R3a/R3b: throwing and non-throwing; R5c: fallback; R5a: success flag. + CHECK_THROWS(convert("z").value()); + CHECK_NOTHROW(convert("z").value_or(0)); + CHECK(convert("z", boost::cnv::cstream(), 7) == 7); + CHECK_FALSE(convert("z").has_value()); +} diff --git a/tests/convert/include_spirit.cpp b/tests/convert/include_spirit.cpp new file mode 100644 index 00000000..8925c5d6 --- /dev/null +++ b/tests/convert/include_spirit.cpp @@ -0,0 +1,52 @@ +// boost.convert spirit converter — pure include. boost/convert/spirit.hpp +// pulls Boost.Spirit (header-only, no module), so it is not in the convert +// module (tools/export_policy.toml [convert]); this TU uses headers only. +// Covers the spirit part of libs/convert/example/getting_started.cpp +// (example3) and the stream_converter page's default-constructible note. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include +#include + +namespace { +// A type without a default constructor: convert() builds it through +// boost::make_default. +struct direction { + enum value_type { up, dn }; + direction(value_type value) : value_(value) {} + bool operator==(direction that) const { return value_ == that.value_; } + value_type value() const { return value_; } +private: + value_type value_; +}; +std::istream& operator>>(std::istream& stream, direction& dir) { + std::string str; + stream >> str; + if (str == "up") dir = direction::up; + else if (str == "dn") dir = direction::dn; + else stream.setstate(std::ios_base::failbit); + return stream; +} +} // namespace + +namespace boost { +template <> +inline direction make_default() { + return direction(direction::up); +} +} // namespace boost + +TEST_CASE("example: getting_started (spirit converter)") { + auto cnv3 = boost::cnv::spirit(); + CHECK(boost::convert("123", cnv3).value() == 123); + CHECK_FALSE(boost::convert("12x", cnv3).has_value()); + CHECK(boost::convert("1.25", cnv3).value() == 1.25); +} + +TEST_CASE("doc: the-default-constructible-type-requirement") { + boost::cnv::cstream cnv; + CHECK(boost::convert("dn", cnv).value() == direction::dn); + CHECK(boost::convert("xx", cnv, direction::up) == direction::up); +} diff --git a/tests/uuid/api.cpp b/tests/uuid/api.cpp new file mode 100644 index 00000000..a355315a --- /dev/null +++ b/tests/uuid/api.cpp @@ -0,0 +1,382 @@ +// mcpplibs.mwrap.boost.uuid — every documented component of Boost.UUID +// (libs/uuid/doc/uuid/*.adoc): the uuid class (construction, iteration, data, +// size, is_nil, variant, version, time-based fields, swap, operators, hashing, +// serialization), the generators (nil, string, name md5/sha1, random, +// basic_random, time v1/v6/v7), uuid_clock, namespaces, constants and the I/O +// functions. Cases are "doc: #
". +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.uuid; +import mcpplibs.mwrap.boost.lexical_cast; +import mcpplibs.mwrap.boost.random; +import mcpplibs.mwrap.boost.serialization; + +using namespace boost::uuids; + +namespace { +constexpr uuid sample = uuid_from_string("01234567-89ab-cdef-0123-456789abcdef"); +std::string str(const uuid& u) { return to_string(u); } +} // namespace + +// ---- examples --------------------------------------------------------------- + +namespace { +class object { +public: + object() : tag(random_generator()()), state(0) {} + explicit object(int state) : tag(random_generator()()), state(state) {} + object(object const& rhs) = default; + bool operator==(object const& rhs) const { return tag == rhs.tag; } + object& operator=(object const& rhs) = default; + int get_state() const { return state; } + void set_state(int new_state) { state = new_state; } +private: + uuid tag; + int state; +}; +} // namespace + +TEST_CASE("doc: examples#examples") { + object o1(1); + object o2 = o1; + o2.set_state(2); + CHECK(o1 == o2); + object o3(3); + CHECK(o1 != o3); + CHECK(o2 != o3); +} + +TEST_CASE("doc: examples#byte-extraction") { + uuid u = sample; + std::vector v(u.size()); + std::copy(u.begin(), u.end(), v.begin()); + CHECK(v.front() == 0x01); + CHECK(v.back() == 0xef); +} + +// ---- uuid --------------------------------------------------------------------- + +TEST_CASE("doc: uuid#constructors") { + constexpr uuid nil; + static_assert(nil.is_nil()); + constexpr std::uint8_t bytes[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + constexpr uuid u(bytes); + static_assert(*u.begin() == 1); + CHECK(str(u) == "01020304-0506-0708-090a-0b0c0d0e0f10"); +} +TEST_CASE("doc: uuid#iteration") { + uuid u; + std::iota(u.begin(), u.end(), std::uint8_t(0)); + CHECK(std::distance(u.begin(), u.end()) == 16); + CHECK(*(u.end() - 1) == 15); +} +TEST_CASE("doc: uuid#data") { + uuid u = sample; + CHECK(u.data()[0] == 0x01); + u.data()[0] = 0xff; + CHECK(str(u).substr(0, 2) == "ff"); +} +TEST_CASE("doc: uuid#size") { + static_assert(uuid::static_size() == 16); + CHECK(sample.size() == 16); +} +TEST_CASE("doc: uuid#is-nil") { + CHECK(uuid().is_nil()); + CHECK_FALSE(sample.is_nil()); +} +TEST_CASE("doc: uuid#variant") { + CHECK(random_generator()().variant() == uuid::variant_rfc_4122); + uuid u; + u.data()[8] = 0x00; + CHECK(u.variant() == uuid::variant_ncs); +} +TEST_CASE("doc: uuid#version") { + CHECK(random_generator()().version() == uuid::version_random_number_based); + CHECK(name_generator_md5(ns::dns())("x").version() == uuid::version_name_based_md5); + CHECK(name_generator_sha1(ns::dns())("x").version() == uuid::version_name_based_sha1); + CHECK(time_generator_v1()().version() == uuid::version_time_based); + CHECK(time_generator_v6()().version() == uuid::version_time_based_v6); + CHECK(time_generator_v7()().version() == uuid::version_time_based_v7); + CHECK(uuid().version() == uuid::version_unknown); +} +TEST_CASE("doc: uuid#time-based-fields") { + uuid u1 = time_generator_v1()(); + CHECK(u1.timestamp_v1() == uuid_clock::to_timestamp(u1.time_point_v1())); + uuid u6 = time_generator_v6()(); + CHECK(u6.timestamp_v6() == uuid_clock::to_timestamp(u6.time_point_v6())); + uuid u7 = time_generator_v7()(); + auto ms = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); + auto t7 = std::chrono::duration_cast(u7.time_point_v7().time_since_epoch()); + CHECK(std::abs((ms - t7).count()) < 60000); + static_assert(std::is_same_v>); + CHECK(u1.node_identifier().size() == 6); + (void)u1.clock_seq(); +} +TEST_CASE("doc: uuid#swap") { + uuid a = sample, b; + a.swap(b); + CHECK(a.is_nil()); + CHECK(b == sample); +} +TEST_CASE("doc: uuid#free-swap") { + uuid a = sample, b; + swap(a, b); + CHECK(b == sample); +} +TEST_CASE("doc: uuid#operators") { + uuid a = sample, b = uuid_from_string("ffffffff-ffff-ffff-ffff-ffffffffffff"); + CHECK(a < b); + CHECK(a <= b); + CHECK(b > a); + CHECK(b >= a); + CHECK(a != b); + CHECK((a <=> b) == std::strong_ordering::less); + std::set s{b, a}; + CHECK(*s.begin() == a); +} +TEST_CASE("doc: uuid#hash-value") { + CHECK(hash_value(sample) == hash_value(uuid(sample))); + CHECK(hash_value(sample) != hash_value(uuid())); +} +TEST_CASE("doc: uuid#std-hash") { + std::unordered_set s{sample, uuid()}; + CHECK(s.count(sample) == 1); + CHECK(std::hash()(sample) == std::hash()(sample)); +} +TEST_CASE("doc: uuid#serialization") { + std::stringstream ss; + { + boost::archive::text_oarchive oa(ss); + uuid u = sample; + oa << u; + } + uuid back; + { + boost::archive::text_iarchive ia(ss); + ia >> back; + } + CHECK(back == sample); +} + +// ---- I/O ------------------------------------------------------------------------ + +TEST_CASE("doc: uuid_io#stream-insertion") { + uuid u1 = random_generator()(); + std::ostringstream os; + os << u1; + std::string s1 = boost::lexical_cast(u1); + CHECK(os.str() == s1); + CHECK(s1.size() == 36); + std::wostringstream wos; + wos << sample; + CHECK(wos.str() == L"01234567-89ab-cdef-0123-456789abcdef"); +} +TEST_CASE("doc: uuid_io#stream-extraction") { + uuid u1 = random_generator()(); + std::stringstream ss; + ss << u1; + uuid u2 = boost::lexical_cast(ss.str()); + CHECK(u1 == u2); + std::istringstream bad("not-a-uuid"); + uuid u3; + bad >> u3; + CHECK(bad.fail()); +} +TEST_CASE("doc: uuid_io#to-chars") { + char buf[37]; + char* end = to_chars(sample, buf); + CHECK(end == buf + 36); + CHECK(std::string(buf) == "01234567-89ab-cdef-0123-456789abcdef"); // the array form terminates + char small[10]; + CHECK_FALSE(to_chars(sample, small, small + 10)); + std::string out; + to_chars(sample, std::back_inserter(out)); + CHECK(out.size() == 36); + static_assert([] { + char b[37]{}; + to_chars(sample, b); + return b[8] == '-'; + }()); +} +TEST_CASE("doc: uuid_io#to-string") { + CHECK(to_string(sample) == "01234567-89ab-cdef-0123-456789abcdef"); + CHECK(to_wstring(sample) == L"01234567-89ab-cdef-0123-456789abcdef"); +} +TEST_CASE("doc: uuid_io#from-chars") { + std::string_view s = "01234567-89ab-cdef-0123-456789abcdef"; + uuid u; + auto r = from_chars(s.data(), s.data() + s.size(), u); + CHECK(static_cast(r)); + CHECK(u == sample); + std::string_view bad = "01234567-89ab-cdef-0123-456789abcdeX"; + auto r2 = from_chars(bad.data(), bad.data() + bad.size(), u); + CHECK(r2.ec == from_chars_error::hex_digit_expected); +} +TEST_CASE("doc: uuid_io#from-chars-result") { + std::string_view s = "0123456789abcdef0123456789abcdef"; // no dashes + uuid u; + auto r = from_chars(s.data(), s.data() + s.size(), u); + CHECK(r.ec == from_chars_error::dash_expected); + CHECK_FALSE(static_cast(r)); + std::string_view tail = "01234567-89ab-cdef-0123-456789abcdefXX"; + auto r2 = from_chars(tail.data(), tail.data() + tail.size(), u); + CHECK(r2.ec == from_chars_error::none); + CHECK(r2.ptr == tail.data() + 36); +} +TEST_CASE("doc: uuid_io#uuid-from-string") { + constexpr uuid u = uuid_from_string("01234567-89ab-cdef-0123-456789abcdef"); + static_assert(u.data()[1] == 0x23); + CHECK(uuid_from_string(std::string("01234567-89ab-cdef-0123-456789abcdef")) == sample); + CHECK_THROWS_AS(uuid_from_string("xyz"), std::runtime_error); +} + +// ---- generators ------------------------------------------------------------------- + +TEST_CASE("doc: nil_generator#nil-generator") { + nil_generator gen; + CHECK(gen().is_nil()); +} +TEST_CASE("doc: nil_generator#nil-uuid") { + constexpr uuid u = nil_uuid(); + static_assert(u.is_nil()); +} +TEST_CASE("doc: string_generator#string-generator") { + string_generator gen; + uuid u1 = gen(std::string("0123456789abcdef0123456789abcdef")); + uuid u2 = gen(std::wstring(L"01234567-89AB-CDEF-0123-456789ABCDEF")); + CHECK(u1 == u2); + uuid u3 = gen("{01234567-89ab-cdef-0123-456789abcdef}"); + uuid u4 = gen(L"01234567-89ab-cdef-0123-456789abcdef"); + CHECK(u3 == u4); + CHECK(u1 == u3); + std::string text = "{0123456789abcdef0123456789abcdef}"; + CHECK(gen(text.begin(), text.end()) == u1); + CHECK_THROWS_AS(gen("0123"), std::runtime_error); +} +TEST_CASE("doc: name_generator_md5#name-generator-md5") { + name_generator_md5 gen(ns::dns()); + uuid u = gen("boost.org"); + CHECK(u.version() == uuid::version_name_based_md5); + CHECK(gen("boost.org") == u); // deterministic + CHECK(gen(std::string("boost.org")) == u); + CHECK(gen("boost.org", 9) == u); // buffer + byte count +} +TEST_CASE("doc: name_generator_sha1#name-generator-sha1") { + name_generator_sha1 gen(ns::dns()); + CHECK(str(gen("boost.org")) == "0043f363-bbb4-5369-840a-322df6ec1926"); + CHECK(str(gen(L"boost.org")) == "c31c5016-3493-5dc2-8484-5813d495cc18"); + CHECK(str(gen(u"boost.org")) == "0043f363-bbb4-5369-840a-322df6ec1926"); + static_assert(std::is_same_v); + CHECK(name_generator_latest(ns::url())("x").version() >= uuid::version_name_based_sha1); +} +TEST_CASE("doc: namespaces#namespaces") { + CHECK(str(ns::dns()) == "6ba7b810-9dad-11d1-80b4-00c04fd430c8"); + CHECK(str(ns::url()) == "6ba7b811-9dad-11d1-80b4-00c04fd430c8"); + CHECK(str(ns::oid()) == "6ba7b812-9dad-11d1-80b4-00c04fd430c8"); + CHECK(str(ns::x500dn()) == "6ba7b814-9dad-11d1-80b4-00c04fd430c8"); +} +TEST_CASE("doc: random_generator#random-generator") { + random_generator gen; + uuid a = gen(), b = gen(); + CHECK(a != b); + CHECK(a.version() == uuid::version_random_number_based); + random_generator_mt19937 mt; + CHECK(mt().variant() == uuid::variant_rfc_4122); + random_generator_pure pure; + CHECK_FALSE(pure().is_nil()); +} +TEST_CASE("doc: basic_random_generator#basic-random-generator") { + basic_random_generator bulkgen; + std::set seen; + for (int i = 0; i < 1000; ++i) seen.insert(bulkgen()); + CHECK(seen.size() == 1000); + boost::mt19937 engine(42), engine2(42); + basic_random_generator by_ref(engine), by_ptr(&engine2); + CHECK(by_ref() == by_ptr()); // same seed, same sequence +} +TEST_CASE("doc: time_generator_v1#constructors") { + time_generator_v1 gen; + std::array node{1, 2, 3, 4, 5, 6}; + time_generator_v1::state_type state{0, 0x1234}; + time_generator_v1 fixed(node, state); + uuid u = fixed(); + CHECK(u.node_identifier() == node); + (void)gen(); +} +TEST_CASE("doc: time_generator_v1#operator") { + time_generator_v1 gen; + uuid a = gen(), b = gen(); + CHECK(a != b); + CHECK(a.timestamp_v1() <= b.timestamp_v1()); +} +TEST_CASE("doc: time_generator_v7#constructors") { + time_generator_v7 a; + time_generator_v7 b(a); + time_generator_v7 c(std::move(b)); + CHECK(c().version() == uuid::version_time_based_v7); +} +TEST_CASE("doc: time_generator_v7#assignment") { + time_generator_v7 a, b; + b = a; + b = std::move(a); + CHECK_FALSE(b().is_nil()); +} +TEST_CASE("doc: time_generator_v7#operator") { + time_generator_v7 gen; + uuid prev = gen(); + for (int i = 0; i < 100; ++i) { + uuid next = gen(); + CHECK(prev < next); // v7 values sort by creation time + prev = next; + } +} +TEST_CASE("doc: uuid_clock#now") { + auto t = uuid_clock::now(); + CHECK(t.time_since_epoch().count() > 0); + static_assert(!uuid_clock::is_steady); +} +TEST_CASE("doc: uuid_clock#from-sys") { + auto sys = std::chrono::system_clock::now(); + auto u = uuid_clock::from_sys(sys); + auto back = uuid_clock::to_sys(u); + CHECK(std::chrono::abs(back - std::chrono::time_point_cast(sys)).count() == 0); +} +TEST_CASE("doc: uuid_clock#to-sys") { + // The Gregorian epoch of RFC 4122 timestamps is 1582-10-15. + auto epoch = uuid_clock::to_sys(uuid_clock::time_point{}); + auto days = std::chrono::floor(epoch); + CHECK(std::chrono::year_month_day(days) == std::chrono::year{1582} / std::chrono::October / 15); +} +TEST_CASE("doc: uuid_clock#from-timestamp") { + auto tp = uuid_clock::from_timestamp(12345); + CHECK(tp.time_since_epoch().count() == 12345); +} +TEST_CASE("doc: uuid_clock#to-timestamp") { + uuid u = time_generator_v1()(); + CHECK(u.timestamp_v1() == uuid_clock::to_timestamp(u.time_point_v1())); +} +TEST_CASE("doc: constants#constants") { + // : nil_uuid() and the namespaces. + CHECK(nil_uuid().is_nil()); + CHECK(nil_uuid() == uuid()); + CHECK_FALSE(ns::dns().is_nil()); +} +TEST_CASE("doc: invalid_uuid#invalid-uuid") { + try { + (void)uuid_from_string("nope"); + FAIL("expected an exception"); + } catch (const std::runtime_error& e) { + CHECK(std::string(e.what()).size() > 0); + } + static_assert(std::is_base_of_v); + try { + (void)uuid_from_string("01234567-89ab-cdef-0123-456789abcdeX"); + } catch (const invalid_uuid& e) { + CHECK(e.position() == 35); + CHECK(e.error() == from_chars_error::hex_digit_expected); + } + invalid_uuid made(3, from_chars_error::dash_expected); + CHECK(made.position() == 3); +} diff --git a/tests/uuid/coverage.toml b/tests/uuid/coverage.toml index 1331ed4f..eb1da215 100644 --- a/tests/uuid/coverage.toml +++ b/tests/uuid/coverage.toml @@ -6,216 +6,216 @@ upstream = "1.92.0" library = "uuid" [[snippet]] -doc = "libs/uuid/doc/uuid/basic_random_generator.adoc#basic-random-generator" -title = "basic_random_generator" -status = "todo" +doc = "libs/uuid/doc/uuid/basic_random_generator.adoc#basic-random-generator" +title = "basic_random_generator" +case = "uuid/api:doc: basic_random_generator#basic-random-generator" [[snippet]] -doc = "libs/uuid/doc/uuid/constants.adoc#constants" -title = "Constants" -status = "todo" +doc = "libs/uuid/doc/uuid/constants.adoc#constants" +title = "Constants" +case = "uuid/api:doc: constants#constants" [[snippet]] -doc = "libs/uuid/doc/uuid/examples.adoc#byte-extraction" -title = "Byte Extraction" -status = "todo" +doc = "libs/uuid/doc/uuid/examples.adoc#byte-extraction" +title = "Byte Extraction" +case = "uuid/api:doc: examples#byte-extraction" [[snippet]] -doc = "libs/uuid/doc/uuid/examples.adoc#examples" -title = "Tagging" -status = "todo" +doc = "libs/uuid/doc/uuid/examples.adoc#examples" +title = "Tagging" +case = "uuid/api:doc: examples#examples" [[snippet]] -doc = "libs/uuid/doc/uuid/invalid_uuid.adoc#invalid-uuid" -title = "invalid_uuid" -status = "todo" +doc = "libs/uuid/doc/uuid/invalid_uuid.adoc#invalid-uuid" +title = "invalid_uuid" +case = "uuid/api:doc: invalid_uuid#invalid-uuid" [[snippet]] -doc = "libs/uuid/doc/uuid/name_generator_md5.adoc#name-generator-md5" -title = "name_generator_md5" -status = "todo" +doc = "libs/uuid/doc/uuid/name_generator_md5.adoc#name-generator-md5" +title = "name_generator_md5" +case = "uuid/api:doc: name_generator_md5#name-generator-md5" [[snippet]] -doc = "libs/uuid/doc/uuid/name_generator_sha1.adoc#name-generator-sha1" -title = "name_generator_sha1" -status = "todo" +doc = "libs/uuid/doc/uuid/name_generator_sha1.adoc#name-generator-sha1" +title = "name_generator_sha1" +case = "uuid/api:doc: name_generator_sha1#name-generator-sha1" [[snippet]] -doc = "libs/uuid/doc/uuid/namespaces.adoc#namespaces" -title = "Namespaces" -status = "todo" +doc = "libs/uuid/doc/uuid/namespaces.adoc#namespaces" +title = "Namespaces" +case = "uuid/api:doc: namespaces#namespaces" [[snippet]] -doc = "libs/uuid/doc/uuid/nil_generator.adoc#nil-generator" -title = "nil_generator" -status = "todo" +doc = "libs/uuid/doc/uuid/nil_generator.adoc#nil-generator" +title = "nil_generator" +case = "uuid/api:doc: nil_generator#nil-generator" [[snippet]] -doc = "libs/uuid/doc/uuid/nil_generator.adoc#nil-uuid" -title = "nil_uuid" -status = "todo" +doc = "libs/uuid/doc/uuid/nil_generator.adoc#nil-uuid" +title = "nil_uuid" +case = "uuid/api:doc: nil_generator#nil-uuid" [[snippet]] -doc = "libs/uuid/doc/uuid/random_generator.adoc#random-generator" -title = "random_generator" -status = "todo" +doc = "libs/uuid/doc/uuid/random_generator.adoc#random-generator" +title = "random_generator" +case = "uuid/api:doc: random_generator#random-generator" [[snippet]] -doc = "libs/uuid/doc/uuid/string_generator.adoc#string-generator" -title = "string_generator" -status = "todo" +doc = "libs/uuid/doc/uuid/string_generator.adoc#string-generator" +title = "string_generator" +case = "uuid/api:doc: string_generator#string-generator" [[snippet]] -doc = "libs/uuid/doc/uuid/time_generator_v1.adoc#constructors" -title = "Constructors" -status = "todo" +doc = "libs/uuid/doc/uuid/time_generator_v1.adoc#constructors" +title = "Constructors" +case = "uuid/api:doc: time_generator_v1#constructors" [[snippet]] -doc = "libs/uuid/doc/uuid/time_generator_v1.adoc#operator" -title = "operator()" -status = "todo" +doc = "libs/uuid/doc/uuid/time_generator_v1.adoc#operator" +title = "operator()" +case = "uuid/api:doc: time_generator_v1#operator" [[snippet]] -doc = "libs/uuid/doc/uuid/time_generator_v7.adoc#assignment" -title = "Assignment" -status = "todo" +doc = "libs/uuid/doc/uuid/time_generator_v7.adoc#assignment" +title = "Assignment" +case = "uuid/api:doc: time_generator_v7#assignment" [[snippet]] -doc = "libs/uuid/doc/uuid/time_generator_v7.adoc#constructors" -title = "Constructors" -status = "todo" +doc = "libs/uuid/doc/uuid/time_generator_v7.adoc#constructors" +title = "Constructors" +case = "uuid/api:doc: time_generator_v7#constructors" [[snippet]] -doc = "libs/uuid/doc/uuid/time_generator_v7.adoc#operator" -title = "operator()" -status = "todo" +doc = "libs/uuid/doc/uuid/time_generator_v7.adoc#operator" +title = "operator()" +case = "uuid/api:doc: time_generator_v7#operator" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#constructors" -title = "Constructors" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#constructors" +title = "Constructors" +case = "uuid/api:doc: uuid#constructors" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#data" -title = "Data" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#data" +title = "Data" +case = "uuid/api:doc: uuid#data" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#free-swap" -title = "Free Swap" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#free-swap" +title = "Free Swap" +case = "uuid/api:doc: uuid#free-swap" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#hash-value" -title = "hash_value" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#hash-value" +title = "hash_value" +case = "uuid/api:doc: uuid#hash-value" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#is-nil" -title = "is_nil" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#is-nil" +title = "is_nil" +case = "uuid/api:doc: uuid#is-nil" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#iteration" -title = "Iteration" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#iteration" +title = "Iteration" +case = "uuid/api:doc: uuid#iteration" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#operators" -title = "Operators" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#operators" +title = "Operators" +case = "uuid/api:doc: uuid#operators" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#serialization" -title = "Serialization" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#serialization" +title = "Serialization" +case = "uuid/api:doc: uuid#serialization" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#size" -title = "Size" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#size" +title = "Size" +case = "uuid/api:doc: uuid#size" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#std-hash" -title = "std::hash" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#std-hash" +title = "std::hash" +case = "uuid/api:doc: uuid#std-hash" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#swap" -title = "Swap" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#swap" +title = "Swap" +case = "uuid/api:doc: uuid#swap" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#time-based-fields" -title = "Time-based Fields" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#time-based-fields" +title = "Time-based Fields" +case = "uuid/api:doc: uuid#time-based-fields" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#variant" -title = "Variant" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#variant" +title = "Variant" +case = "uuid/api:doc: uuid#variant" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid.adoc#version" -title = "Version" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid.adoc#version" +title = "Version" +case = "uuid/api:doc: uuid#version" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_clock.adoc#from-sys" -title = "from_sys" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#from-sys" +title = "from_sys" +case = "uuid/api:doc: uuid_clock#from-sys" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_clock.adoc#from-timestamp" -title = "from_timestamp" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#from-timestamp" +title = "from_timestamp" +case = "uuid/api:doc: uuid_clock#from-timestamp" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_clock.adoc#now" -title = "now" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#now" +title = "now" +case = "uuid/api:doc: uuid_clock#now" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_clock.adoc#to-sys" -title = "to_sys" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#to-sys" +title = "to_sys" +case = "uuid/api:doc: uuid_clock#to-sys" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_clock.adoc#to-timestamp" -title = "to_timestamp" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_clock.adoc#to-timestamp" +title = "to_timestamp" +case = "uuid/api:doc: uuid_clock#to-timestamp" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_io.adoc#from-chars" -title = "from_chars" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_io.adoc#from-chars" +title = "from_chars" +case = "uuid/api:doc: uuid_io#from-chars" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_io.adoc#from-chars-result" -title = "from_chars_result" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_io.adoc#from-chars-result" +title = "from_chars_result" +case = "uuid/api:doc: uuid_io#from-chars-result" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_io.adoc#stream-extraction" -title = "Stream Extraction" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_io.adoc#stream-extraction" +title = "Stream Extraction" +case = "uuid/api:doc: uuid_io#stream-extraction" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_io.adoc#stream-insertion" -title = "Stream Insertion" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_io.adoc#stream-insertion" +title = "Stream Insertion" +case = "uuid/api:doc: uuid_io#stream-insertion" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_io.adoc#to-chars" -title = "to_chars" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_io.adoc#to-chars" +title = "to_chars" +case = "uuid/api:doc: uuid_io#to-chars" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_io.adoc#to-string" -title = "to_string" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_io.adoc#to-string" +title = "to_string" +case = "uuid/api:doc: uuid_io#to-string" [[snippet]] -doc = "libs/uuid/doc/uuid/uuid_io.adoc#uuid-from-string" -title = "uuid_from_string" -status = "todo" +doc = "libs/uuid/doc/uuid/uuid_io.adoc#uuid-from-string" +title = "uuid_from_string" +case = "uuid/api:doc: uuid_io#uuid-from-string" diff --git a/tests/uuid/custom_types.cpp b/tests/uuid/custom_types.cpp new file mode 100644 index 00000000..338a0a05 --- /dev/null +++ b/tests/uuid/custom_types.cpp @@ -0,0 +1,103 @@ +// mcpplibs.mwrap.boost.uuid — user types at UUID's customization points: +// basic_random_generator over a user UniformRandomNumberGenerator, a user +// namespace UUID for the name generators, uuids as keys in user containers +// and members of user types (with hashing), time_generator_v1 over a user +// node id and a shared std::atomic state, and to_chars into a user buffer. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.uuid; + +using namespace boost::uuids; + +namespace user { +// A deterministic UniformRandomNumberGenerator (splitmix64). +class splitmix { +public: + using result_type = std::uint64_t; + splitmix() = default; // basic_random_generator default-constructs its own engine member + explicit splitmix(std::uint64_t seed) : s_(seed) {} + static constexpr result_type min() { return 0; } + static constexpr result_type max() { return ~result_type(0); } + result_type operator()() { + std::uint64_t z = (s_ += 0x9E3779B97F4A7C15ull); + z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ull; + z = (z ^ (z >> 27)) * 0x94D049BB133111EBull; + return z ^ (z >> 31); + } +private: + std::uint64_t s_ = 0; +}; + +struct asset { + uuid id; + std::string name; + bool operator==(const asset& o) const { return id == o.id; } +}; +struct asset_hash { + std::size_t operator()(const asset& a) const noexcept { return hash_value(a.id); } +}; + +// The application's own namespace UUID for name-based ids. +constexpr uuid app_namespace = uuid_from_string("a0b1c2d3-e4f5-4607-8899-aabbccddeeff"); + +struct fixed_buffer { + char bytes[40]{}; +}; +} // namespace user + +TEST_CASE("basic_random_generator over a user engine") { + user::splitmix e1(1), e2(1), e3(2); + basic_random_generator g1(e1), g2(e2), g3(e3); + uuid a = g1(), b = g2(), c = g3(); + CHECK(a == b); // same seed, same uuid + CHECK(a != c); + CHECK(a.version() == uuid::version_random_number_based); + CHECK(a.variant() == uuid::variant_rfc_4122); + basic_random_generator by_ptr(&e3); + CHECK(by_ptr() != c); // the engine advanced +} + +TEST_CASE("name generators in a user namespace") { + name_generator_sha1 ids(user::app_namespace); + uuid alice = ids("alice"), bob = ids("bob"); + CHECK(alice != bob); + CHECK(ids("alice") == alice); + name_generator_md5 legacy(user::app_namespace); + CHECK(legacy("alice") != alice); + CHECK(legacy("alice").version() == uuid::version_name_based_md5); + const std::uint8_t blob[] = {1, 2, 3}; + CHECK(ids(blob, sizeof blob) == ids(blob, sizeof blob)); +} + +TEST_CASE("uuids in user types and containers") { + random_generator gen; + std::unordered_set assets; + user::asset a{gen(), "a"}, b{gen(), "b"}; + assets.insert(a); + assets.insert(b); + assets.insert(a); + CHECK(assets.size() == 2); + std::map by_id{{a.id, a}, {b.id, b}}; + CHECK(by_id.at(b.id).name == "b"); + std::unordered_map counts; + ++counts[a.id]; + ++counts[a.id]; + CHECK(counts[a.id] == 2); +} + +TEST_CASE("time_generator_v1 with a user node and a shared atomic state") { + uuid::node_type node{0x02, 0, 0, 0, 0, 0x01}; // locally administered + std::atomic state{time_generator_v1::state_type{0, 0x0101}}; + time_generator_v1 g1(node, state), g2(node, state); + uuid a = g1(), b = g2(); + CHECK(a != b); // the shared state keeps the two generators apart + CHECK(a.node_identifier() == node); + CHECK(b.node_identifier() == node); +} + +TEST_CASE("to_chars into a user buffer") { + user::fixed_buffer buf; + CHECK(to_chars(user::app_namespace, buf.bytes, buf.bytes + 36)); + CHECK(std::string(buf.bytes, 36) == "a0b1c2d3-e4f5-4607-8899-aabbccddeeff"); +} diff --git a/tools/apply_patches.py b/tools/apply_patches.py index 484e3977..0fac612a 100644 --- a/tools/apply_patches.py +++ b/tools/apply_patches.py @@ -28,8 +28,9 @@ # One entry per patches/.patch; see patches/README.md for each reason. PATCHES = [ - "accumulators", "algorithm", "asio", "beast", "config_platform", "config_user", "hof", - "io", "lambda", "mqtt5", "multi_array", "parameter", "range_adaptors", + "accumulators", "algorithm", "asio", "beast", "config_platform", "config_user", + "convert", "hof", "io", "lambda", "mqtt5", "multi_array", "parameter", + "range_adaptors", "numeric", "regex", "safe_numerics", "serialization", "system", "test", "ublas", "units", ] diff --git a/tools/export_policy.toml b/tools/export_policy.toml index 848be222..27de7a79 100644 --- a/tools/export_policy.toml +++ b/tools/export_policy.toml @@ -155,6 +155,24 @@ gmf = [ "boost/algorithm/string/trim_all.hpp", ] +[convert] +# The converter headers are not self-contained (they use uint8_t without +# and boost::optional without its header, relying on +# coming first, as in every upstream example), so the +# standalone-compile filter dropped them and the module exported convert() +# with no converter. convert.hpp leads the GMF. boost/convert/spirit.hpp is +# left out: it pulls Boost.Spirit, a header-only library no module owns, whose +# whole qi surface would become convert's exports; the spirit converter is +# consumed as headers (tests/convert/include_spirit.cpp). +gmf = [ + "boost/convert.hpp", + "boost/convert/charconv.hpp", + "boost/convert/lexical_cast.hpp", + "boost/convert/printf.hpp", + "boost/convert/stream.hpp", + "boost/convert/strtol.hpp", +] + [cobalt] # POSIX cobalt selects the epoll reactor; asio's reactor/hash_map details are # reached only through Windows-flavored chains in the MinGW snapshot. The diff --git a/upstream/boost/boost/convert.hpp b/upstream/boost/boost/convert.hpp index b022ccb7..a7d988dd 100644 --- a/upstream/boost/boost/convert.hpp +++ b/upstream/boost/boost/convert.hpp @@ -34,7 +34,10 @@ namespace boost /// @details boost::throw_on_failure is the 'tag' object /// to request the exception-throwing behavior. - detail::throw_on_failure const throw_on_failure = detail::throw_on_failure(0); + // boost-m vendored edit: `const` gave the tag internal linkage, so the + // module could not export it (TU-local); `inline constexpr` has external + // linkage and the same value. Replay after re-running import_boost. + inline constexpr detail::throw_on_failure throw_on_failure = detail::throw_on_failure(0); namespace cnv { From cf3e28112cf9555d5b0fd0248fe4867d735e2263 Mon Sep 17 00:00:00 2001 From: Zhe Feng Date: Mon, 9 Nov 2026 14:22:40 +0000 Subject: [PATCH 5/9] bimap: export the _key/_data lambda placeholders; port the 27 official examples patches/bimap.patch turns the anonymous-namespace references of boost/bimap/support/lambda.hpp into inline constexpr (as lambda's _1), so the bimap module exports them. The examples run as doctest cases whose output must equal the original program's, built from upstream/boost alone with clang++; examples that combine bimap with assign, lambda, property_map, range or serialization use those modules, and the foreach / typeof / xpressive ones are a pure-include TU. api.cpp covers the inline tutorial code, custom_types.cpp user keys, comparators, hashers, tags and info payloads. --- generated/exports/bimap.inc | 4 +- generated/meta/api-surface.json | 2 +- patches/README.md | 4 + patches/bimap.patch | 30 + tests/bimap/api.cpp | 146 ++ tests/bimap/coverage.toml | 177 +- tests/bimap/custom_types.cpp | 89 + tests/bimap/examples.cpp | 1667 +++++++++++++++++ tests/bimap/examples_with_boost.cpp | 623 ++++++ tests/bimap/include_with_boost.cpp | 278 +++ tools/apply_patches.py | 9 +- upstream/boost/boost/bimap/support/lambda.hpp | 12 +- 12 files changed, 2941 insertions(+), 100 deletions(-) create mode 100644 patches/bimap.patch create mode 100644 tests/bimap/api.cpp create mode 100644 tests/bimap/custom_types.cpp create mode 100644 tests/bimap/examples.cpp create mode 100644 tests/bimap/examples_with_boost.cpp create mode 100644 tests/bimap/include_with_boost.cpp diff --git a/generated/exports/bimap.inc b/generated/exports/bimap.inc index 6bdeab84..824b146b 100644 --- a/generated/exports/bimap.inc +++ b/generated/exports/bimap.inc @@ -1,5 +1,5 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=bimap boost=1.92.0 target=x86_64-w64-mingw32 entities=190 +// lib=bimap boost=1.92.0 target=x86_64-w64-mingw32 entities=192 export namespace boost { using boost::bimap; @@ -9,6 +9,8 @@ export namespace boost { } export namespace boost { namespace bimaps { + using boost::bimaps::_data; + using boost::bimaps::_key; using boost::bimaps::_relation; using boost::bimaps::bimap; using boost::bimaps::left_based; diff --git a/generated/meta/api-surface.json b/generated/meta/api-surface.json index dc405a5b..a8aa6e65 100644 --- a/generated/meta/api-surface.json +++ b/generated/meta/api-surface.json @@ -233,7 +233,7 @@ "multi_index", "type_traits" ], - "exported": 190, + "exported": 192, "gmf": [ "boost/bimap.hpp", "boost/bimap/container_adaptor/support/iterator_facade_converters.hpp", diff --git a/patches/README.md b/patches/README.md index 5a74ae75..b6ec1e23 100644 --- a/patches/README.md +++ b/patches/README.md @@ -38,6 +38,10 @@ second one. The algorithm module's GMF includes them in that order, and namespace-scope `const` object (internal linkage), which a module cannot export; it becomes `inline constexpr`. +`bimap.patch` is the same case: the `_key` / `_data` lambda placeholders +of `boost/bimap/support/lambda.hpp` were references in an anonymous +namespace; they become `inline constexpr`, as `boost::lambda::_1` did. + Every patch is reviewed against the 1.92.0 tag it was written for; `upstream/boost/SOURCE.md` records the archive and checksum the tree comes from. A patch that neither applies nor is already applied fails the replay. diff --git a/patches/bimap.patch b/patches/bimap.patch new file mode 100644 index 00000000..a73bb582 --- /dev/null +++ b/patches/bimap.patch @@ -0,0 +1,30 @@ +diff --git a/upstream/boost/boost/bimap/support/lambda.hpp b/upstream/boost/boost/bimap/support/lambda.hpp +index c8c44ecc..5985970c 100644 +--- a/upstream/boost/boost/bimap/support/lambda.hpp ++++ b/upstream/boost/boost/bimap/support/lambda.hpp +@@ -23,8 +23,6 @@ + namespace boost { + namespace bimaps { + +-namespace { +- + /* + boost::lambda::placeholder1_type & _first = boost::lambda::_1; + boost::lambda::placeholder2_type & _second = boost::lambda::_2; +@@ -33,10 +31,12 @@ boost::lambda::placeholder1_type & _left = boost::lambda::_1; + boost::lambda::placeholder2_type & _right = boost::lambda::_2; + */ + +-boost::lambda::placeholder1_type & _key = boost::lambda::_1; +-boost::lambda::placeholder1_type & _data = boost::lambda::_1; +- +-} ++// boost-m vendored edit: the references lived in an anonymous namespace ++// (internal linkage), so the bimap module could not export them; like ++// boost::lambda::_1 (patches/lambda.patch) they become inline constexpr. ++// Replay after re-running import_boost. ++inline constexpr boost::lambda::placeholder1_type & _key = boost::lambda::_1; ++inline constexpr boost::lambda::placeholder1_type & _data = boost::lambda::_1; + + } // namespace bimaps + } // namespace boost diff --git a/tests/bimap/api.cpp b/tests/bimap/api.cpp new file mode 100644 index 00000000..4a02aded --- /dev/null +++ b/tests/bimap/api.cpp @@ -0,0 +1,146 @@ +// mcpplibs.mwrap.boost.bimap — the tutorial and rationale sections of the +// Boost.Bimap documentation (libs/bimap/doc/{quick_tutorial,tutorial, +// rationale}.qbk) whose code is written inline; sections that import an +// example are covered by that example's case (coverage.toml). +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.bimap; + +using namespace boost::bimaps; + +TEST_CASE("doc: one-minute-tutorial") { + using bm_type = bimap; + bm_type bm; + bm.insert(bm_type::value_type(1, "one")); + bm.insert(bm_type::value_type(2, "two")); + bm.left.insert(bm_type::left_value_type(3, "three")); + bm.right.insert(bm_type::right_value_type("four", 4)); + CHECK(bm.size() == 4); + CHECK(bm.left.at(1) == "one"); + CHECK(bm.right.at("two") == 2); + for (bm_type::const_iterator it = bm.begin(); it != bm.end(); ++it) + CHECK(bm.left.at(it->left) == it->right); + // The left view works like std::map. + bm_type::left_const_iterator l = bm.left.find(3); + REQUIRE(l != bm.left.end()); + CHECK(l->second == "three"); + // Inserting a duplicate key in either view fails. + CHECK_FALSE(bm.insert(bm_type::value_type(1, "uno")).second); + CHECK_FALSE(bm.insert(bm_type::value_type(9, "one")).second); +} + +TEST_CASE("doc: bimap-mapping-framework") { + // A bimap has three views: left (a map from left), right (a map from + // right) and the relation set itself. + bimap bm; + bm.insert({"a", 1}); + CHECK(bm.left.begin()->first == "a"); + CHECK(bm.right.begin()->first == 1); + CHECK(bm.begin()->left == "a"); + CHECK(bm.begin()->right == 1); +} + +TEST_CASE("doc: freedom-of-choice") { + // Each side picks its own collection type. + bimap, multiset_of> a; + a.insert({"x", 1}); + a.insert({"y", 1}); // multiset_of: repeated rights allowed + CHECK(a.right.count(1) == 2); + bimap, list_of> b; + b.insert({1, "z"}); + b.insert({2, "z"}); + CHECK(b.size() == 2); + bimap, unconstrained_set_of> c; + c.left.push_back(decltype(c)::left_value_type(7, 70)); + CHECK(c.left.size() == 1); +} + +TEST_CASE("doc: complete-instantiation-scheme") { + struct a_tag {}; + struct b_tag {}; + using bm_type = bimap, multiset_of>, set_of_relation<>, + with_info>; + bm_type bm; + bm.insert(bm_type::value_type(1, "one", 1.0)); + CHECK(bm.by().at(1) == "one"); + CHECK(bm.by().find("one")->info == 1.0); +} + +TEST_CASE("doc: bimaps-with-user-defined-names") { + struct id {}; + struct name {}; + using people = bimap, tagged>; + people p; + p.insert(people::value_type(1, "ada")); + CHECK(p.by().at(1) == "ada"); + CHECK(p.by().at("ada") == 1); + people::map_by::const_iterator it = p.by().find("ada"); + CHECK(it->get() == 1); +} + +TEST_CASE("doc: insertion") { + using bm_type = bimap; + bm_type bm; + bm.insert(bm_type::value_type(1, "one")); + bm.left.insert(bm_type::left_value_type(2, "two")); + bm.right.insert(bm_type::right_value_type("three", 3)); + bm.left.insert(std::make_pair(4, "four")); + CHECK(bm.size() == 4); + auto r = bm.insert(bm_type::value_type(1, "uno")); + CHECK_FALSE(r.second); + CHECK(r.first->right == "one"); +} + +TEST_CASE("doc: iterator-value-type") { + using bm_type = bimap; + bm_type bm; + bm.insert(bm_type::value_type(1, "a")); + // The left view's value_type has first/second, the relation left/right. + bm_type::left_reference lr = *bm.left.begin(); + CHECK(lr.first == 1); + CHECK(lr.second == "a"); + bm_type::right_reference rr = *bm.right.begin(); + CHECK(rr.first == "a"); + CHECK(rr.second == 1); + bm_type::reference rel = *bm.begin(); + CHECK(rel.left == 1); + CHECK(rel.right == "a"); + // Keys are not modifiable through a set_of view. + static_assert(std::is_const_v>); +} + +TEST_CASE("doc: operator-and-at") { + bimap> bm; + bm.insert({"one", 1}); + CHECK(bm.left.at("one") == 1); + CHECK_THROWS_AS(bm.left.at("two"), std::out_of_range); + bimap> freq; // operator[] needs a mutable data side + freq.left["a"] = 1; + freq.left["a"] += 2; + CHECK(freq.left.at("a") == 3); +} + +TEST_CASE("doc: general-design") { + // The relation type is a pair-like with left/right and views as maps. + using bm_type = bimap; + bm_type::value_type v('c', 3); + CHECK(v.left == 'c'); + CHECK(v.right == 3); + using left_pair = bm_type::left_map::value_type; + left_pair lp('d', 4); + CHECK(lp.first == 'd'); +} + +TEST_CASE("doc: the-student-and-the-mentor") { + // The rationale's student/mentor bidirectional assignment. + struct student {}; + struct mentor {}; + using assignments = bimap>, tagged>; + assignments a; + a.insert(assignments::value_type("dijkstra", "alice")); + a.insert(assignments::value_type("dijkstra", "bob")); + a.insert(assignments::value_type("knuth", "carol")); + CHECK(a.by().count("dijkstra") == 2); + CHECK(a.by().at("carol") == "knuth"); +} diff --git a/tests/bimap/coverage.toml b/tests/bimap/coverage.toml index 94fb5877..0e6227e1 100644 --- a/tests/bimap/coverage.toml +++ b/tests/bimap/coverage.toml @@ -6,174 +6,177 @@ upstream = "1.92.0" library = "bimap" [[example]] -path = "libs/bimap/example/at_function_examples.cpp" -status = "todo" +path = "libs/bimap/example/at_function_examples.cpp" +case = "bimap/examples:example: at_function_examples" [[example]] -path = "libs/bimap/example/bimap_and_boost/assign.cpp" -status = "todo" +path = "libs/bimap/example/bimap_and_boost/assign.cpp" +case = "bimap/examples_with_boost:example: assign" [[example]] -path = "libs/bimap/example/bimap_and_boost/foreach.cpp" -status = "todo" +path = "libs/bimap/example/bimap_and_boost/foreach.cpp" +case = "bimap/include_with_boost:example: foreach" [[example]] -path = "libs/bimap/example/bimap_and_boost/lambda.cpp" -status = "todo" +path = "libs/bimap/example/bimap_and_boost/lambda.cpp" +case = "bimap/examples_with_boost:example: lambda" [[example]] -path = "libs/bimap/example/bimap_and_boost/property_map.cpp" -status = "todo" +path = "libs/bimap/example/bimap_and_boost/property_map.cpp" +case = "bimap/examples_with_boost:example: property_map" [[example]] -path = "libs/bimap/example/bimap_and_boost/range.cpp" -status = "todo" +path = "libs/bimap/example/bimap_and_boost/range.cpp" +case = "bimap/examples_with_boost:example: range" [[example]] -path = "libs/bimap/example/bimap_and_boost/serialization.cpp" -status = "todo" +path = "libs/bimap/example/bimap_and_boost/serialization.cpp" +case = "bimap/examples_with_boost:example: serialization" [[example]] -path = "libs/bimap/example/bimap_and_boost/typeof.cpp" -status = "todo" +path = "libs/bimap/example/bimap_and_boost/typeof.cpp" +case = "bimap/include_with_boost:example: typeof" [[example]] -path = "libs/bimap/example/bimap_and_boost/xpressive.cpp" -status = "todo" +path = "libs/bimap/example/bimap_and_boost/xpressive.cpp" +case = "bimap/include_with_boost:example: xpressive" [[example]] -path = "libs/bimap/example/mi_to_b_path/bidirectional_map.cpp" -status = "todo" +path = "libs/bimap/example/mi_to_b_path/bidirectional_map.cpp" +case = "bimap/examples:example: bidirectional_map" [[example]] -path = "libs/bimap/example/mi_to_b_path/hashed_indices.cpp" -status = "todo" +path = "libs/bimap/example/mi_to_b_path/hashed_indices.cpp" +case = "bimap/examples:example: hashed_indices" [[example]] -path = "libs/bimap/example/mi_to_b_path/mi_bidirectional_map.cpp" -status = "todo" +path = "libs/bimap/example/mi_to_b_path/mi_bidirectional_map.cpp" +case = "bimap/examples:example: mi_bidirectional_map" [[example]] -path = "libs/bimap/example/mi_to_b_path/mi_hashed_indices.cpp" -status = "todo" +path = "libs/bimap/example/mi_to_b_path/mi_hashed_indices.cpp" +case = "bimap/examples_with_boost:example: mi_hashed_indices" [[example]] -path = "libs/bimap/example/mi_to_b_path/tagged_bidirectional_map.cpp" -status = "todo" +path = "libs/bimap/example/mi_to_b_path/tagged_bidirectional_map.cpp" +case = "bimap/examples:example: tagged_bidirectional_map" [[example]] -path = "libs/bimap/example/mighty_bimap.cpp" -status = "todo" +path = "libs/bimap/example/mighty_bimap.cpp" +case = "bimap/examples:example: mighty_bimap" [[example]] -path = "libs/bimap/example/population_bimap.cpp" -status = "todo" +path = "libs/bimap/example/population_bimap.cpp" +case = "bimap/examples_with_boost:example: population_bimap" [[example]] -path = "libs/bimap/example/projection.cpp" -status = "todo" +path = "libs/bimap/example/projection.cpp" +case = "bimap/examples:example: projection" [[example]] -path = "libs/bimap/example/repetitions_counter.cpp" -status = "todo" +path = "libs/bimap/example/repetitions_counter.cpp" +case = "bimap/examples:example: repetitions_counter" [[example]] -path = "libs/bimap/example/simple_bimap.cpp" -status = "todo" +path = "libs/bimap/example/simple_bimap.cpp" +case = "bimap/examples:example: simple_bimap" [[example]] -path = "libs/bimap/example/standard_map_comparison.cpp" -status = "todo" +path = "libs/bimap/example/standard_map_comparison.cpp" +case = "bimap/examples:example: standard_map_comparison" [[example]] -path = "libs/bimap/example/step_by_step.cpp" -status = "todo" +path = "libs/bimap/example/step_by_step.cpp" +case = "bimap/examples:example: step_by_step" [[example]] -path = "libs/bimap/example/tagged_simple_bimap.cpp" -status = "todo" +path = "libs/bimap/example/tagged_simple_bimap.cpp" +case = "bimap/examples:example: tagged_simple_bimap" [[example]] -path = "libs/bimap/example/tutorial_info_hook.cpp" -status = "todo" +path = "libs/bimap/example/tutorial_info_hook.cpp" +case = "bimap/examples:example: tutorial_info_hook" [[example]] -path = "libs/bimap/example/tutorial_modify_and_replace.cpp" -status = "todo" +path = "libs/bimap/example/tutorial_modify_and_replace.cpp" +case = "bimap/examples:example: tutorial_modify_and_replace" [[example]] -path = "libs/bimap/example/tutorial_range.cpp" -status = "todo" +path = "libs/bimap/example/tutorial_range.cpp" +case = "bimap/examples:example: tutorial_range" [[example]] -path = "libs/bimap/example/unconstrained_collection.cpp" -status = "todo" +path = "libs/bimap/example/unconstrained_collection.cpp" +case = "bimap/examples:example: unconstrained_collection" [[example]] -path = "libs/bimap/example/user_defined_names.cpp" -status = "todo" +path = "libs/bimap/example/user_defined_names.cpp" +case = "bimap/examples:example: user_defined_names" [[snippet]] doc = "libs/bimap/doc/bimap_and_boost.qbk#boost-typeof" title = "Boost.Typeof" -status = "todo" +case = "bimap/include_with_boost:example: typeof" +reason = "the section imports bimap_and_boost/typeof.cpp" [[snippet]] -doc = "libs/bimap/doc/quick_tutorial.qbk#one-minute-tutorial" -title = "One minute tutorial" -status = "todo" +doc = "libs/bimap/doc/quick_tutorial.qbk#one-minute-tutorial" +title = "One minute tutorial" +case = "bimap/api:doc: one-minute-tutorial" [[snippet]] -doc = "libs/bimap/doc/rationale.qbk#general-design" -title = "General Design" -status = "todo" +doc = "libs/bimap/doc/rationale.qbk#general-design" +title = "General Design" +case = "bimap/api:doc: general-design" [[snippet]] -doc = "libs/bimap/doc/rationale.qbk#the-student-and-the-mentor" -title = "The student and the mentor" -status = "todo" +doc = "libs/bimap/doc/rationale.qbk#the-student-and-the-mentor" +title = "The student and the mentor" +case = "bimap/api:doc: the-student-and-the-mentor" [[snippet]] -doc = "libs/bimap/doc/tutorial.qbk#bimap-mapping-framework" -title = "Bimap mapping framework" -status = "todo" +doc = "libs/bimap/doc/tutorial.qbk#bimap-mapping-framework" +title = "Bimap mapping framework" +case = "bimap/api:doc: bimap-mapping-framework" [[snippet]] -doc = "libs/bimap/doc/tutorial.qbk#bimaps-with-user-defined-names" -title = "Bimaps with user defined names" -status = "todo" +doc = "libs/bimap/doc/tutorial.qbk#bimaps-with-user-defined-names" +title = "Bimaps with user defined names" +case = "bimap/api:doc: bimaps-with-user-defined-names" [[snippet]] -doc = "libs/bimap/doc/tutorial.qbk#complete-instantiation-scheme" -title = "Complete instantiation scheme" -status = "todo" +doc = "libs/bimap/doc/tutorial.qbk#complete-instantiation-scheme" +title = "Complete instantiation scheme" +case = "bimap/api:doc: complete-instantiation-scheme" [[snippet]] doc = "libs/bimap/doc/tutorial.qbk#examples" title = "Examples" -status = "todo" +case = "bimap/examples_with_boost:example: population_bimap" +reason = "the section imports population_bimap.cpp (and repetitions_counter.cpp)" [[snippet]] doc = "libs/bimap/doc/tutorial.qbk#examples-2" title = "Examples" -status = "todo" +case = "bimap/examples_with_boost:example: population_bimap" +reason = "the section imports population_bimap.cpp (and repetitions_counter.cpp)" [[snippet]] -doc = "libs/bimap/doc/tutorial.qbk#freedom-of-choice" -title = "Freedom of choice" -status = "todo" +doc = "libs/bimap/doc/tutorial.qbk#freedom-of-choice" +title = "Freedom of choice" +case = "bimap/api:doc: freedom-of-choice" [[snippet]] -doc = "libs/bimap/doc/tutorial.qbk#insertion" -title = "Insertion" -status = "todo" +doc = "libs/bimap/doc/tutorial.qbk#insertion" +title = "Insertion" +case = "bimap/api:doc: insertion" [[snippet]] -doc = "libs/bimap/doc/tutorial.qbk#iterator-value-type" -title = "iterator::value_type" -status = "todo" +doc = "libs/bimap/doc/tutorial.qbk#iterator-value-type" +title = "iterator::value_type" +case = "bimap/api:doc: iterator-value-type" [[snippet]] -doc = "libs/bimap/doc/tutorial.qbk#operator-and-at" -title = "operator\\[\\] and at()" -status = "todo" +doc = "libs/bimap/doc/tutorial.qbk#operator-and-at" +title = "operator\\[\\] and at()" +case = "bimap/api:doc: operator-and-at" diff --git a/tests/bimap/custom_types.cpp b/tests/bimap/custom_types.cpp new file mode 100644 index 00000000..9d572533 --- /dev/null +++ b/tests/bimap/custom_types.cpp @@ -0,0 +1,89 @@ +// mcpplibs.mwrap.boost.bimap — user types on both sides: a user key with its +// own ordering and hashing, user comparators and hashers passed to the set +// types, user tags, user info payloads, modify/replace with user functors. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +import std; +import mcpplibs.mwrap.boost.bimap; +import mcpplibs.mwrap.boost.container_hash; + +using namespace boost::bimaps; + +namespace user { +struct sku { + std::string code; + auto operator<=>(const sku&) const = default; + friend std::size_t hash_value(const sku& s) { return boost::hash()(s.code); } +}; +struct by_length { + bool operator()(const std::string& a, const std::string& b) const { + return a.size() != b.size() ? a.size() < b.size() : a < b; + } +}; +struct ci_hash { + std::size_t operator()(const std::string& s) const { + std::string l(s); + for (auto& c : l) c = char(std::tolower(c)); + return std::hash()(l); + } +}; +struct ci_equal { + bool operator()(const std::string& a, const std::string& b) const { + return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), [](char x, char y) { + return std::tolower(x) == std::tolower(y); + }); + } +}; +struct product {}; +struct shelf {}; +struct stock { + int count = 0; +}; +} // namespace user + +TEST_CASE("user key types with operator< and hash_value") { + bimap, unordered_set_of> bm; + bm.insert({user::sku{"b"}, user::sku{"B"}}); + bm.insert({user::sku{"a"}, user::sku{"A"}}); + CHECK(bm.left.begin()->first.code == "a"); + CHECK(bm.right.at(user::sku{"B"}).code == "b"); +} + +TEST_CASE("user comparator and hasher in the set types") { + bimap, unordered_set_of> bm; + bm.insert({"ccc", "X"}); + bm.insert({"a", "y"}); + CHECK(bm.left.begin()->first == "a"); + CHECK(bm.right.count("x") == 1); // case-insensitive lookup + CHECK_FALSE(bm.insert({"zz", "Y"}).second); +} + +TEST_CASE("user tags and info payload") { + using inventory = bimap, tagged, with_info>; + inventory inv; + inv.insert(inventory::value_type("bolt", 7, user::stock{40})); + auto it = inv.by().find("bolt"); + REQUIRE(it != inv.by().end()); + it->info.count -= 5; + CHECK(inv.by().find(7)->info.count == 35); +} + +TEST_CASE("modify and replace with user functors") { + using bm_type = bimap; + bm_type bm; + bm.insert(bm_type::value_type(1, "one")); + bm.insert(bm_type::value_type(2, "two")); + struct shout { + void operator()(std::string& s) const { + for (auto& c : s) c = char(std::toupper(c)); + } + }; + auto it = bm.left.find(1); + CHECK(bm.left.modify_data(it, shout())); + CHECK(bm.left.at(1) == "ONE"); + CHECK(bm.left.replace_key(it, 10)); + CHECK(bm.left.count(10) == 1); + CHECK_FALSE(bm.left.replace_data(bm.left.find(2), "ONE")); // would collide on the right + CHECK(bm.left.modify_key(bm.left.find(2), _key = 20)); + CHECK(bm.left.count(20) == 1); +} diff --git a/tests/bimap/examples.cpp b/tests/bimap/examples.cpp new file mode 100644 index 00000000..e3f27349 --- /dev/null +++ b/tests/bimap/examples.cpp @@ -0,0 +1,1667 @@ +// mcpplibs.mwrap.boost.bimap — the official examples (libs/bimap/example/*.cpp, +// mi_to_b_path/*.cpp) ported mechanically: each program runs as a case and its +// output must equal what the original printed when built from upstream/boost +// alone. Interactive examples read a fixed stdin. population_bimap.cpp, +// mi_hashed_indices.cpp and the bimap_and_boost/ examples are +// examples_with_boost.cpp and include_with_boost.cpp. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +import std; +import mcpplibs.mwrap.boost.bimap; +import mcpplibs.mwrap.boost.tokenizer; + +// Output of the ported examples is captured here; each case compares it with +// what the original program printed (built from upstream/boost alone). +inline std::ostringstream ex_out; +inline std::istringstream ex_in; +inline char ex_arg0[] = "example"; +inline char* ex_argv[] = {ex_arg0, nullptr}; +template +int ex_printf(const char* fmt, A... a) { + int n = std::snprintf(nullptr, 0, fmt, a...); + std::string s(std::size_t(n) + 1, '\0'); + std::snprintf(s.data(), s.size(), fmt, a...); + s.resize(std::size_t(n)); + ex_out << s; + return n; +} +inline int ex_puts(const char* s) { + ex_out << s << '\n'; + return 0; +} + +// libs/bimap/example/at_function_examples.cpp +namespace ex_at_function_examples { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +void first_bimap() +{ + //[ code_at_function_first + + typedef bimap< set_of< std::string >, list_of< int > > bm_type; + bm_type bm; + + try + { + bm.left.at("one") = 1; // throws std::out_of_range + } + catch( std::out_of_range & e ) {} + + assert( bm.empty() ); + + bm.left["one"] = 1; // Ok + + assert( bm.left.at("one") == 1 ); // Ok + //] +} + +void second_bimap() +{ + //[ code_at_function_second + + typedef bimap< multiset_of, unordered_set_of > bm_type; + bm_type bm; + + //<- + /* + //-> + bm.right[1] = "one"; // compilation error + //<- + */ + //-> + + bm.right.insert( bm_type::right_value_type(1,"one") ); + + assert( bm.right.at(1) == "one" ); // Ok + + try + { + ex_out << bm.right.at(2); // throws std::out_of_range + } + catch( std::out_of_range & e ) {} + + //<- + /* + //-> + bm.right.at(1) = "1"; // compilation error + //<- + */ + //-> + + //] +} + +int example_main() +{ + first_bimap(); + second_bimap(); + return 0; +} + +TEST_CASE("example: at_function_examples") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_at_function_examples + +// libs/bimap/example/mi_to_b_path/bidirectional_map.cpp +namespace ex_bidirectional_map { +using namespace boost::bimaps; + +// A dictionary is a bidirectional map from strings to strings + +typedef bimap dictionary; +typedef dictionary::value_type translation; + +int example_main() +{ + dictionary d; + + // Fill up our microdictionary. + // first members Spanish, second members English. + + d.insert( translation("hola" ,"hello" )); + d.insert( translation("adios","goodbye")); + d.insert( translation("rosa" ,"rose" )); + d.insert( translation("mesa" ,"table" )); + + ex_out << "enter a word" << std::endl; + std::string word; + std::getline(ex_in,word); + + // search the queried word on the from index (Spanish) + + dictionary::left_const_iterator it = d.left.find(word); + + if( it != d.left.end() ) + { + // the second part of the element is the equivalent in English + + ex_out << word << " is said " + << it->second /*< `it` is an iterator of the left view, so + `it->second` refers to the right element of + the relation, the word in english >*/ + << " in English" << std::endl; + } + else + { + // word not found in Spanish, try our luck in English + + dictionary::right_const_iterator it2 = d.right.find(word); + if( it2 != d.right.end() ) + { + ex_out << word << " is said " + << it2->second /*< `it2` is an iterator of the right view, so + `it2->second` refers to the left element of + the relation, the word in spanish >*/ + << " in Spanish" << std::endl; + } + else + { + ex_out << "No such word in the dictionary" << std::endl; + } + } + + return 0; +} +//] + +TEST_CASE("example: bidirectional_map") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + ex_in.clear(); + ex_in.str(R"ex(hola +)ex"); // the program's stdin + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(enter a word +hola is said hello in English +)ex")); +} +} // namespace ex_bidirectional_map + +// libs/bimap/example/mi_to_b_path/hashed_indices.cpp +namespace ex_hashed_indices { +using namespace boost::bimaps; + +struct word {}; +struct occurrences {}; + +typedef bimap +< + + multiset_of< tagged, std::greater >, +unordered_set_of< tagged< std::string, word> > + +> word_counter; + +typedef boost::tokenizer > text_tokenizer; + +int example_main() +{ + + std::string text= + "Relations between data in the STL are represented with maps." + "A map is a directed relation, by using it you are representing " + "a mapping. In this directed relation, the first type is related to " + "the second type but it is not true that the inverse relationship " + "holds. This is useful in a lot of situations, but there are some " + "relationships that are bidirectional by nature."; + + // feed the text into the container + + word_counter wc; + text_tokenizer tok(text,boost::char_separator(" \t\n.,;:!?'\"-")); + unsigned int total_occurrences = 0; + + for( text_tokenizer::const_iterator it = tok.begin(), it_end = tok.end(); + it != it_end ; ++it ) + { + ++total_occurrences; + + word_counter::map_by::iterator wit = + wc.by().insert( + word_counter::map_by::value_type(0,*it) + ).first; + + wc.by().modify_key( wit, ++_key); + } + + // list words by frequency of appearance + + ex_out << std::fixed << std::setprecision(2); + + for( word_counter::map_by::const_iterator + wit = wc.by().begin(), + wit_end = wc.by().end(); + + wit != wit_end; ++wit ) + { + ex_out << std::setw(15) << wit->get() << ": " + << std::setw(5) + << 100.0 * wit->get() / total_occurrences << "%" + << std::endl; + } + + return 0; +} +//] + +TEST_CASE("example: hashed_indices") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex( the: 6.15% + is: 6.15% + are: 6.15% + a: 4.62% + directed: 3.08% + relation: 3.08% + type: 3.08% + it: 3.08% + in: 3.08% + but: 3.08% + that: 3.08% + by: 3.08% + Relations: 1.54% + between: 1.54% + data: 1.54% + STL: 1.54% + represented: 1.54% + with: 1.54% + maps: 1.54% + A: 1.54% + map: 1.54% + using: 1.54% + you: 1.54% + representing: 1.54% + mapping: 1.54% + In: 1.54% + this: 1.54% + first: 1.54% + related: 1.54% + to: 1.54% + second: 1.54% + not: 1.54% + true: 1.54% + inverse: 1.54% + relationship: 1.54% + holds: 1.54% + This: 1.54% + useful: 1.54% + lot: 1.54% + of: 1.54% + situations: 1.54% + there: 1.54% + some: 1.54% + relationships: 1.54% + bidirectional: 1.54% + nature: 1.54% +)ex")); +} +} // namespace ex_hashed_indices + +// libs/bimap/example/mi_to_b_path/mi_bidirectional_map.cpp +namespace ex_mi_bidirectional_map { +//[ code_mi_to_b_path_mi_bidirectional_map + + + +using namespace boost; +using namespace boost::multi_index; + +// tags for accessing both sides of a bidirectional map + +struct from {}; +struct to {}; + +// The class template bidirectional_map wraps the specification +// of a bidirectional map based on multi_index_container. + +template +struct bidirectional_map +{ + typedef std::pair value_type; + + typedef multi_index_container< + value_type, + indexed_by + < + ordered_unique + < + tag, member + >, + ordered_unique + < + tag, member + > + > + + > type; + +}; + +// A dictionary is a bidirectional map from strings to strings + +typedef bidirectional_map::type dictionary; + +int example_main() +{ + dictionary d; + + // Fill up our microdictionary. + // first members Spanish, second members English. + + d.insert(dictionary::value_type("hola","hello")); + d.insert(dictionary::value_type("adios","goodbye")); + d.insert(dictionary::value_type("rosa","rose")); + d.insert(dictionary::value_type("mesa","table")); + + ex_out << "enter a word" << std::endl; + std::string word; + std::getline(ex_in,word); + + // search the queried word on the from index (Spanish) + + dictionary::iterator it = d.get().find(word); + + if( it != d.end() ) + { + // the second part of the element is the equivalent in English + + ex_out << word << " is said " + << it->second << " in English" << std::endl; + } + else + { + // word not found in Spanish, try our luck in English + + dictionary::index_iterator::type it2 = d.get().find(word); + if( it2 != d.get().end() ) + { + ex_out << word << " is said " + << it2->first << " in Spanish" << std::endl; + } + else + { + ex_out << "No such word in the dictionary" << std::endl; + } + } + + return 0; +} +//] + +TEST_CASE("example: mi_bidirectional_map") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + ex_in.clear(); + ex_in.str(R"ex(table +)ex"); // the program's stdin + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(enter a word +table is said mesa in Spanish +)ex")); +} +} // namespace ex_mi_bidirectional_map + +// libs/bimap/example/mi_to_b_path/tagged_bidirectional_map.cpp +namespace ex_tagged_bidirectional_map { +using namespace boost::bimaps; + +// tags + +struct spanish {}; +struct english {}; + +// A dictionary is a bidirectional map from strings to strings + +typedef bimap +< + tagged< std::string,spanish >, tagged< std::string,english > + +> dictionary; + +typedef dictionary::value_type translation; + +int example_main() +{ + dictionary d; + + // Fill up our microdictionary. + // first members Spanish, second members English. + + d.insert( translation("hola" ,"hello" )); + d.insert( translation("adios","goodbye")); + d.insert( translation("rosa" ,"rose" )); + d.insert( translation("mesa" ,"table" )); + + ex_out << "enter a word" << std::endl; + std::string word; + std::getline(ex_in,word); + + // search the queried word on the from index (Spanish) */ + + dictionary::map_by::const_iterator it = + d.by().find(word); + + if( it != d.by().end() ) + { + ex_out << word << " is said " + << it->get() << " in English" << std::endl; + } + else + { + // word not found in Spanish, try our luck in English + + dictionary::map_by::const_iterator it2 = + d.by().find(word); + + if( it2 != d.by().end() ) + { + ex_out << word << " is said " + << it2->get() << " in Spanish" << std::endl; + } + else + { + ex_out << "No such word in the dictionary" << std::endl; + } + } + + return 0; +} +//] + +TEST_CASE("example: tagged_bidirectional_map") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + ex_in.clear(); + ex_in.str(R"ex(goodbye +)ex"); // the program's stdin + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(enter a word +goodbye is said adios in Spanish +)ex")); +} +} // namespace ex_tagged_bidirectional_map + +// libs/bimap/example/mighty_bimap.cpp +namespace ex_mighty_bimap { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- +// This is the translator example from the tutorial. +// In this example the set type of relation is changed to allow the iteration +// of the container. + + +//[ code_mighty_bimap + + +struct english {}; +struct spanish {}; + +int example_main() +{ + using namespace boost::bimaps; + + typedef bimap + < + unordered_set_of< tagged< std::string, spanish > >, + unordered_set_of< tagged< std::string, english > >, + list_of_relation + + > translator; + + translator trans; + + // We have to use `push_back` because the collection of relations is + // a `list_of_relation` + + trans.push_back( translator::value_type("hola" ,"hello" ) ); + trans.push_back( translator::value_type("adios" ,"goodbye" ) ); + trans.push_back( translator::value_type("rosa" ,"rose" ) ); + trans.push_back( translator::value_type("mesa" ,"table" ) ); + + ex_out << "enter a word" << std::endl; + std::string word; + std::getline(ex_in,word); + + // Search the queried word on the from index (Spanish) + + translator::map_by::const_iterator is + = trans.by().find(word); + + if( is != trans.by().end() ) + { + ex_out << word << " is said " + << is->get() + << " in English" << std::endl; + } + else + { + // Word not found in Spanish, try our luck in English + + translator::map_by::const_iterator ie + = trans.by().find(word); + + if( ie != trans.by().end() ) + { + ex_out << word << " is said " + << ie->get() + << " in Spanish" << std::endl; + } + else + { + // Word not found, show the possible translations + + ex_out << "No such word in the dictionary" << std::endl; + ex_out << "These are the possible translations" << std::endl; + + for( translator::const_iterator + i = trans.begin(), + i_end = trans.end(); + + i != i_end ; ++i ) + { + ex_out << i->get() + << " <---> " + << i->get() + << std::endl; + } + } + } + return 0; +} +//] + +TEST_CASE("example: mighty_bimap") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + ex_in.clear(); + ex_in.str(R"ex(hola +)ex"); // the program's stdin + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(enter a word +hola is said hello in English +)ex")); +} +} // namespace ex_mighty_bimap + +// libs/bimap/example/projection.cpp +namespace ex_projection { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + +using namespace boost::bimaps; + +void years_example() +{ + //[ code_projection_years + + typedef bimap > > bm_type; + + bm_type bm; + bm.insert( bm_type::value_type("John" ,34) ); + bm.insert( bm_type::value_type("Peter",24) ); + bm.insert( bm_type::value_type("Mary" ,12) ); + + // Find the name of the next younger person after Peter + + bm_type::left_const_iterator name_iter = bm.left.find("Peter"); + + bm_type::right_const_iterator years_iter = bm.project_right(name_iter); + + ++years_iter; + + ex_out << "The next younger person after Peter is " << years_iter->second; + //] +} + +int example_main() +{ + years_example(); + + return 0; +} + +TEST_CASE("example: projection") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(The next younger person after Peter is Mary)ex")); +} +} // namespace ex_projection + +// libs/bimap/example/repetitions_counter.cpp +namespace ex_repetitions_counter { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +struct counter { + counter() : c(0) {} + counter& operator++() { ++c; return *this; } + unsigned int operator++(int) { return c++; } + operator const unsigned int() const { return c; } + private: + unsigned int c; +}; + +int example_main() +{ + //[ code_repetitions_counter + + typedef bimap + < + unordered_set_of< std::string >, + list_of< counter > /*< `counter` is an integer that is initialized + in zero in the constructor >*/ + + > word_counter; + + typedef boost::tokenizer > text_tokenizer; + + std::string text= + "Relations between data in the STL are represented with maps." + "A map is a directed relation, by using it you are representing " + "a mapping. In this directed relation, the first type is related to " + "the second type but it is not true that the inverse relationship " + "holds. This is useful in a lot of situations, but there are some " + "relationships that are bidirectional by nature."; + + // feed the text into the container + word_counter wc; + text_tokenizer tok(text,boost::char_separator(" \t\n.,;:!?'\"-")); + + for( text_tokenizer::const_iterator it = tok.begin(), it_end = tok.end(); + it != it_end ; ++it ) + { + /*<< Because the right collection type is `list_of`, the right data + is not used a key and can be modified in the same way as with + standard maps. >>*/ + ++ wc.left[*it]; + } + + // list words with counters by order of appearance + /*<< When we insert the elements using the left map view, the element + is inserted at the end of the list. >>*/ + for( word_counter::right_const_iterator + wit = wc.right.begin(), wit_end = wc.right.end(); + + wit != wit_end; ++wit ) + { + ex_out << wit->second << ": " << wit->first; + } + //] + + return 0; +} + +TEST_CASE("example: repetitions_counter") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(Relations: 1between: 1data: 1in: 2the: 4STL: 1are: 4represented: 1with: 1maps: 1A: 1map: 1is: 4a: 3directed: 2relation: 2by: 2using: 1it: 2you: 1representing: 1mapping: 1In: 1this: 1first: 1type: 2related: 1to: 1second: 1but: 2not: 1true: 1that: 2inverse: 1relationship: 1holds: 1This: 1useful: 1lot: 1of: 1situations: 1there: 1some: 1relationships: 1bidirectional: 1nature: 1)ex")); +} +} // namespace ex_repetitions_counter + +// libs/bimap/example/simple_bimap.cpp +namespace ex_simple_bimap { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + +//[ code_simple_bimap + + + +template< class MapType > +void print_map(const MapType & map, + const std::string & separator, + std::ostream & os ) +{ + typedef typename MapType::const_iterator const_iterator; + + for( const_iterator i = map.begin(), iend = map.end(); i != iend; ++i ) + { + os << i->first << separator << i->second << std::endl; + } +} + +int example_main() +{ + // Soccer World cup + + typedef boost::bimap< std::string, int > results_bimap; + typedef results_bimap::value_type position; + + results_bimap results; + results.insert( position("Argentina" ,1) ); + results.insert( position("Spain" ,2) ); + results.insert( position("Germany" ,3) ); + results.insert( position("France" ,4) ); + + ex_out << "The number of countries is " << results.size() + << std::endl; + + ex_out << "The winner is " << results.right.at(1) + << std::endl + << std::endl; + + ex_out << "Countries names ordered by their final position:" + << std::endl; + + // results.right works like a std::map< int, std::string > + + print_map( results.right, ") ", ex_out ); + + ex_out << std::endl + << "Countries names ordered alphabetically along with" + "their final position:" + << std::endl; + + // results.left works like a std::map< std::string, int > + + print_map( results.left, " ends in position ", ex_out ); + + return 0; +} +//] + +TEST_CASE("example: simple_bimap") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(The number of countries is 4 +The winner is Argentina + +Countries names ordered by their final position: +1) Argentina +2) Spain +3) Germany +4) France + +Countries names ordered alphabetically along withtheir final position: +Argentina ends in position 1 +France ends in position 4 +Germany ends in position 3 +Spain ends in position 2 +)ex")); +} +} // namespace ex_simple_bimap + +// libs/bimap/example/standard_map_comparison.cpp +namespace ex_standard_map_comparison { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +//[ code_standard_map_comparison + +template< class Map, class CompatibleKey, class CompatibleData > +void use_it( Map & m, + const CompatibleKey & key, + const CompatibleData & data ) +{ + typedef typename Map::value_type value_type; + typedef typename Map::const_iterator const_iterator; + + m.insert( value_type(key,data) ); + const_iterator iter = m.find(key); + if( iter != m.end() ) + { + assert( iter->first == key ); + assert( iter->second == data ); + + ex_out << iter->first << " --> " << iter->second; + } + m.erase(key); +} + +int example_main() +{ + typedef bimap< set_of, set_of > bimap_type; + bimap_type bm; + + // Standard map + { + typedef std::map< std::string, int > map_type; + map_type m; + + use_it( m, "one", 1 ); + } + + // Left map view + { + typedef bimap_type::left_map map_type; + map_type & m = bm.left; + + use_it( m, "one", 1 ); + } + + // Reverse standard map + { + typedef std::map< int, std::string > reverse_map_type; + reverse_map_type rm; + + use_it( rm, 1, "one" ); + } + + // Right map view + { + typedef bimap_type::right_map reverse_map_type; + reverse_map_type & rm = bm.right; + + use_it( rm, 1, "one" ); + } + + return 0; +} +//] + +TEST_CASE("example: standard_map_comparison") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(one --> 1one --> 11 --> one1 --> one)ex")); +} +} // namespace ex_standard_map_comparison + +// libs/bimap/example/step_by_step.cpp +namespace ex_step_by_step { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + +// A convenience header is available in the boost directory: + +int example_main() +{ + //[ code_step_by_step_definition + + typedef boost::bimap< int, std::string > bm_type; + bm_type bm; + //] + + //[ code_step_by_step_set_of_relations_view + + bm.insert( bm_type::value_type(1, "one" ) ); + bm.insert( bm_type::value_type(2, "two" ) ); + + ex_out << "There are " << bm.size() << "relations" << std::endl; + + for( bm_type::const_iterator iter = bm.begin(), iend = bm.end(); + iter != iend; ++iter ) + { + // iter->left : data : int + // iter->right : data : std::string + + ex_out << iter->left << " <--> " << iter->right << std::endl; + } + //] + + //[ code_step_by_step_left_map_view + + /*<< The type of `bm.left` is `bm_type::left_map` and the type + of `bm.right` is `bm_type::right_map` >>*/ + typedef bm_type::left_map::const_iterator left_const_iterator; + + for( left_const_iterator left_iter = bm.left.begin(), iend = bm.left.end(); + left_iter != iend; ++left_iter ) + { + // left_iter->first : key : int + // left_iter->second : data : std::string + + ex_out << left_iter->first << " --> " << left_iter->second << std::endl; + } + + /*<< `bm_type::left_`\ -type- can be used as a shortcut for the more verbose + `bm_type::left_map::`\ -type- >>*/ + bm_type::left_const_iterator left_iter = bm.left.find(2); + assert( left_iter->second == "two" ); + + /*<< This line produces the same effect of + `bm.insert( bm_type::value_type(3,"three") );` >>*/ + bm.left.insert( bm_type::left_value_type( 3, "three" ) ); + //] + + + + //[ code_step_by_step_right_map_view + + bm_type::right_const_iterator right_iter = bm.right.find("two"); + + // right_iter->first : key : std::string + // right_iter->second : data : int + + assert( right_iter->second == 2 ); + + assert( bm.right.at("one") == 1 ); + + bm.right.erase("two"); + + /*<< This line produces the same effect of + `bm.insert( bm_type::value_type(4,"four") );` >>*/ + bm.right.insert( bm_type::right_value_type( "four", 4 ) ); + //] + + return 0; +} + +TEST_CASE("example: step_by_step") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(There are 2relations +1 <--> one +2 <--> two +1 --> one +2 --> two +)ex")); +} +} // namespace ex_step_by_step + +// libs/bimap/example/tagged_simple_bimap.cpp +namespace ex_tagged_simple_bimap { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + +//[ code_tagged_simple_bimap + + + +struct country {}; +struct place {}; + +int example_main() +{ + using namespace boost::bimaps; + + // Soccer World cup. + + typedef bimap + < + tagged< std::string, country >, + tagged< int , place > + + > results_bimap; + + typedef results_bimap::value_type position; + + results_bimap results; + results.insert( position("Argentina" ,1) ); + results.insert( position("Spain" ,2) ); + results.insert( position("Germany" ,3) ); + results.insert( position("France" ,4) ); + + ex_out << "Countries names ordered by their final position:" + << std::endl; + + /*<< `results.by()` is equivalent to `results.right` >>*/ + for( results_bimap::map_by::const_iterator + i = results.by().begin(), + iend = results.by().end() ; + i != iend; ++i ) + { + /*<< `get` works for each view of the bimap >>*/ + ex_out << i->get() << ") " + << i->get() << std::endl; + } + + ex_out << std::endl + << "Countries names ordered alfabetically along with" + "their final position:" + << std::endl; + + /*<< `results.by()` is equivalent to `results.left` >>*/ + for( results_bimap::map_by::const_iterator + i = results.by().begin(), + iend = results.by().end() ; + i != iend; ++i ) + { + ex_out << i->get() << " ends " + << i->get() << "º" + << std::endl; + } + + return 0; +} +//] + +TEST_CASE("example: tagged_simple_bimap") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(Countries names ordered by their final position: +1) Argentina +2) Spain +3) Germany +4) France + +Countries names ordered alfabetically along withtheir final position: +Argentina ends 1º +France ends 4º +Germany ends 3º +Spain ends 2º +)ex")); +} +} // namespace ex_tagged_simple_bimap + +// libs/bimap/example/tutorial_info_hook.cpp +namespace ex_tutorial_info_hook { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + + +void tutorial_about_info_hook() +{ + //[ code_tutorial_info_hook_first + + typedef bimap< + + multiset_of< std::string >, // author + set_of< std::string >, // title + + with_info< std::string > // abstract + + > bm_type; + typedef bm_type::value_type book; + + bm_type bm; + + bm.insert( + + book( "Bjarne Stroustrup" , "The C++ Programming Language", + + "For C++ old-timers, the first edition of this book is" + "the one that started it all—the font of our knowledge." ) + ); + + + // Print the author of the bible + ex_out << bm.right.at("The C++ Programming Language"); + + // Print the abstract of this book + bm_type::left_iterator i = bm.left.find("Bjarne Stroustrup"); + ex_out << i->info; + //] + + // Contrary to the two key types, the information will be mutable + // using iterators. + + //[ code_tutorial_info_hook_mutable + + i->info += "More details about this book"; + //] + + // A new function is included in unique map views: info_at(key), that + // mimics the standard at(key) function but returned the associated + // information instead of the data. + + //[ code_tutorial_info_hook_info_at + + // Print the new abstract + ex_out << bm.right.info_at("The C++ Programming Language"); + //] +} + +struct author {}; +struct title {}; +struct abstract {}; + +void tutorial_about_tagged_info_hook() +{ + //[ code_tutorial_info_hook_tagged_info + + typedef bimap< + + multiset_of< tagged< std::string, author > >, + set_of< tagged< std::string, title > >, + + with_info< tagged< std::string, abstract > > + + > bm_type; + typedef bm_type::value_type book; + + bm_type bm; + + bm.insert( + + book( "Bjarne Stroustrup" , "The C++ Programming Language", + + "For C++ old-timers, the first edition of this book is" + "the one that started it all—the font of our knowledge." ) + ); + + // Print the author of the bible + ex_out << bm.by().at("The C++ Programming Language"); + + // Print the abstract of this book + bm_type::map_by<author>::iterator i = bm.by<author>().find("Bjarne Stroustrup"); + ex_out << i->get<abstract>(); + + // Contrary to the two key types, the information will be mutable + // using iterators. + + i->get<abstract>() += "More details about this book"; + + // Print the new abstract + ex_out << bm.by<title>().info_at("The C++ Programming Language"); + //] +} + + +void bimap_without_an_info_hook() +{ + //[ code_tutorial_info_hook_nothing + + typedef bimap< + + multiset_of< std::string >, // author + set_of< std::string > // title + + > bm_type; + typedef bm_type::value_type book; + + bm_type bm; + + bm.insert( book( "Bjarne Stroustrup" , "The C++ Programming Language" ) ); + bm.insert( book( "Scott Meyers" , "Effective C++" ) ); + bm.insert( book( "Andrei Alexandrescu" , "Modern C++ Design" ) ); + + // Print the author of Modern C++ + ex_out << bm.right.at( "Modern C++ Design" ); + //] +} + + +int example_main() +{ + tutorial_about_info_hook(); + tutorial_about_tagged_info_hook(); + bimap_without_an_info_hook(); + + return 0; +} + +TEST_CASE("example: tutorial_info_hook") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(Bjarne StroustrupFor C++ old-timers, the first edition of this book isthe one that started it all—the font of our knowledge.For C++ old-timers, the first edition of this book isthe one that started it all—the font of our knowledge.More details about this bookBjarne StroustrupFor C++ old-timers, the first edition of this book isthe one that started it all—the font of our knowledge.For C++ old-timers, the first edition of this book isthe one that started it all—the font of our knowledge.More details about this bookAndrei Alexandrescu)ex")); +} +} // namespace ex_tutorial_info_hook + +// libs/bimap/example/tutorial_modify_and_replace.cpp +namespace ex_tutorial_modify_and_replace { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +void test_replace() +{ + //[ code_tutorial_replace + + typedef bimap< int, std::string > bm_type; + bm_type bm; + + bm.insert( bm_type::value_type(1,"one") ); + + // Replace (1,"one") with (1,"1") using the right map view + { + bm_type::right_iterator it = bm.right.find("one"); + + bool successful_replace = bm.right.replace_key( it, "1" ); + + assert( successful_replace ); + } + + bm.insert( bm_type::value_type(2,"two") ); + + // Fail to replace (1,"1") with (1,"two") using the left map view + { + assert( bm.size() == 2 ); + + bm_type::left_iterator it = bm.left.find(1); + + bool successful_replace = bm.left.replace_data( it, "two" ); + + /*<< `it` is still valid here, and the bimap was left unchanged >>*/ + assert( ! successful_replace ); + assert( bm.size() == 2 ); + } + //] +} + +void test_modify() +{ + //[ code_tutorial_modify + + typedef bimap< int, std::string > bm_type; + bm_type bm; + bm.insert( bm_type::value_type(1,"one") ); + + // Modify (1,"one") to (1,"1") using the right map view + { + bm_type::right_iterator it = bm.right.find("one"); + + bool successful_modify = bm.right.modify_key( it , _key = "1" ); + + assert( successful_modify ); + } + + bm.insert( bm_type::value_type(2,"two") ); + + // Fail to modify (1,"1") to (1,"two") using the left map view + { + assert( bm.size() == 2 ); + + bm_type::left_iterator it = bm.left.find(1); + + bool successful_modify = bm.left.modify_data( it, _data = "two" ); + + /*<< `it` is not longer valid and `(1,"1")` is removed from the bimap >>*/ + assert( ! successful_modify ); + assert( bm.size() == 1 ); + } + //] + + /* + // Modify (2,"two") to (3,"two") using the set of relations view + { + bm_type::iterator it = bm.begin(); + + bool successful_modify = bm.modify( it, ++_left ); + + assert( successful_modify ); + } + */ +} + +int example_main() +{ + test_replace(); + test_modify(); + return 0; +} + +TEST_CASE("example: tutorial_modify_and_replace") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_tutorial_modify_and_replace + +// libs/bimap/example/tutorial_range.cpp +namespace ex_tutorial_range { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +void using_upper_and_lower_bound() +{ + //[ code_tutorial_range_standard_way + + typedef bimap<int,std::string> bm_type; + bm_type bm; + + // ... + + bm_type::left_iterator iter_first = bm.left.lower_bound(20); + bm_type::left_iterator iter_second = bm.left.upper_bound(50); + + // range [iter_first,iter_second) contains the elements in [20,50] + //] + + // Subtle changes + { + //[ code_tutorial_range_standard_way_subtle_changes + + bm_type::left_iterator iter_first = bm.left.upper_bound(20); + bm_type::left_iterator iter_second = bm.left.lower_bound(50); + + // range [iter_first,iter_second) contains the elements in (20,50) + //] + } +} + +void using_range() +{ + //[ code_tutorial_range + + typedef bimap<int,std::string> bm_type; + bm_type bm; + + // ... + + /*<< `range_type` is a handy typedef equal to `std::pair<iterator,iterator>`. + `const_range_type` is provided too, and it is equal to + `std::pair<const_iterator,const_iterator>` >>*/ + bm_type::left_range_type r; + + /*<< _key is a Boost.Lambda placeholder. To use it you have to include + `<boost/bimap/support/lambda.hpp>` >>*/ + r = bm.left.range( 20 <= _key, _key <= 50 ); // [20,50] + + r = bm.left.range( 20 < _key, _key < 50 ); // (20,50) + + r = bm.left.range( 20 <= _key, _key < 50 ); // [20,50) + //] + + //[ code_tutorial_range_unbounded + + r = bm.left.range( 20 <= _key, unbounded ); // [20,inf) + + r = bm.left.range( unbounded , _key < 50 ); // (-inf,50) + + /*<< This is equivalent to std::make_pair(s.begin(),s.end()) >>*/ + r = bm.left.range( unbounded , unbounded ); // (-inf,inf) + //] +} + +int example_main() +{ + using_upper_and_lower_bound(); + using_range(); + + return 0; +} + +TEST_CASE("example: tutorial_range") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_tutorial_range + +// libs/bimap/example/unconstrained_collection.cpp +namespace ex_unconstrained_collection { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +int example_main() +{ + // Boost.Bimap + { + //[ code_unconstrained_collection_bimap + + typedef bimap< std::string, unconstrained_set_of<int> > bm_type; + typedef bm_type::left_map map_type; + + bm_type bm; + map_type & m = bm.left; + //] + + //[ code_unconstrained_collection_common + + m["one"] = 1; + + assert( m.find("one") != m.end() ); + + for( map_type::iterator i = m.begin(), iend = m.end(); i != iend; ++i ) + { + /*<< The right collection of the bimap is mutable so its elements + can be modified using iterators. >>*/ + ++(i->second); + } + + m.erase("one"); + //] + + m["one"] = 1; + m["two"] = 2; + + //[ code_unconstrained_collection_only_for_bimap + typedef map_type::const_iterator const_iterator; + typedef std::pair<const_iterator,const_iterator> const_range; + + /*<< This range is a model of BidirectionalRange, read the docs of + Boost.Range for more information. >>*/ + const_range r = m.range( "one" <= _key, _key <= "two" ); + for( const_iterator i = r.first; i != r.second; ++i ) + { + ex_out << i->first << "-->" << i->second << std::endl; + } + + m.modify_key( m.begin(), _key = "1" ); + //] + } + + // Standard map + { + //[ code_unconstrained_collection_map + + typedef std::map< std::string, int > map_type; + + map_type m; + //] + } + + return 0; +} + +TEST_CASE("example: unconstrained_collection") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(one-->1 +two-->2 +)ex")); +} +} // namespace ex_unconstrained_collection + +// libs/bimap/example/user_defined_names.cpp +namespace ex_user_defined_names { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +void untagged_version() +{ + //[ code_user_defined_names_untagged_version + + typedef bimap + < + multiset_of<std::string>, + int + + > People; + + People people; + + // ... + + int user_id; + ex_in >> user_id; + + // people.right : map<id,name> + + People::right_const_iterator id_iter = people.right.find(user_id); + if( id_iter != people.right.end() ) + { + // first : id + // second : name + + ex_out << "name: " << id_iter->second << std::endl + << "id: " << id_iter->first << std::endl; + } + else + { + ex_out << "Unknown id, users are:" << std::endl; + + // people.left : map<name,id> + + for( People::left_const_iterator + name_iter = people.left.begin(), + iend = people.left.end(); + + name_iter != iend; ++name_iter ) + { + // first : name + // second : id + + ex_out << "name: " << name_iter->first << std::endl + << "id: " << name_iter->second << std::endl; + } + } + //] +} + +struct id {}; +struct name {}; + +void tagged_version() +{ + //[ code_user_defined_names_tagged_version + + //<- + /* + //-> + struct id {}; // Tag for the identification number + struct name {}; // Tag for the name of the person + //<- + */ + //-> + + typedef bimap + < + tagged< int , id > , + multiset_of< tagged< std::string, name > > + + > People; + + People people; + + // ... + + int user_id; + ex_in >> user_id; + + People::map_by<id>::const_iterator id_iter = people.by<id>().find(user_id); + if( id_iter != people.by<id>().end() ) + { + ex_out << "name: " << id_iter->get<name>() << std::endl + << "id: " << id_iter->get<id>() << std::endl; + } + else + { + ex_out << "Unknown id, users are:" << std::endl; + + for( People::map_by<name>::const_iterator + name_iter = people.by<name>().begin(), + iend = people.by<name>().end(); + + name_iter != iend; ++name_iter ) + { + ex_out << "name: " << name_iter->get<name>() << std::endl + << "id: " << name_iter->get<id>() << std::endl; + } + } + //] +} + +int example_main() +{ + untagged_version(); + tagged_version(); + + return 0; +} + +TEST_CASE("example: user_defined_names") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + ex_in.clear(); + ex_in.str(R"ex(1 +2 +)ex"); // the program's stdin + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(Unknown id, users are: +Unknown id, users are: +)ex")); +} +} // namespace ex_user_defined_names diff --git a/tests/bimap/examples_with_boost.cpp b/tests/bimap/examples_with_boost.cpp new file mode 100644 index 00000000..9618b95d --- /dev/null +++ b/tests/bimap/examples_with_boost.cpp @@ -0,0 +1,623 @@ +// mcpplibs.mwrap.boost.bimap — the official examples that combine Boost.Bimap +// with other libraries through their modules (bimap_and_boost/{assign, +// lambda,property_map,range,serialization}.cpp, population_bimap.cpp, +// mi_to_b_path/mi_hashed_indices.cpp), with the same output oracle as +// examples.cpp. Two programs are edited before porting (and the oracle runs +// the edited program): population_bimap spells boost::optional (the unqualified +// name is ambiguous with std::optional since C++17, upstream too) and writes +// its BOOST_FOREACH loops over an iterator pair as range-for over +// boost::make_iterator_range; mi_hashed_indices writes +// BOOST_MULTI_INDEX_MEMBER(T, type, m) as the member<T, type, &T::m> it +// expands to. The foreach, typeof and xpressive examples are about those +// macro libraries: include_with_boost.cpp. +// range.cpp also writes using namespace boost::placeholders (see there), and +// serialization.cpp archives to the temporary directory instead of ./data. +// The range module exports boost::count_if, which range.cpp's own count_if +// meets through `using namespace boost` (the original includes only the range +// headers it needs); the port calls the example's count_if qualified. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdio> +#include <cassert> +import std; +import mcpplibs.mwrap.boost.bimap; +import mcpplibs.mwrap.boost.assign; +import mcpplibs.mwrap.boost.bind; +import mcpplibs.mwrap.boost.lambda; +import mcpplibs.mwrap.boost.property_map; +import mcpplibs.mwrap.boost.range; +import mcpplibs.mwrap.boost.serialization; +import mcpplibs.mwrap.boost.multi_index; +import mcpplibs.mwrap.boost.tokenizer; +import mcpplibs.mwrap.boost.optional; + +// Output of the ported examples is captured here; each case compares it with +// what the original program printed (built from upstream/boost alone). +inline std::ostringstream ex_out; +inline std::istringstream ex_in; +inline char ex_arg0[] = "example"; +inline char* ex_argv[] = {ex_arg0, nullptr}; +template <class... A> +int ex_printf(const char* fmt, A... a) { + int n = std::snprintf(nullptr, 0, fmt, a...); + std::string s(std::size_t(n) + 1, '\0'); + std::snprintf(s.data(), s.size(), fmt, a...); + s.resize(std::size_t(n)); + ex_out << s; + return n; +} +inline int ex_puts(const char* s) { + ex_out << s << '\n'; + return 0; +} + +// libs/bimap/example/population_bimap.cpp +namespace ex_population_bimap { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + + +using namespace boost::bimaps; +using namespace boost; +using namespace std; + +int example_main() +{ + { + + typedef bimap< + + string, + multiset_of< boost::optional<string> > + + > bm_type; + + bm_type bm; + + assign::insert( bm ) + + ( "John" , string("lazarus" ) ) + ( "Peter", string("vinicius") ) + ( "Simon", string("vinicius") ) + ( "Brian", none ) + ; + + ex_out << "John is working in " + << bm.left.at( "John" ).get_value_or( "no project" ) + << endl; + + ex_out << "Project vinicius is being developed by " << endl; + for( bm_type::right_reference rp : boost::make_iterator_range( bm.right.equal_range( std::string("vinicius") ) ) ) + { + ex_out << rp.second << endl; + } + + ex_out << "This workers need a project " << endl; + for( bm_type::right_reference rp : boost::make_iterator_range( bm.right.equal_range(none) ) ) + { + ex_out << rp.second << endl; + } + +} + + //[ code_population_bimap + + typedef bimap< + + unordered_set_of< std::string >, + multiset_of< long, std::greater<long> > + + > population_bimap; + + typedef population_bimap::value_type population; + + population_bimap pop; + pop.insert( population("China", 1321000000) ); + pop.insert( population("India", 1129000000) ); + pop.insert( population("United States", 301950000) ); + pop.insert( population("Indonesia", 234950000) ); + pop.insert( population("Brazil", 186500000) ); + pop.insert( population("Pakistan", 163630000) ); + + ex_out << "Countries by their population:" << std::endl; + + // First requirement + /*<< The right map view works like a + `std::multimap< long, std::string, std::greater<long> >`, + We can iterate over it to print the results in the required order. >>*/ + for( population_bimap::right_const_iterator + i = pop.right.begin(), iend = pop.right.end(); + i != iend ; ++i ) + { + ex_out << i->second << " with " << i->first << std::endl; + } + + // Second requirement + /*<< The left map view works like a `std::unordered_map< std::string, long >`, + given the name of the country we can use it to search for the population + in constant time >>*/ + ex_out << "Population of China: " << pop.left.at("China") << std::endl; + //] + + return 0; +} + +TEST_CASE("example: population_bimap") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(John is working in lazarus +Project vinicius is being developed by +Peter +Simon +This workers need a project +Brian +Countries by their population: +China with 1321000000 +India with 1129000000 +United States with 301950000 +Indonesia with 234950000 +Brazil with 186500000 +Pakistan with 163630000 +Population of China: 1321000000 +)ex")); +} +} // namespace ex_population_bimap + +// libs/bimap/example/mi_to_b_path/mi_hashed_indices.cpp +namespace ex_mi_hashed_indices { +//[ code_mi_to_b_path_mi_hashed_indices + + + + +using namespace boost::multi_index; +namespace bl = boost::lambda; + +// word_counter keeps the occurrences of words inserted. A hashed +// index allows for fast checking of preexisting entries. + +struct word_counter_entry +{ + std::string word; + unsigned int occurrences; + + word_counter_entry( std::string word_ ) : word(word_), occurrences(0) {} +}; + +typedef multi_index_container +< + word_counter_entry, + indexed_by + < + ordered_non_unique + < + boost::multi_index::member< word_counter_entry, unsigned int, &word_counter_entry::occurrences >, + std::greater<unsigned int> + >, + hashed_unique + < + boost::multi_index::member< word_counter_entry, std::string, &word_counter_entry::word > + > + > + +> word_counter; + +typedef boost::tokenizer<boost::char_separator<char> > text_tokenizer; + +int example_main() +{ + std::string text= + "En un lugar de la Mancha, de cuyo nombre no quiero acordarme... " + "...snip..." + "...no se salga un punto de la verdad."; + + // feed the text into the container + + word_counter wc; + text_tokenizer tok(text,boost::char_separator<char>(" \t\n.,;:!?'\"-")); + unsigned int total_occurrences = 0; + + for( text_tokenizer::iterator it = tok.begin(), it_end = tok.end(); + it != it_end ; ++it ) + { + ++total_occurrences; + word_counter::iterator wit = wc.insert(*it).first; + wc.modify_key( wit, ++ bl::_1 ); + } + + // list words by frequency of appearance + + ex_out << std::fixed << std::setprecision(2); + + for( word_counter::iterator wit = wc.begin(), wit_end=wc.end(); + wit != wit_end; ++wit ) + { + ex_out << std::setw(11) << wit->word << ": " + << std::setw(5) + << 100.0 * wit->occurrences / total_occurrences << "%" + << std::endl; + } + + return 0; +} +//] + +TEST_CASE("example: mi_hashed_indices") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex( de: 14.29% + no: 9.52% + un: 9.52% + la: 9.52% + En: 4.76% + lugar: 4.76% + Mancha: 4.76% + cuyo: 4.76% + nombre: 4.76% + quiero: 4.76% + acordarme: 4.76% + snip: 4.76% + se: 4.76% + salga: 4.76% + punto: 4.76% + verdad: 4.76% +)ex")); +} +} // namespace ex_mi_hashed_indices + +// libs/bimap/example/bimap_and_boost/assign.cpp +namespace ex_assign { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + + +using namespace boost::bimaps; +using namespace boost; + + +int example_main() +{ + //[ code_bimap_and_boost_assign + + typedef bimap< multiset_of< int >, list_of< std::string > > bm_type; + + // We can use assign::list_of to initialize the container. + + bm_type bm = assign::list_of< bm_type::relation > /*< + Note that `bm_type::relation` has to be used instead of `bm_type::value_type`. + Contrary to `value_type`, `relation` type stores the elements as non const, a + requirement of `assign::list_of` >*/ + ( 1, "one" ) + ( 2, "two" ) + ( 3, "three" ); + + // The left map view is a multiset, again we use insert + + assign::insert( bm.left ) + ( 4, "four" ) + ( 5, "five" ) + ( 6, "six" ); + + // The right map view is a list so we use push_back here + // Note the order of the elements in the list! + + assign::push_back( bm.right ) + ( "seven" , 7 ) + ( "eight" , 8 ); + + assign::push_front( bm.right ) + ( "nine" , 9 ) + ( "ten" , 10 ) + ( "eleven", 11 ); + + // Since it is left_based the main view is a multiset, so we use insert + + assign::insert( bm ) + ( 12, "twelve" ) + ( 13, "thirteen" ); + //] + + return 0; +} + +TEST_CASE("example: assign") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_assign + +// libs/bimap/example/bimap_and_boost/lambda.cpp +namespace ex_lambda { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +int example_main() +{ + //[ code_bimap_and_boost_lambda + + typedef bimap< std::string, int > bm_type; + + bm_type bm; + bm.insert( bm_type::value_type("one",1) ); + bm.insert( bm_type::value_type("two",2) ); + + bm.right.range( 5 < _key, _key < 10 ); + + bm.left.modify_key( bm.left.find("one"), _key = "1" ); + + bm.left.modify_data( bm.left.begin(), _data *= 10 ); + //] + return 0; +} + +TEST_CASE("example: lambda") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_lambda + +// libs/bimap/example/bimap_and_boost/property_map.cpp +namespace ex_property_map { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +//[ code_bimap_and_boost_property_map + +template <typename AddressMap> +void foo(AddressMap & address_map) +{ + typedef typename boost::property_traits<AddressMap>::value_type value_type; + typedef typename boost::property_traits<AddressMap>::key_type key_type; + + value_type address; + key_type fred = "Fred"; + ex_out << boost::get(address_map, fred); +} + +int example_main() +{ + typedef bimap<std::string, multiset_of<std::string> > Name2Address; + typedef Name2Address::value_type location; + + Name2Address name2address; + name2address.insert( location("Fred", "710 West 13th Street") ); + name2address.insert( location( "Joe", "710 West 13th Street") ); + + foo( name2address.left ); + + return 0; +} +//] + +TEST_CASE("example: property_map") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(710 West 13th Street)ex")); +} +} // namespace ex_property_map + +// libs/bimap/example/bimap_and_boost/range.cpp +namespace ex_range { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +//[ code_bimap_and_boost_range_functions + +template< class ForwardReadableRange, class UnaryFunctor > +UnaryFunctor for_each(const ForwardReadableRange & r, UnaryFunctor func) +{ + typedef typename + boost::range_const_iterator<ForwardReadableRange>::type const_iterator; + + for(const_iterator i= boost::begin(r), iend= boost::end(r); i!=iend; ++i ) + { + func(*i); + } + + return func; +} + +template< class ForwardReadableRange, class Predicate > +typename boost::range_difference<ForwardReadableRange>::type + count_if(const ForwardReadableRange & r, Predicate pred) +{ + typedef typename + boost::range_const_iterator<ForwardReadableRange>::type const_iterator; + + typename boost::range_difference<ForwardReadableRange>::type c = 0; + + for( const_iterator i = boost::begin(r), iend = boost::end(r); i != iend; ++i ) + { + if( pred(*i) ) ++c; + } + + return c; +} +//] + + +using namespace boost::bimaps; +using namespace boost; +// boost/bind.hpp also makes boost::placeholders visible globally, which a +// module cannot do; the module consumer writes the directive (the +// upstream-recommended spelling). +using namespace boost::placeholders; + +//[ code_bimap_and_boost_range + +struct pair_printer +{ + pair_printer(std::ostream & o) : os(o) {} + template< class Pair > + void operator()(const Pair & p) + { + os << "(" << p.first << "," << p.second << ")"; + } + private: + std::ostream & os; +}; + +struct second_extractor +{ + template< class Pair > + const typename Pair::second_type & operator()(const Pair & p) + { + return p.second; + } +}; + +int example_main() +{ + typedef bimap< double, multiset_of<int> > bm_type; + + bm_type bm; + bm.insert( bm_type::value_type(2.5 , 1) ); + bm.insert( bm_type::value_type(3.1 , 2) ); + //... + bm.insert( bm_type::value_type(6.4 , 4) ); + bm.insert( bm_type::value_type(1.7 , 2) ); + + // Print all the elements of the left map view + + for_each( bm.left, pair_printer(ex_out) ); + + // Print a range of elements of the right map view + + for_each( bm.right.range( 2 <= _key, _key < 6 ), pair_printer(ex_out) ); + + // Count the number of elements where the data is equal to 2 from a + // range of elements of the left map view + + ex_range::count_if( bm.left.range( 2.3 < _key, _key < 5.4 ), + bind<int>( second_extractor(), _1 ) == 2 ); + + return 0; +} +//] + +TEST_CASE("example: range") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex((1.7,2)(2.5,1)(3.1,2)(6.4,4)(2,3.1)(2,1.7)(4,6.4))ex")); +} +} // namespace ex_range + +// libs/bimap/example/bimap_and_boost/serialization.cpp +namespace ex_serialization { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + + +using namespace boost::bimaps; + +int example_main() +{ + //[ code_bimap_and_boost_serialization + + typedef bimap< std::string, int > bm_type; + + // Create a bimap and serialize it to a file + { + bm_type bm; + bm.insert( bm_type::value_type("one",1) ); + bm.insert( bm_type::value_type("two",2) ); + + std::ofstream ofs((std::filesystem::temp_directory_path() / "boost-m-bimap-serialization.data").string()); + boost::archive::text_oarchive oa(ofs); + + oa << const_cast<const bm_type&>(bm); /*< + We must do a const cast because Boost.Serialization archives + only save const objects. Read Boost.Serializartion docs for the + rationale behind this decision >*/ + + /*<< We can only serialize iterators if the bimap was serialized first. + Note that the const cast is not required here because we create + our iterators as const. >>*/ + const bm_type::left_iterator left_iter = bm.left.find("two"); + oa << left_iter; + + const bm_type::right_iterator right_iter = bm.right.find(1); + oa << right_iter; + } + + // Load the bimap back + { + bm_type bm; + + std::ifstream ifs((std::filesystem::temp_directory_path() / "boost-m-bimap-serialization.data").string(), std::ios::binary); + boost::archive::text_iarchive ia(ifs); + + ia >> bm; + + assert( bm.size() == 2 ); + + bm_type::left_iterator left_iter; + ia >> left_iter; + + assert( left_iter->first == "two" ); + + bm_type::right_iterator right_iter; + ia >> right_iter; + + assert( right_iter->first == 1 ); + } + //] + + return 0; +} + +TEST_CASE("example: serialization") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_serialization diff --git a/tests/bimap/include_with_boost.cpp b/tests/bimap/include_with_boost.cpp new file mode 100644 index 00000000..adeb4db5 --- /dev/null +++ b/tests/bimap/include_with_boost.cpp @@ -0,0 +1,278 @@ +// boost.bimap with the macro libraries — pure include: bimap_and_boost/ +// {foreach,typeof,xpressive}.cpp exercise BOOST_FOREACH, BOOST_TYPEOF / +// BOOST_AUTO and xpressive actions, which no module can carry, so this TU +// includes everything and imports nothing. Same output oracle as examples.cpp. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdio> +#include <boost/config.hpp> +#include <string> +#include <iostream> +#include <boost/foreach.hpp> +#include <boost/bimap/bimap.hpp> +#include <boost/bimap/list_of.hpp> +#include <boost/bimap/support/lambda.hpp> +#include <boost/typeof/typeof.hpp> +#include <boost/xpressive/xpressive.hpp> +#include <boost/xpressive/regex_actions.hpp> +#include <sstream> +#include <string> +#include <string_view> + +// Output of the ported examples is captured here; each case compares it with +// what the original program printed (built from upstream/boost alone). +inline std::ostringstream ex_out; +inline std::istringstream ex_in; +inline char ex_arg0[] = "example"; +inline char* ex_argv[] = {ex_arg0, nullptr}; +template <class... A> +int ex_printf(const char* fmt, A... a) { + int n = std::snprintf(nullptr, 0, fmt, a...); + std::string s(std::size_t(n) + 1, '\0'); + std::snprintf(s.data(), s.size(), fmt, a...); + s.resize(std::size_t(n)); + ex_out << s; + return n; +} +inline int ex_puts(const char* s) { + ex_out << s << '\n'; + return 0; +} + +// libs/bimap/example/bimap_and_boost/foreach.cpp +namespace ex_foreach { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + + +using namespace boost::bimaps; + + +int example_main() +{ + //[ code_bimap_and_boost_foreach + + typedef bimap< std::string, list_of<int> > bm_type; + + bm_type bm; + bm.insert( bm_type::value_type("1", 1) ); + bm.insert( bm_type::value_type("2", 2) ); + bm.insert( bm_type::value_type("3", 4) ); + bm.insert( bm_type::value_type("4", 2) ); + + BOOST_FOREACH( bm_type::left_reference p, bm.left ) + { + ++p.second; /*< We can modify the right element because we have + use a mutable collection type in the right side. >*/ + } + + BOOST_FOREACH( bm_type::right_const_reference p, bm.right ) + { + ex_out << p.first << "-->" << p.second << std::endl; + } + + //] + + // More examples + + BOOST_FOREACH( bm_type::right_reference p, bm.right ) + { + ++p.first; + } + + BOOST_FOREACH( bm_type::left_const_reference p, bm.left ) + { + ex_out << p.first << "-->" << p.second << std::endl; + } + + BOOST_FOREACH( bm_type::reference p, bm ) + { + ++p.right; + } + + const bm_type & cbm = bm; + BOOST_FOREACH( bm_type::const_reference p, cbm ) + { + ex_out << p.left << "-->" << p.right << std::endl; + } + + BOOST_FOREACH( bm_type::const_reference p, bm ) + { + ex_out << p.left << "-->" << p.right << std::endl; + } + + //[ code_bimap_and_boost_foreach_using_range + + BOOST_FOREACH( bm_type::left_reference p, + ( bm.left.range( std::string("1") <= _key, _key < std::string("3") ) )) + { + ++p.second; + } + + BOOST_FOREACH( bm_type::left_const_reference p, + ( bm.left.range( std::string("1") <= _key, _key < std::string("3") ) )) + { + ex_out << p.first << "-->" << p.second << std::endl; + } + //] + + return 0; +} + +TEST_CASE("example: foreach") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(2-->1 +3-->2 +5-->3 +3-->4 +1-->3 +2-->4 +3-->6 +4-->4 +1-->4 +2-->5 +3-->7 +4-->5 +1-->4 +2-->5 +3-->7 +4-->5 +1-->5 +2-->6 +)ex")); +} +} // namespace ex_foreach + +// libs/bimap/example/bimap_and_boost/typeof.cpp +namespace ex_typeof { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + +using namespace boost::bimaps; + +struct name {}; +struct number {}; + +void using_auto() +{ + //[ code_bimap_and_boost_typeof_first + + typedef bimap< tagged<std::string,name>, tagged<int,number> > bm_type; + bm_type bm; + bm.insert( bm_type::value_type("one" ,1) ); + bm.insert( bm_type::value_type("two" ,2) ); + //] + + //[ code_bimap_and_boost_typeof_using_auto + + for( BOOST_AUTO(iter, bm.by<name>().begin()); iter!=bm.by<name>().end(); ++iter) + { + ex_out << iter->first << " --> " << iter->second << std::endl; + } + + BOOST_AUTO( iter, bm.by<number>().find(2) ); + ex_out << "2: " << iter->get<name>(); + //] +} + +void not_using_auto() +{ + typedef bimap< tagged<std::string,name>, tagged<int,number> > bm_type; + bm_type bm; + bm.insert( bm_type::value_type("one" ,1) ); + bm.insert( bm_type::value_type("two" ,2) ); + + //[ code_bimap_and_boost_typeof_not_using_auto + + for( bm_type::map_by<name>::iterator iter = bm.by<name>().begin(); + iter!=bm.by<name>().end(); ++iter) + { + ex_out << iter->first << " --> " << iter->second << std::endl; + } + + bm_type::map_by<number>::iterator iter = bm.by<number>().find(2); + ex_out << "2: " << iter->get<name>(); + //] +} + +int example_main() +{ + using_auto(); + not_using_auto(); + + return 0; +} + +TEST_CASE("example: typeof") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(one --> 1 +two --> 2 +2: twoone --> 1 +two --> 2 +2: two)ex")); +} +} // namespace ex_typeof + +// libs/bimap/example/bimap_and_boost/xpressive.cpp +namespace ex_xpressive { +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +// Boost.Bimap Example +//----------------------------------------------------------------------------- + + + + + +using namespace boost::bimaps; +using namespace boost::xpressive; +namespace xp = boost::xpressive; + +int example_main() +{ + //[ code_bimap_and_boost_xpressive + + typedef bimap< std::string, int > bm_type; + bm_type bm; + + std::string rel_str("one <--> 1 two <--> 2 three <--> 3"); + + sregex rel = ( (s1= +_w) >> " <--> " >> (s2= +_d) ) + [ + xp::ref(bm)->*insert( xp::construct<bm_type::value_type>(s1, as<int>(s2)) ) + ]; + + sregex relations = rel >> *(+_s >> rel); + + regex_match(rel_str, relations); + + assert( bm.size() == 3 ); + //] + + return 0; +} + +TEST_CASE("example: xpressive") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_xpressive diff --git a/tools/apply_patches.py b/tools/apply_patches.py index 0fac612a..a75367d8 100644 --- a/tools/apply_patches.py +++ b/tools/apply_patches.py @@ -28,11 +28,10 @@ # One entry per patches/<name>.patch; see patches/README.md for each reason. PATCHES = [ - "accumulators", "algorithm", "asio", "beast", "config_platform", "config_user", - "convert", "hof", "io", "lambda", "mqtt5", "multi_array", "parameter", - "range_adaptors", - "numeric", "regex", "safe_numerics", "serialization", "system", "test", - "ublas", "units", + "accumulators", "algorithm", "asio", "beast", "bimap", "config_platform", + "config_user", "convert", "hof", "io", "lambda", "mqtt5", "multi_array", + "parameter", "range_adaptors", "numeric", "regex", "safe_numerics", + "serialization", "system", "test", "ublas", "units", ] # A vendored file that is an addition, not a patch of an upstream file. diff --git a/upstream/boost/boost/bimap/support/lambda.hpp b/upstream/boost/boost/bimap/support/lambda.hpp index c8c44ecc..5985970c 100644 --- a/upstream/boost/boost/bimap/support/lambda.hpp +++ b/upstream/boost/boost/bimap/support/lambda.hpp @@ -23,8 +23,6 @@ namespace boost { namespace bimaps { -namespace { - /* boost::lambda::placeholder1_type & _first = boost::lambda::_1; boost::lambda::placeholder2_type & _second = boost::lambda::_2; @@ -33,10 +31,12 @@ boost::lambda::placeholder1_type & _left = boost::lambda::_1; boost::lambda::placeholder2_type & _right = boost::lambda::_2; */ -boost::lambda::placeholder1_type & _key = boost::lambda::_1; -boost::lambda::placeholder1_type & _data = boost::lambda::_1; - -} +// boost-m vendored edit: the references lived in an anonymous namespace +// (internal linkage), so the bimap module could not export them; like +// boost::lambda::_1 (patches/lambda.patch) they become inline constexpr. +// Replay after re-running import_boost. +inline constexpr boost::lambda::placeholder1_type & _key = boost::lambda::_1; +inline constexpr boost::lambda::placeholder1_type & _data = boost::lambda::_1; } // namespace bimaps } // namespace boost From fa467834692cf1261cdcdded362b3b39482c33ba Mon Sep 17 00:00:00 2001 From: Zhe Feng <zhefeng7110@outlook.com> Date: Mon, 9 Nov 2026 14:37:15 +0000 Subject: [PATCH 6/9] tests: bloom, describe and container_hash suites; example ports with an output oracle bloom: the tutorial section by section, every subfilter, user hashers and allocator; genome.cpp runs the k-mer example on a FASTA fixture instead of the 140 MB download. describe: all non-fmt examples (the json and serialization ones with the BOOST_DESCRIBE_* macros included before the module imports) and the class/enum pages over user types. container_hash: every customization point (hash_value by ADL, boost::hash specialization, is_range / is_tuple_like / is_unordered_range). Ported example programs compare their output with an oracle: the same ported code compiled from upstream/boost headers alone with clang++. test_coverage.py link also maps <page>#example_<stem> sections to the example's case. --- tests/bimap/examples.cpp | 17 + tests/bimap/examples_with_boost.cpp | 7 + tests/bimap/include_with_boost.cpp | 3 + tests/bloom/api.cpp | 134 +++++ tests/bloom/coverage.toml | 56 +- tests/bloom/custom_types.cpp | 90 +++ tests/bloom/examples.cpp | 326 +++++++++++ tests/bloom/genome.cpp | 184 ++++++ tests/container_hash/custom_types.cpp | 135 +++++ tests/describe/coverage.toml | 182 +++--- tests/describe/examples_json.cpp | 417 ++++++++++++++ tests/describe/examples_serialization.cpp | 184 ++++++ tests/describe/include_custom_types.cpp | 146 +++++ tests/describe/include_examples.cpp | 661 ++++++++++++++++++++++ tools/ci/test_coverage.py | 12 +- 15 files changed, 2455 insertions(+), 99 deletions(-) create mode 100644 tests/bloom/api.cpp create mode 100644 tests/bloom/custom_types.cpp create mode 100644 tests/bloom/examples.cpp create mode 100644 tests/bloom/genome.cpp create mode 100644 tests/container_hash/custom_types.cpp create mode 100644 tests/describe/examples_json.cpp create mode 100644 tests/describe/examples_serialization.cpp create mode 100644 tests/describe/include_custom_types.cpp create mode 100644 tests/describe/include_examples.cpp diff --git a/tests/bimap/examples.cpp b/tests/bimap/examples.cpp index e3f27349..ce0de51f 100644 --- a/tests/bimap/examples.cpp +++ b/tests/bimap/examples.cpp @@ -107,6 +107,7 @@ int example_main() first_bimap(); second_bimap(); return 0; + return 0; // main's implicit return } TEST_CASE("example: at_function_examples") { @@ -176,6 +177,7 @@ int example_main() } return 0; + return 0; // main's implicit return } //] @@ -256,6 +258,7 @@ int example_main() } return 0; + return 0; // main's implicit return } //] @@ -401,6 +404,7 @@ int example_main() } return 0; + return 0; // main's implicit return } //] @@ -481,6 +485,7 @@ int example_main() } return 0; + return 0; // main's implicit return } //] @@ -586,6 +591,7 @@ int example_main() } } return 0; + return 0; // main's implicit return } //] @@ -642,6 +648,7 @@ int example_main() years_example(); return 0; + return 0; // main's implicit return } TEST_CASE("example: projection") { @@ -722,6 +729,7 @@ int example_main() //] return 0; + return 0; // main's implicit return } TEST_CASE("example: repetitions_counter") { @@ -795,6 +803,7 @@ int example_main() print_map( results.left, " ends in position ", ex_out ); return 0; + return 0; // main's implicit return } //] @@ -893,6 +902,7 @@ int example_main() } return 0; + return 0; // main's implicit return } //] @@ -987,6 +997,7 @@ int example_main() //] return 0; + return 0; // main's implicit return } TEST_CASE("example: step_by_step") { @@ -1070,6 +1081,7 @@ int example_main() } return 0; + return 0; // main's implicit return } //] @@ -1235,6 +1247,7 @@ int example_main() bimap_without_an_info_hook(); return 0; + return 0; // main's implicit return } TEST_CASE("example: tutorial_info_hook") { @@ -1343,6 +1356,7 @@ int example_main() test_replace(); test_modify(); return 0; + return 0; // main's implicit return } TEST_CASE("example: tutorial_modify_and_replace") { @@ -1433,6 +1447,7 @@ int example_main() using_range(); return 0; + return 0; // main's implicit return } TEST_CASE("example: tutorial_range") { @@ -1515,6 +1530,7 @@ int example_main() } return 0; + return 0; // main's implicit return } TEST_CASE("example: unconstrained_collection") { @@ -1650,6 +1666,7 @@ int example_main() tagged_version(); return 0; + return 0; // main's implicit return } TEST_CASE("example: user_defined_names") { diff --git a/tests/bimap/examples_with_boost.cpp b/tests/bimap/examples_with_boost.cpp index 9618b95d..281feab2 100644 --- a/tests/bimap/examples_with_boost.cpp +++ b/tests/bimap/examples_with_boost.cpp @@ -146,6 +146,7 @@ int example_main() //] return 0; + return 0; // main's implicit return } TEST_CASE("example: population_bimap") { @@ -246,6 +247,7 @@ int example_main() } return 0; + return 0; // main's implicit return } //] @@ -332,6 +334,7 @@ int example_main() //] return 0; + return 0; // main's implicit return } TEST_CASE("example: assign") { @@ -372,6 +375,7 @@ int example_main() bm.left.modify_data( bm.left.begin(), _data *= 10 ); //] return 0; + return 0; // main's implicit return } TEST_CASE("example: lambda") { @@ -420,6 +424,7 @@ int example_main() foo( name2address.left ); return 0; + return 0; // main's implicit return } //] @@ -533,6 +538,7 @@ int example_main() bind<int>( second_extractor(), _1 ) == 2 ); return 0; + return 0; // main's implicit return } //] @@ -612,6 +618,7 @@ int example_main() //] return 0; + return 0; // main's implicit return } TEST_CASE("example: serialization") { diff --git a/tests/bimap/include_with_boost.cpp b/tests/bimap/include_with_boost.cpp index adeb4db5..bfe35169 100644 --- a/tests/bimap/include_with_boost.cpp +++ b/tests/bimap/include_with_boost.cpp @@ -123,6 +123,7 @@ int example_main() //] return 0; + return 0; // main's implicit return } TEST_CASE("example: foreach") { @@ -215,6 +216,7 @@ int example_main() not_using_auto(); return 0; + return 0; // main's implicit return } TEST_CASE("example: typeof") { @@ -267,6 +269,7 @@ int example_main() //] return 0; + return 0; // main's implicit return } TEST_CASE("example: xpressive") { diff --git a/tests/bloom/api.cpp b/tests/bloom/api.cpp new file mode 100644 index 00000000..8a6929b7 --- /dev/null +++ b/tests/bloom/api.cpp @@ -0,0 +1,134 @@ +// mcpplibs.mwrap.boost.bloom — the tutorial of Boost.Bloom +// (libs/bloom/doc/bloom/tutorial.adoc) section by section, plus the subfilter +// family and the filter's construction/assignment/comparison members. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +import std; +import mcpplibs.mwrap.boost.bloom; +import mcpplibs.mwrap.boost.core; // boost::span + +using filter = boost::bloom::filter<std::string, 8>; + +TEST_CASE("doc: tutorial#tutorial") { + // Filter Definition: T, K, Subfilter, Stride, Hash, Allocator. + using f1 = boost::bloom::filter<int, 3>; + using f2 = boost::bloom::filter<int, 1, boost::bloom::block<std::uint64_t, 4>>; + using f3 = boost::bloom::filter<int, 1, boost::bloom::multiblock<std::uint32_t, 8>, 2>; + static_assert(std::is_same_v<f1::value_type, int>); + static_assert(f1::k == 3); + static_assert(std::is_same_v<f2::subfilter, boost::bloom::block<std::uint64_t, 4>>); + static_assert(f3::stride == 2); + static_assert(std::is_same_v<f1::hasher, boost::hash<int>>); +} + +TEST_CASE("doc: tutorial#capacity") { + filter f(1'000'000); + CHECK(f.capacity() >= 1'000'000); + filter g(100'000, 0.01); + filter g2(filter::capacity_for(100'000, 0.01)); + CHECK(g.capacity() == g2.capacity()); + // filter(100'000, 1E-50) asks for ~1.4E12 bits; the documented bad_alloc + // depends on the allocator refusing it (Linux overcommit may not), so the + // test checks the computed capacity instead of allocating it. + CHECK(filter::capacity_for(100'000, 1E-50) > std::size_t(1) << 40); + g.reset(2'000'000); + CHECK(g.capacity() >= 2'000'000); + g.reset(100'000, 0.005); + CHECK(g.capacity() == filter(filter::capacity_for(100'000, 0.005)).capacity()); + g.reset(); + CHECK(g.capacity() == 0); +} + +TEST_CASE("doc: tutorial#insertion-and-lookup") { + filter f(10'000, 0.01); + std::vector<std::string> data{"a", "b", "c"}; + f.insert("hello"); + f.insert(data.begin(), data.end()); + CHECK(f.may_contain("hello")); + CHECK(f.may_contain("b")); + int fp = 0; + for (int i = 0; i < 1000; ++i) fp += f.may_contain("absent-" + std::to_string(i)); + CHECK(fp < 50); // FPR configured at 1% + double fpr = filter::fpr_for(100, f.capacity()); + CHECK(fpr > 0.0); + CHECK(fpr < 0.01); + f.clear(); + CHECK_FALSE(f.may_contain("hello")); +} + +TEST_CASE("doc: tutorial#bulk-operations") { + filter f(10'000, 0.01); + std::vector<std::string> data{"one", "two", "three"}; + f.insert(data.begin(), data.end()); + std::vector<std::string> input{"one", "nope", "three"}; + std::vector<bool> results; + f.may_contain(input.begin(), input.end(), [&](const std::string&, bool b) { results.push_back(b); }); + CHECK(results.size() == 3); + CHECK(results[0]); + CHECK(results[2]); +} + +TEST_CASE("doc: tutorial#filter-combination") { + filter f(10'000, 0.01), f2(f.capacity()), f3(f.capacity()); + f.insert("a"); + f2.insert("b"); + f |= f2; + CHECK(f.may_contain("a")); + CHECK(f.may_contain("b")); + f3.insert("a"); + f &= f3; + CHECK(f.may_contain("a")); + filter other(f.capacity() * 2); + CHECK_THROWS_AS(f |= other, std::invalid_argument); +} + +TEST_CASE("doc: tutorial#direct-access-to-the-array") { + filter f1(5'000, 0.01); + f1.insert("persisted"); + const auto path = std::filesystem::temp_directory_path() / "boost-m-bloom-array.bin"; + { + std::ofstream out(path, std::ios::binary); + std::size_t c1 = f1.capacity(); + out.write(reinterpret_cast<const char*>(&c1), sizeof(c1)); + boost::span<const unsigned char> s1 = f1.array(); + out.write(reinterpret_cast<const char*>(s1.data()), static_cast<std::streamsize>(s1.size())); + } + filter f2; + { + std::ifstream in(path, std::ios::binary); + std::size_t c2; + in.read(reinterpret_cast<char*>(&c2), sizeof(c2)); + f2.reset(c2); + boost::span<unsigned char> s2 = f2.array(); + in.read(reinterpret_cast<char*>(s2.data()), static_cast<std::streamsize>(s2.size())); + } + CHECK(f2 == f1); + CHECK(f2.may_contain("persisted")); + std::filesystem::remove(path); +} + +TEST_CASE("doc: future_work#future_work") { + // try_insert is future work; today's idiom: + filter f(1000, 0.01); + auto try_insert = [&](const std::string& x) { + if (f.may_contain(x)) return false; + f.insert(x); + return true; + }; + CHECK(try_insert("x")); + CHECK_FALSE(try_insert("x")); +} + +TEST_CASE_TEMPLATE("subfilters", Sub, boost::bloom::block<unsigned char, 1>, boost::bloom::block<std::uint64_t, 3>, + boost::bloom::multiblock<std::uint32_t, 4>, boost::bloom::fast_multiblock32<8>, + boost::bloom::fast_multiblock64<4>) { + boost::bloom::filter<int, 1, Sub> f(4'000, 0.02); + for (int i = 0; i < 200; ++i) f.insert(i); + for (int i = 0; i < 200; ++i) REQUIRE(f.may_contain(i)); + auto g = f; + CHECK(g == f); + g.clear(); + CHECK(g != f); + std::swap(f, g); + CHECK_FALSE(f.may_contain(1)); +} diff --git a/tests/bloom/coverage.toml b/tests/bloom/coverage.toml index fecdb2ab..b393f205 100644 --- a/tests/bloom/coverage.toml +++ b/tests/bloom/coverage.toml @@ -6,17 +6,59 @@ upstream = "1.92.0" library = "bloom" [[example]] -path = "libs/bloom/example/basic.cpp" -status = "todo" +path = "libs/bloom/example/basic.cpp" +case = "bloom/examples:example: basic" [[example]] path = "libs/bloom/example/genome.cpp" -status = "todo" +case = "bloom/genome:example: genome" +reason = "run on a FASTA fixture: the original reads a 140 MB NCBI genome download" [[example]] -path = "libs/bloom/example/rolling_filter.cpp" -status = "todo" +path = "libs/bloom/example/rolling_filter.cpp" +case = "bloom/examples:example: rolling_filter" [[example]] -path = "libs/bloom/example/serialization.cpp" -status = "todo" +path = "libs/bloom/example/serialization.cpp" +case = "bloom/examples:example: serialization" + +[[snippet]] +doc = "libs/bloom/doc/bloom/future_work.adoc#future_work" +title = "`try_insert`" +case = "bloom/api:doc: future_work#future_work" + +[[snippet]] +doc = "libs/bloom/doc/bloom/tutorial.adoc#bulk-operations" +title = "Bulk Operations" +case = "bloom/api:doc: tutorial#bulk-operations" + +[[snippet]] +doc = "libs/bloom/doc/bloom/tutorial.adoc#capacity" +title = "Capacity" +case = "bloom/api:doc: tutorial#capacity" + +[[snippet]] +doc = "libs/bloom/doc/bloom/tutorial.adoc#direct-access-to-the-array" +title = "Direct Access to the Array" +case = "bloom/api:doc: tutorial#direct-access-to-the-array" + +[[snippet]] +doc = "libs/bloom/doc/bloom/tutorial.adoc#filter-combination" +title = "Filter Combination" +case = "bloom/api:doc: tutorial#filter-combination" + +[[snippet]] +doc = "libs/bloom/doc/bloom/tutorial.adoc#gdb-pretty-printer" +title = "GDB Pretty-Printer" +status = "excluded" +reason = "GDB session transcript (pretty-printer setup), no C++ API use" + +[[snippet]] +doc = "libs/bloom/doc/bloom/tutorial.adoc#insertion-and-lookup" +title = "Insertion and Lookup" +case = "bloom/api:doc: tutorial#insertion-and-lookup" + +[[snippet]] +doc = "libs/bloom/doc/bloom/tutorial.adoc#tutorial" +title = "Filter Definition" +case = "bloom/api:doc: tutorial#tutorial" diff --git a/tests/bloom/custom_types.cpp b/tests/bloom/custom_types.cpp new file mode 100644 index 00000000..9d87c16a --- /dev/null +++ b/tests/bloom/custom_types.cpp @@ -0,0 +1,90 @@ +// mcpplibs.mwrap.boost.bloom — user element types and user hashers: a user +// struct hashed through boost::hash (hash_value found by ADL), a user hasher +// marked as avalanching (is_avalanching, so the filter skips its own mixing), +// a user hasher without the mark (mixed by the filter), and a user allocator. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +import std; +import mcpplibs.mwrap.boost.bloom; +import mcpplibs.mwrap.boost.container_hash; + +namespace user { +struct ip4 { + std::uint8_t a, b, c, d; + bool operator==(const ip4&) const = default; + friend std::size_t hash_value(const ip4& x) { + std::size_t seed = 0; + boost::hash_combine(seed, (x.a << 24) | (x.b << 16) | (x.c << 8) | x.d); + return seed; + } +}; + +struct avalanching_hash { + using is_avalanching = std::true_type; + std::size_t operator()(std::uint64_t x) const noexcept { + x ^= x >> 33; + x *= 0xff51afd7ed558ccdull; + x ^= x >> 33; + x *= 0xc4ceb9fe1a85ec53ull; + x ^= x >> 33; + return std::size_t(x); + } +}; + +struct identity_hash { // poor quality: the filter post-mixes it + std::size_t operator()(std::uint64_t x) const noexcept { return std::size_t(x); } +}; + +inline std::size_t bytes_allocated = 0; +template <class T> +struct counting_allocator { + using value_type = T; + counting_allocator() = default; + template <class U> + counting_allocator(const counting_allocator<U>&) noexcept {} + T* allocate(std::size_t n) { + bytes_allocated += n * sizeof(T); + return std::allocator<T>().allocate(n); + } + void deallocate(T* p, std::size_t n) noexcept { std::allocator<T>().deallocate(p, n); } + template <class U> + bool operator==(const counting_allocator<U>&) const noexcept { return true; } +}; +} // namespace user + +TEST_CASE("user struct through boost::hash") { + boost::bloom::filter<user::ip4, 5> f(10'000, 0.01); + f.insert({10, 0, 0, 1}); + f.insert({192, 168, 1, 1}); + CHECK(f.may_contain({10, 0, 0, 1})); + int fp = 0; + for (int i = 0; i < 256; ++i) fp += f.may_contain({172, 16, 0, std::uint8_t(i)}); + CHECK(fp < 20); +} + +TEST_CASE("user hashers, avalanching or not") { + boost::bloom::filter<std::uint64_t, 4, boost::bloom::block<unsigned char, 1>, 0, user::avalanching_hash> a(8'000, 0.01); + boost::bloom::filter<std::uint64_t, 4, boost::bloom::block<unsigned char, 1>, 0, user::identity_hash> b(8'000, 0.01); + for (std::uint64_t i = 0; i < 500; ++i) { + a.insert(i * 4096); // structured keys: bad for an unmixed identity hash + b.insert(i * 4096); + } + int fpa = 0, fpb = 0; + for (std::uint64_t i = 0; i < 2000; ++i) { + fpa += a.may_contain(i * 4096 + 1); + fpb += b.may_contain(i * 4096 + 1); + } + CHECK(fpa < 100); + CHECK(fpb < 100); // the filter mixes a non-avalanching hash itself + CHECK(a.hash_function()(1) == user::avalanching_hash()(1)); +} + +TEST_CASE("user allocator") { + user::bytes_allocated = 0; + boost::bloom::filter<int, 2, boost::bloom::block<unsigned char, 1>, 0, boost::hash<int>, + user::counting_allocator<unsigned char>> + f(80'000); + CHECK(user::bytes_allocated >= 10'000); // 80'000 bits + f.insert(7); + CHECK(f.may_contain(7)); +} diff --git a/tests/bloom/examples.cpp b/tests/bloom/examples.cpp new file mode 100644 index 00000000..7febc15e --- /dev/null +++ b/tests/bloom/examples.cpp @@ -0,0 +1,326 @@ +// mcpplibs.mwrap.boost.bloom — libs/bloom/example/{basic,rolling_filter, +// serialization}.cpp ported mechanically: each program runs as a case and its +// output must equal what the original printed when built from upstream/boost +// alone. serialization writes its filter to the temporary directory instead +// of ./filter.bin, and keeps its #include of boost/core/detail/splitmix64.hpp +// (a detail header no module exports). genome.cpp reads a 140 MB genome +// download: genome.cpp in this directory runs it on a small FASTA fixture. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdio> +#include <cassert> +#include <cmath> +#include <boost/core/detail/splitmix64.hpp> +import std; +import mcpplibs.mwrap.boost.bloom; +import mcpplibs.mwrap.boost.uuid; + +// Output of the ported examples is captured here; each case compares it with +// what the original program printed (built from upstream/boost alone). +inline std::ostringstream ex_out; +inline std::istringstream ex_in; +inline char ex_arg0[] = "example"; +inline char* ex_argv[] = {ex_arg0, nullptr}; +template <class... A> +int ex_printf(const char* fmt, A... a) { + int n = std::snprintf(nullptr, 0, fmt, a...); + std::string s(std::size_t(n) + 1, '\0'); + std::snprintf(s.data(), s.size(), fmt, a...); + s.resize(std::size_t(n)); + ex_out << s; + return n; +} +inline int ex_puts(const char* s) { + ex_out << s << '\n'; + return 0; +} + +// libs/bloom/example/basic.cpp +namespace ex_basic { +int example_main() +{ + /* Bloom filter of strings with 5 bits set per insertion */ + + using filter = boost::bloom::filter<std::string, 5>; + + /* create filter with a capacity of 1,000,000 bits */ + + filter f(1000000); + + /* insert elements in the set */ + + f.insert("hello"); + f.insert("Boost"); + + /* elements inserted are always correctly checked as such */ + + assert(f.may_contain("hello") == true); + + /* Elements not inserted may incorrectly be identified as such with a + * false probability rate (FPR) which is a function of the array capacity, + * the number of bits set per element and generally how the + * boost::bloom::filter was configured. + */ + + if(f.may_contain("bye")) { /* likely false */ + ex_out << "false positive\n"; + } + else { + ex_out << "everything worked as expected\n"; + } + return 0; // main's implicit return +} + +TEST_CASE("example: basic") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(everything worked as expected +)ex")); +} +} // namespace ex_basic + +// libs/bloom/example/rolling_filter.cpp +namespace ex_rolling_filter { +/* Regular Bloom filters don't "forget", that is, the number of elements + * in the filter keeps growing (and the FPR increasing) until the entire bit + * array is reset. This is how a *rolling filter* can be devised that keeps + * track of the last elements inserted only: + * + * - A so-called *window size* w and a number of windows n are specified. + * - n regular Bloom filters are kept simultaneously, one for each window. + * - We keep an index i to designate the i-th filter as the *active filter*. + * - Insertion of a new element is done in the active filter only. + * - Lookup queries all the filters. + * - Every w insertions, we bump i (mod n) to switch to a new active filter + * and clear it before continuing with the insertions. + * + * It's not hard to see that this algorithm implements a filter that remembers + * only the last s elements inserted, with w * (n-1) < s <= w * n. The + * resulting FPR oscillates between 1 - (1 - sub_fpr)^(n-1) and + * 1 - (1 - sub_fpr)^n, where sub_fpr is the FPR of an individual filter + * after w insertions. + */ + +template< + typename T, std::size_t K, + typename Subfilter = boost::bloom::block<unsigned char,1>, + std::size_t Stride = 0, + typename Hash = boost::hash<T>, + typename Allocator=std::allocator<unsigned char> +> +class rolling_filter +{ +public: + rolling_filter(std::size_t w_, std::size_t n, double max_fpr): + w(w_), fs(n) + { + assert(w > 0); + assert(n >= 2); + assert(max_fpr >= 0.0 && max_fpr <= 1.0); + + /* Adjust the capacity of each individual filter so that + * this->max_fpr() ~ max_fpr. + */ + + auto sub_fpr = 1.0 - std::pow(1.0 - max_fpr, 1.0 / n); + auto m = filter_type::capacity_for(w, sub_fpr); + for(auto& f: fs) f.reset(m); + } + + std::size_t min_size() const + { + /* Minimum size under *stationary* conditions: the actual size can be + * smaller than this if we haven't yet inserted that many elements. + */ + + return w * (fs.size() - 1); + } + + std::size_t max_size() const + { + return w * fs.size(); + } + + double min_fpr() const + { + double sub_fpr = filter_type::fpr_for(w, fs[0].capacity()); + return 1.0 - std::pow(1.0 - sub_fpr, (double)fs.size() - 1.0); + } + + double max_fpr() const + { + double sub_fpr = filter_type::fpr_for(w, fs[0].capacity()); + return 1.0 - std::pow(1.0 - sub_fpr, (double)fs.size()); + } + + std::size_t capacity() const + { + return fs[0].capacity() * fs.size(); + } + + void insert(const T& x) + { + if(++count > w) { + count = 1; + if(++i >= fs.size()) i = 0; + fs[i].clear(); + } + fs[i].insert(x); + } + + bool may_contain(const T& x) const + { + for(const auto& f: fs) { + if(f.may_contain(x)) return true; + } + return false; + } + +private: + using filter_type = boost::bloom::filter< + T, K, Subfilter, Stride, Hash, Allocator + >; + using vector_type = std::vector< + filter_type, + typename std::allocator_traits<Allocator>:: + template rebind_alloc<filter_type> + >; + + std::size_t w; + vector_type fs; + std::size_t count = 0, + i = 0; +}; + +int example_main() +{ + /* Construct a rolling filter with a size between + * 9,000 and 10,000 elements. + */ + + const std::size_t window_size = 1000; + const std::size_t num_windows = 10; + const double max_fpr = 0.01; + + rolling_filter<std::size_t, 5> rf(window_size, num_windows, max_fpr); + ex_out << "rolling filter capacity: " << rf.capacity() << " bits\n"; + + /* Run the filter through more than 10x the elements it can hold. */ + + const std::size_t num_elements = rf.max_size() * 10 + window_size / 2; + for(std::size_t i = 0 ; i < num_elements; ++i) rf.insert(i); + + /* Check the filter has actually forgotten the first + * num_elements - rf.max_size() elements. + */ + + std::size_t count = 0; + for(std::size_t i = 0 ; i < num_elements - rf.max_size(); ++i) { + count += rf.may_contain(i); + } + ex_out << "measured fpr: " + << (double)count / (num_elements - rf.max_size()) + << " (should be between " << rf.min_fpr() + << " and " << rf.max_fpr() << ")\n"; + + /* The remaining elements must be mostly in the filter. */ + + count = 0; + for(std::size_t i = num_elements - rf.max_size() ; i < num_elements; ++i) { + count += rf.may_contain(i); + } + ex_out << "elements found: " << count + << " (must be between " << rf.min_size() + << " and " << rf.max_size() << ")\n"; + return 0; // main's implicit return +} + +TEST_CASE("example: rolling_filter") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(rolling filter capacity: 172720 bits +measured fpr: 0.00909392 (should be between 0.00899309 and 0.00998731) +elements found: 9505 (must be between 9000 and 10000) +)ex")); +} +} // namespace ex_rolling_filter + +// libs/bloom/example/serialization.cpp +namespace ex_serialization { +/* emits a deterministic pseudorandom sequence of UUIDs */ + +struct uuid_generator +{ + boost::uuids::uuid operator()() + { + std::uint8_t data[16]; + std::uint64_t x = rng(); + std::memcpy(&data[0], &x, sizeof(x)); + x = rng(); + std::memcpy(&data[8], &x, sizeof(x)); + + return {data}; + } + + boost::detail::splitmix64 rng; +}; + +using filter = boost::bloom::filter< + boost::uuids::uuid, 1, boost::bloom::multiblock<std::uint64_t, 8> >; + +static constexpr std::size_t num_elements = 10000; + +/* creates a filter with num_elements UUIDs */ + +filter create_filter() +{ + uuid_generator gen; + filter f(num_elements, 0.005); + for(std::size_t i = 0; i < num_elements; ++i) f.insert(gen()); + return f; +} + +void save_filter(const filter& f, const char* filename) +{ + std::ofstream out(filename, std::ios::binary | std::ios::trunc); + std::size_t c=f.capacity(); + out.write(reinterpret_cast<const char*>(&c), sizeof(c)); /* save capacity (bits) */ + auto s = f.array(); + out.write(reinterpret_cast<const char*>(s.data()), s.size()); /* save array */ +} + +filter load_filter(const char* filename) +{ + std::ifstream in(filename, std::ios::binary); + std::size_t c; + in.read(reinterpret_cast<char*>(&c), sizeof(c)); + filter f(c); + auto s = f.array(); + in.read(reinterpret_cast<char*>(s.data()), s.size()); /* load array */ + return f; +} + +int example_main() +{ + static const std::string filename_s = (std::filesystem::temp_directory_path() / "boost-m-bloom-filter.bin").string(); + static const char* filename = filename_s.c_str(); + + auto f1 = create_filter(); + save_filter(f1, filename); + auto f2 = load_filter(filename); + + if (f1 == f2) ex_out << "serialization correct\n"; + else ex_out << "something went wrong\n"; + return 0; // main's implicit return +} + +TEST_CASE("example: serialization") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(serialization correct +)ex")); +} +} // namespace ex_serialization diff --git a/tests/bloom/genome.cpp b/tests/bloom/genome.cpp new file mode 100644 index 00000000..ade61184 --- /dev/null +++ b/tests/bloom/genome.cpp @@ -0,0 +1,184 @@ +// mcpplibs.mwrap.boost.bloom — libs/bloom/example/genome.cpp. The original +// reads the fruit-fly genome (a 140 MB NCBI download); this case runs the same +// k-mer filter code (copied unchanged below) on a small FASTA fixture holding +// the three sequences the example expects to find, and checks the example's +// expected answers: 1 1 1 0 0. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cassert> +#include <climits> +import std; +import mcpplibs.mwrap.boost.bloom; + +namespace ex_genome { +/* A k-mer is a sliding segment of size k over a sequence of DNA nucleotides + * (A, C, G, T). k_mer encodes the segment in a 64-bit word using 2 bits per + * nucleotide. + */ + +template<std::size_t K> +struct k_mer +{ + static_assert( + K >= 0 && + 2 * K <= sizeof(std::uint64_t) * CHAR_BIT); + + static constexpr std::size_t size() + { + return K; + } + + void reset() + { + data = 0; + } + + /* shift the k-mer and append a new nucleotide */ + + k_mer& operator+=(char n) + { + static constexpr std::uint64_t mask= + (((std::uint64_t)1) << (2 * size())) - 1; + + data <<= 2; + data &= mask; + data |= table[(unsigned char)n]; + return *this; + } + + std::uint64_t data = 0; + + using table_type=std::array<unsigned char, UCHAR_MAX>; + + static constexpr table_type table = [] { + table_type table{}; + table['A'] = table['a'] = 0; + table['C'] = table['c'] = 1; + table['G'] = table['g'] = 2; + table['T'] = table['t'] = 3; + return table; + }(); +}; + +template<std::size_t N> +std::size_t hash_value(const k_mer<N>& km) +{ + /* k:mer::data is 8 bytes wide. We use it directly as the associated + * hash value in 64-bit mode, as std::size_t is the same size; in 32-bit + * mode, we XOR the high and low portions of data to make it fit into + * a std::size_t. + */ + + if constexpr (sizeof(std::size_t) >= sizeof(std::uint64_t)) { + return (std::size_t)km.data; + } + else{ /* 32-bit mode */ + return (std::size_t)(km.data ^ (km.data >> 32)); + } +} + +/* Insert all the k-mers of a given genome in a boost::bloom::filter. + * Assumed format is FASTA with A, C, G, T. + * https://en.wikipedia.org/wiki/FASTA_format + */ + +using genome_filter = boost::bloom::filter< + k_mer<20>, /* using k-mers of length 20 */ + 1, boost::bloom::fast_multiblock32<8> >; + +genome_filter make_genome_filter(const char* filename) +{ + using k_mer = genome_filter::value_type; + + std::ifstream in(filename, std::ios::ate); /* open at end to tell size */ + if(!in) throw std::runtime_error("can't open file"); + + /* As a rough estimation, we assume that the number of k-mers + * is approximately equal to the length of the genome --this is + * overpessimistic due to the likely presence of duplicate k-mers. + * We set FPR = 1%. + */ + + genome_filter f((std::size_t)in.tellg(), 0.01); + in.seekg(0); + + std::string line; + std::size_t width = 0; + k_mer km; + while(std::getline(in, line)) { + if(line.empty()) continue; + if(line[0] == '>') { /* annotation lines in the FASTA format */ + width = 0; + km.reset(); + continue; + } + std::size_t i = 0; + + /* don't insert km till it has km.size() nucleotides */ + + for(; width< km.size() - 1 && i < line.size(); ++i) { + km += line[i]; + ++width; + } + + for(; i < line.size(); ++i) { + km += line[i]; + f.insert(km); + } + } + return f; +} + +/* We estimate a DNA sequence seq to be contained in a genome if all the k-mers + * of seq are contained. The calculation of the resulting false positive rate + * is left as an exercise for the reader. + */ + +bool may_contain(const genome_filter& f, std::string_view seq) +{ + using k_mer = genome_filter::value_type; + + assert(seq.size() >= k_mer::size()); + + k_mer km; + auto first = seq.begin(), last = seq.end(); + + /* feed first km.size() -1 nucleotides */ + + for(std::size_t i = 0; i < km.size() - 1; ++i) km += *first++; + + do{ + km += *first++; + if(!f.may_contain(km)) return false; + }while(first != last); + return true; +} + +} // namespace ex_genome + +TEST_CASE("example: genome") { + using namespace ex_genome; + const auto path = std::filesystem::temp_directory_path() / "boost-m-bloom-genome.fna"; + { + std::ofstream fna(path); + fna << ">NC_004354.4 chromosome X\n" + "ggcgtaccATAAATAAGATTGCGACTCAAAATTAAGCAATAACACttacgtagtc\n" + ">NT_037436.4 chromosome 3L\n" + "cgattATTATAGGGAGAAATATGATCGCGTATGCGAGAGTAGTGCCAACATATTGTGCTCgga\n" + ">NC_004353.4 chromosome 4\n" + "ttAGAATTTACTAAGTACTTCTATGAATGGAATTATTATTGGAAACTCTACAAcgt\n"; + } + auto f = make_genome_filter(path.string().c_str()); + const char* seqs[] = { + "ataaataagattgCGACTCAAAATTAAgcaataacac", /* chr. X */ + "attatagggagaaatatgatcgcgtatgcgagagtagtgccaacatattgtgctc", /* chr. 3L */ + "agaATTTACTAAGTACTTCTATGAATGGAATTATTATTGGAAACTCTACAA", /* chr. 4 */ + "ATTTACTAAGTACTTCTATCTGCAAATTAACAATTTATCAAACAACTG", /* not present */ + "ataaataagattgCGACTCAAAAGTAAgcaat" /* mutation in chr. X */ + }; + std::vector<bool> found; + for (auto seq : seqs) found.push_back(may_contain(f, seq)); + CHECK(found == std::vector<bool>{true, true, true, false, false}); + CHECK_THROWS_AS(make_genome_filter("/nonexistent/genome.fna"), std::runtime_error); + std::filesystem::remove(path); +} diff --git a/tests/container_hash/custom_types.cpp b/tests/container_hash/custom_types.cpp new file mode 100644 index 00000000..33b2c184 --- /dev/null +++ b/tests/container_hash/custom_types.cpp @@ -0,0 +1,135 @@ +// mcpplibs.mwrap.boost.container_hash — every way a user type becomes +// hashable by boost::hash, through the module: a hash_value overload found by +// ADL (free function and hidden friend), a specialization of boost::hash, a +// user range (begin/end) hashed element-wise and the is_range opt-out trait, +// a user tuple-like type (tuple_size/get) and the is_tuple_like trait, the +// is_unordered_range trait for a user set type, and hash_combine-based +// composition of user types inside std containers. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +import std; +import mcpplibs.mwrap.boost.container_hash; + +namespace user { +struct money { + long cents; + bool operator==(const money&) const = default; +}; +std::size_t hash_value(const money& m) { return boost::hash<long>()(m.cents); } + +class account { +public: + account(std::string id, money b) : id_(std::move(id)), balance_(b) {} + bool operator==(const account&) const = default; + friend std::size_t hash_value(const account& a) { + std::size_t seed = 0; + boost::hash_combine(seed, a.id_); + boost::hash_combine(seed, a.balance_); + return seed; + } +private: + std::string id_; + money balance_; +}; + +struct special { + int v; +}; + +// A user range of ints. +class ring { +public: + explicit ring(std::vector<int> v) : v_(std::move(v)) {} + const int* begin() const { return v_.data(); } + const int* end() const { return v_.data() + v_.size(); } +private: + std::vector<int> v_; +}; + +// A range that hashes by identity instead (opts out of element-wise hashing). +class handle_list { +public: + const int* begin() const { return nullptr; } + const int* end() const { return nullptr; } + int id = 7; + friend std::size_t hash_value(const handle_list& h) { return std::size_t(h.id) * 1000003u; } +}; + +// A tuple-like user type. +struct pt3 { + int x, y, z; +}; +template <std::size_t I> +int get(const pt3& p) { + if constexpr (I == 0) return p.x; + else if constexpr (I == 1) return p.y; + else return p.z; +} + +// A user set: element order does not affect the hash. +struct bag { + std::vector<int> items; + const int* begin() const { return items.data(); } + const int* end() const { return items.data() + items.size(); } +}; +} // namespace user + +template <> +struct boost::hash<user::special> { + std::size_t operator()(const user::special& s) const noexcept { return std::size_t(s.v) ^ 0xABCDu; } +}; +template <> +struct boost::container_hash::is_range<user::handle_list> : std::false_type {}; +template <> +struct std::tuple_size<user::pt3> : std::integral_constant<std::size_t, 3> {}; +template <> +struct boost::container_hash::is_unordered_range<user::bag> : std::true_type {}; + +TEST_CASE("hash_value found by ADL: free function and hidden friend") { + boost::hash<user::money> hm; + CHECK(hm({5}) == boost::hash<long>()(5)); + user::account a("x", {1}), b("x", {1}), c("y", {1}); + boost::hash<user::account> ha; + CHECK(ha(a) == ha(b)); + CHECK(ha(a) != ha(c)); +} + +TEST_CASE("specialization of boost::hash") { + CHECK(boost::hash<user::special>()({1}) == (1u ^ 0xABCDu)); + std::vector<user::special> v{{1}, {2}}; + std::size_t seed = 0; + boost::hash_combine(seed, v[0]); // hash_combine uses boost::hash<T> + CHECK(seed != 0); +} + +TEST_CASE("user ranges: element-wise, and the is_range opt-out") { + static_assert(boost::container_hash::is_range<user::ring>::value); + user::ring r({1, 2, 3}); + std::vector<int> same{1, 2, 3}; + CHECK(boost::hash<user::ring>()(r) == boost::hash<std::vector<int>>()(same)); + static_assert(!boost::container_hash::is_range<user::handle_list>::value); + CHECK(boost::hash<user::handle_list>()(user::handle_list{}) == 7u * 1000003u); +} + +TEST_CASE("user tuple-like types") { + static_assert(boost::container_hash::is_tuple_like<user::pt3>::value); + user::pt3 p{1, 2, 3}; + CHECK(boost::hash<user::pt3>()(p) == boost::hash<std::tuple<int, int, int>>()(std::tuple(1, 2, 3))); +} + +TEST_CASE("user unordered ranges") { + user::bag a{{1, 2, 3}}, b{{3, 1, 2}}; + CHECK(boost::hash<user::bag>()(a) == boost::hash<user::bag>()(b)); + std::vector<int> va{1, 2, 3}, vb{3, 1, 2}; + CHECK(boost::hash<std::vector<int>>()(va) != boost::hash<std::vector<int>>()(vb)); +} + +TEST_CASE("user types inside std containers") { + std::unordered_map<user::account, int, boost::hash<user::account>> m; + m[user::account("a", {1})] = 1; + m[user::account("a", {1})] += 1; + CHECK(m.size() == 1); + using key = std::pair<user::money, std::string>; + std::unordered_set<key, boost::hash<key>> s{{{1}, "x"}, {{1}, "y"}}; + CHECK(s.size() == 2); +} diff --git a/tests/describe/coverage.toml b/tests/describe/coverage.toml index d6f3cdb7..739f8443 100644 --- a/tests/describe/coverage.toml +++ b/tests/describe/coverage.toml @@ -6,170 +6,174 @@ upstream = "1.92.0" library = "describe" [[example]] -path = "libs/describe/example/console.cpp" -status = "todo" +path = "libs/describe/example/console.cpp" +case = "describe/examples_json:example: console" [[example]] -path = "libs/describe/example/enum_to_string.cpp" -status = "todo" +path = "libs/describe/example/enum_to_string.cpp" +case = "describe/include_examples:example: enum_to_string" [[example]] -path = "libs/describe/example/equality.cpp" -status = "todo" +path = "libs/describe/example/equality.cpp" +case = "describe/include_examples:example: equality" [[example]] path = "libs/describe/example/fmtlib_class_formatter.cpp" -status = "todo" +status = "excluded" +reason = "needs the {fmt} library, which the package does not vendor" [[example]] path = "libs/describe/example/fmtlib_enum_formatter.cpp" -status = "todo" +status = "excluded" +reason = "needs the {fmt} library, which the package does not vendor" [[example]] -path = "libs/describe/example/from_json.cpp" -status = "todo" +path = "libs/describe/example/from_json.cpp" +case = "describe/examples_json:example: from_json" [[example]] -path = "libs/describe/example/hash_value.cpp" -status = "todo" +path = "libs/describe/example/hash_value.cpp" +case = "describe/include_examples:example: hash_value" [[example]] -path = "libs/describe/example/json_rpc.cpp" -status = "todo" +path = "libs/describe/example/json_rpc.cpp" +case = "describe/examples_json:example: json_rpc" [[example]] -path = "libs/describe/example/pm_to_string.cpp" -status = "todo" +path = "libs/describe/example/pm_to_string.cpp" +case = "describe/include_examples:example: pm_to_string" [[example]] -path = "libs/describe/example/print_function.cpp" -status = "todo" +path = "libs/describe/example/print_function.cpp" +case = "describe/include_examples:example: print_function" [[example]] -path = "libs/describe/example/printing_enums_ct.cpp" -status = "todo" +path = "libs/describe/example/printing_enums_ct.cpp" +case = "describe/include_examples:example: printing_enums_ct" [[example]] -path = "libs/describe/example/printing_enums_rt.cpp" -status = "todo" +path = "libs/describe/example/printing_enums_rt.cpp" +case = "describe/include_examples:example: printing_enums_rt" [[example]] -path = "libs/describe/example/serialization.cpp" -status = "todo" +path = "libs/describe/example/serialization.cpp" +case = "describe/examples_serialization:example: serialization" [[example]] -path = "libs/describe/example/string_to_enum.cpp" -status = "todo" +path = "libs/describe/example/string_to_enum.cpp" +case = "describe/include_examples:example: string_to_enum" [[example]] -path = "libs/describe/example/struct_to_tuple.cpp" -status = "todo" +path = "libs/describe/example/struct_to_tuple.cpp" +case = "describe/include_examples:example: struct_to_tuple" [[example]] -path = "libs/describe/example/to_json.cpp" -status = "todo" +path = "libs/describe/example/to_json.cpp" +case = "describe/examples_json:example: to_json" [[snippet]] -doc = "libs/describe/doc/describe/classes.adoc#class-types-with-protected-or-private-members" -title = "Class Types with Protected or Private Members" -status = "todo" +doc = "libs/describe/doc/describe/classes.adoc#class-types-with-protected-or-private-members" +title = "Class Types with Protected or Private Members" +case = "describe/include_custom_types:doc: classes#class-types-with-protected-or-private-members" [[snippet]] -doc = "libs/describe/doc/describe/classes.adoc#class-types-with-public-members" -title = "Class Types with Public Members" -status = "todo" +doc = "libs/describe/doc/describe/classes.adoc#class-types-with-public-members" +title = "Class Types with Public Members" +case = "describe/include_custom_types:doc: classes#class-types-with-public-members" [[snippet]] -doc = "libs/describe/doc/describe/classes.adoc#overloaded-member-functions" -title = "Overloaded Member Functions" -status = "todo" +doc = "libs/describe/doc/describe/classes.adoc#overloaded-member-functions" +title = "Overloaded Member Functions" +case = "describe/include_custom_types:doc: classes#overloaded-member-functions" [[snippet]] -doc = "libs/describe/doc/describe/classes.adoc#retrieving-class-properties" -title = "Retrieving Class Properties" -status = "todo" +doc = "libs/describe/doc/describe/classes.adoc#retrieving-class-properties" +title = "Retrieving Class Properties" +case = "describe/include_custom_types:doc: classes#retrieving-class-properties" [[snippet]] -doc = "libs/describe/doc/describe/enums.adoc#enums" -title = "Describing Enumeration Types" -status = "todo" +doc = "libs/describe/doc/describe/enums.adoc#enums" +title = "Describing Enumeration Types" +case = "describe/include_custom_types:doc: enums#enums" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_console" -title = "Interactive Variable Console" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_console" +title = "Interactive Variable Console" +case = "describe/examples_json:example: console" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_enum_to_string" -title = "enum_to_string" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_enum_to_string" +title = "enum_to_string" +case = "describe/include_examples:example: enum_to_string" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_equality" -title = "Implementing operator==" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_equality" +title = "Implementing operator==" +case = "describe/include_examples:example: equality" [[snippet]] doc = "libs/describe/doc/describe/examples.adoc#example_fmtlib_class_formatter" title = "`fmtlib` Class Formatter" -status = "todo" +status = "excluded" +reason = "needs the {fmt} library, which the package does not vendor" [[snippet]] doc = "libs/describe/doc/describe/examples.adoc#example_fmtlib_enum_formatter" title = "`fmtlib` Enum Formatter" -status = "todo" +status = "excluded" +reason = "needs the {fmt} library, which the package does not vendor" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_from_json" -title = "Automatic Conversion from JSON" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_from_json" +title = "Automatic Conversion from JSON" +case = "describe/examples_json:example: from_json" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_hash_value" -title = "Implementing hash_value" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_hash_value" +title = "Implementing hash_value" +case = "describe/include_examples:example: hash_value" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_json_rpc" -title = "Automatic JSON RPC" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_json_rpc" +title = "Automatic JSON RPC" +case = "describe/examples_json:example: json_rpc" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_pm_to_string" -title = "Printing Pointers to Members" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_pm_to_string" +title = "Printing Pointers to Members" +case = "describe/include_examples:example: pm_to_string" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_print_function" -title = "Defining a Universal Print Function" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_print_function" +title = "Defining a Universal Print Function" +case = "describe/include_examples:example: print_function" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_printing_enums_ct" -title = "Printing Enumerators with a Compile Time Loop" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_printing_enums_ct" +title = "Printing Enumerators with a Compile Time Loop" +case = "describe/include_examples:example: printing_enums_ct" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_printing_enums_rt" -title = "Printing Enumerators with a Run Time Loop" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_printing_enums_rt" +title = "Printing Enumerators with a Run Time Loop" +case = "describe/include_examples:example: printing_enums_rt" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_serialization" -title = "Automatic Serialization" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_serialization" +title = "Automatic Serialization" +case = "describe/examples_serialization:example: serialization" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_string_to_enum" -title = "string_to_enum" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_string_to_enum" +title = "string_to_enum" +case = "describe/include_examples:example: string_to_enum" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_struct_to_tuple" -title = "struct_to_tuple" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_struct_to_tuple" +title = "struct_to_tuple" +case = "describe/include_examples:example: struct_to_tuple" [[snippet]] -doc = "libs/describe/doc/describe/examples.adoc#example_to_json" -title = "Automatic Conversion to JSON" -status = "todo" +doc = "libs/describe/doc/describe/examples.adoc#example_to_json" +title = "Automatic Conversion to JSON" +case = "describe/examples_json:example: to_json" diff --git a/tests/describe/examples_json.cpp b/tests/describe/examples_json.cpp new file mode 100644 index 00000000..c5ba4d27 --- /dev/null +++ b/tests/describe/examples_json.cpp @@ -0,0 +1,417 @@ +// boost.describe with the json module — libs/describe/example/{console, +// from_json,json_rpc,to_json}.cpp: the BOOST_DESCRIBE_* macros come from +// #include (no module can carry them) and Boost.JSON from its module, whose +// describe-based conversions see the described types (design §4.4: the +// includes precede every import). Each program runs as a case; its output +// must equal what the original printed (built from upstream/boost with +// boost/json/src.hpp). +// console.cpp reads a fixed stdin, and its endless loop stops at the end of +// input (an added 'if (!std::cin) break;', also in the oracle's copy). +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdio> +#include <boost/describe.hpp> +#include <boost/version.hpp> +import std; +import mcpplibs.mwrap.boost.json; +import mcpplibs.mwrap.boost.mp11; +import mcpplibs.mwrap.boost.variant2; +import mcpplibs.mwrap.boost.utility; +import mcpplibs.mwrap.boost.type_traits; +import mcpplibs.mwrap.boost.core; + +// Output of the ported examples is captured here; each case compares it with +// what the original program printed (built from upstream/boost alone). +inline std::ostringstream ex_out; +inline std::istringstream ex_in; +inline char ex_arg0[] = "example"; +inline char* ex_argv[] = {ex_arg0, nullptr}; +template <class... A> +int ex_printf(const char* fmt, A... a) { + int n = std::snprintf(nullptr, 0, fmt, a...); + std::string s(std::size_t(n) + 1, '\0'); + std::snprintf(s.data(), s.size(), fmt, a...); + s.resize(std::size_t(n)); + ex_out << s; + return n; +} +inline int ex_puts(const char* s) { + ex_out << s << '\n'; + return 0; +} + +// libs/describe/example/console.cpp +namespace ex_console { +#if defined(_MSC_VER) +# pragma warning(disable: 4702) // unreachable code +#endif + + +// get variable + +template<class Scope> boost::json::value get( Scope& scope, boost::string_view name ) +{ + using Md = boost::describe::describe_members<Scope, boost::describe::mod_public>; + + bool found = false; + boost::json::value result; + + boost::mp11::mp_for_each<Md>([&](auto D) { + + if( !found && name == D.name ) + { + result = boost::json::value_from( scope.*D.pointer ); + found = true; + } + + }); + + if( !found ) + { + throw std::invalid_argument( + std::string( "'" ) + std::string( name ) + "': no such variable" ); + } + + return result; +} + +// set variable + +template<class T> void set_impl( T & t, boost::string_view /*name*/, boost::json::value const& value ) +{ + t = boost::json::value_to<T>( value ); +} + +template<class T> void set_impl( T const & /*t*/, boost::string_view name, boost::json::value const& /*value*/ ) +{ + throw std::invalid_argument( + std::string( "'" ) + std::string( name ) + "': variable cannot be modified" ); +} + +template<class Scope> void set( Scope& scope, boost::string_view name, boost::json::value const& value ) +{ + using Md = boost::describe::describe_members<Scope, boost::describe::mod_public>; + + bool found = false; + + boost::mp11::mp_for_each<Md>([&](auto D) { + + if( !found && name == D.name ) + { + set_impl( scope.*D.pointer, name, value ); + found = true; + } + + }); + + if( !found ) + { + throw std::invalid_argument( + std::string( "'" ) + std::string( name ) + "': no such variable" ); + } +} + +// + +struct globals +{ + std::string const help = "Enter a variable name ('x', 'y', 'v', or 'm') to print its value; enter variable=value to assign a new value to a variable. Values are in JSON format."; + + int x = 1; + + double y = 3.14; + + std::vector<int> v{ 1, 2, 3 }; + + std::map<std::string, double> m{ { "BTC", 44898.68 }, { "ETH", 1386.57 } }; +}; + +BOOST_DESCRIBE_STRUCT( globals, (), (help, x, y, v, m) ) + +int example_main() +{ + globals g_; + + for( ;; ) + { + ex_out << "\n> "; + + std::string line; + std::getline( ex_in, line ); + if( !ex_in ) break; // boost-m port: stop at the end of input + + try + { + std::size_t i = line.find( '=' ); + + if( i != std::string::npos ) + { + set( g_, line.substr( 0, i ), boost::json::parse( line.substr( i + 1 ) ) ); + } + else + { + ex_out << get( g_, line ) << std::endl; + } + } + catch( std::exception const& x ) + { + ex_out << "Error: " << x.what() << std::endl; + } + } + return 0; + return 0; // main's implicit return +} + +TEST_CASE("example: console") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + ex_in.clear(); + ex_in.str(R"ex(x +x=42 +x +v=[4,5] +v +m +help=1 +zzz +y=2.5 +y +)ex"); // the program's stdin + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex( +> 1 + +> +> 42 + +> +> [4,5] + +> {"BTC":4.489868E4,"ETH":1.38657E3} + +> Error: 'help': variable cannot be modified + +> Error: 'zzz': no such variable + +> +> 2.5E0 + +> )ex")); +} +} // namespace ex_console + +// libs/describe/example/from_json.cpp +namespace ex_from_json { +namespace app +{ + +#if BOOST_VERSION < 108100 + +template<class T> void extract( boost::json::object const & obj, char const * name, T & value ) +{ + value = boost::json::value_to<T>( obj.at( name ) ); +} + +template<class T, + class D1 = boost::describe::describe_members<T, + boost::describe::mod_public | boost::describe::mod_protected>, + class D2 = boost::describe::describe_members<T, boost::describe::mod_private>, + class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value && !std::is_union<T>::value> > + + T tag_invoke( boost::json::value_to_tag<T> const&, boost::json::value const& v ) +{ + auto const& obj = v.as_object(); + + T t{}; + + boost::mp11::mp_for_each<D1>([&](auto D){ + + extract( obj, D.name, t.*D.pointer ); + + }); + + return t; +} + +#endif + +struct A +{ + int x; + int y; +}; + +BOOST_DESCRIBE_STRUCT(A, (), (x, y)) + +} // namespace app + + +int example_main() +{ + boost::json::value jv{ { "x", 1 }, { "y", 2 } }; + + ex_out << "jv: " << jv << std::endl; + + auto a = boost::json::value_to<app::A>( jv ); + + ex_out << "a: { " << a.x << ", " << a.y << " }" << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: from_json") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(jv: {"x":1,"y":2} +a: { 1, 2 } +)ex")); +} +} // namespace ex_from_json + +// libs/describe/example/json_rpc.cpp +namespace ex_json_rpc { +template<class C1, class C2, class R, class... A, std::size_t... I> + boost::json::value + call_impl_( C1 & c1, R (C2::*pmf)(A...), boost::json::array const & args, + std::index_sequence<I...> ) +{ + return boost::json::value_from( + (c1.*pmf)( + boost::json::value_to< boost::remove_cv_ref_t<A> >( args[ I ] )... ) ); +} + +template<class C1, class C2, class R, class... A> + boost::json::value + call_impl( C1 & c1, R (C2::*pmf)(A...), boost::json::array const & args ) +{ + if( args.size() != sizeof...(A) ) + { + throw std::invalid_argument( "Invalid number of arguments" ); + } + + return call_impl_( c1, pmf, args, std::index_sequence_for<A...>() ); +} + +template<class C> + boost::json::value + call( C & c, boost::string_view method, boost::json::array const & args ) +{ + using Fd = boost::describe::describe_members<C, + boost::describe::mod_public | boost::describe::mod_function>; + + bool found = false; + boost::json::value result; + + boost::mp11::mp_for_each<Fd>([&](auto D){ + + if( !found && method == D.name) + { + result = call_impl( c, D.pointer, args ); + found = true; + } + + }); + + if( !found ) + { + throw std::invalid_argument( "Invalid method name" ); + } + + return result; +} + +struct Object +{ + std::string greet( std::string const & who ) + { + return "Hello, " + who + "!"; + } + + int add( int x, int y ) + { + return x + y; + } +}; + +BOOST_DESCRIBE_STRUCT(Object, (), (greet, add)) + + +int example_main() +{ + Object obj; + ex_out << call( obj, "greet", { "world" } ) << std::endl; + ex_out << call( obj, "add", { 1, 2 } ) << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: json_rpc") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex("Hello, world!" +3 +)ex")); +} +} // namespace ex_json_rpc + +// libs/describe/example/to_json.cpp +namespace ex_to_json { +namespace app +{ + +#if BOOST_VERSION < 108100 + +template<class T, + class D1 = boost::describe::describe_members<T, + boost::describe::mod_public | boost::describe::mod_protected>, + class D2 = boost::describe::describe_members<T, boost::describe::mod_private>, + class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value && !std::is_union<T>::value> > + + void tag_invoke( boost::json::value_from_tag const&, boost::json::value& v, T const & t ) +{ + auto& obj = v.emplace_object(); + + boost::mp11::mp_for_each<D1>([&](auto D){ + + obj[ D.name ] = boost::json::value_from( t.*D.pointer ); + + }); +} + +#endif + +struct A +{ + int x; + int y; +}; + +BOOST_DESCRIBE_STRUCT(A, (), (x, y)) + +struct B +{ + std::vector<A> v; + std::map<std::string, A> m; +}; + +BOOST_DESCRIBE_STRUCT(B, (), (v, m)) + +} // namespace app + + +int example_main() +{ + app::B b{ { { 1, 2 }, { 3, 4 } }, { { "k1", { 5, 6 } }, { "k2", { 7, 8 } } } }; + + ex_out << boost::json::value_from( b ) << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: to_json") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex({"v":[{"x":1,"y":2},{"x":3,"y":4}],"m":{"k1":{"x":5,"y":6},"k2":{"x":7,"y":8}}} +)ex")); +} +} // namespace ex_to_json diff --git a/tests/describe/examples_serialization.cpp b/tests/describe/examples_serialization.cpp new file mode 100644 index 00000000..002deb25 --- /dev/null +++ b/tests/describe/examples_serialization.cpp @@ -0,0 +1,184 @@ +// boost.describe with the serialization module — libs/describe/example/ +// serialization.cpp: described classes serialized through a generic +// serialize() written with describe_members, archives from the module. +// Output must equal the original program's. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdio> +#include <boost/describe.hpp> +import std; +import mcpplibs.mwrap.boost.serialization; +import mcpplibs.mwrap.boost.mp11; +import mcpplibs.mwrap.boost.core; + +// Output of the ported examples is captured here; each case compares it with +// what the original program printed (built from upstream/boost alone). +inline std::ostringstream ex_out; +inline std::istringstream ex_in; +inline char ex_arg0[] = "example"; +inline char* ex_argv[] = {ex_arg0, nullptr}; +template <class... A> +int ex_printf(const char* fmt, A... a) { + int n = std::snprintf(nullptr, 0, fmt, a...); + std::string s(std::size_t(n) + 1, '\0'); + std::snprintf(s.data(), s.size(), fmt, a...); + s.resize(std::size_t(n)); + ex_out << s; + return n; +} +inline int ex_puts(const char* s) { + ex_out << s << '\n'; + return 0; +} + +// libs/describe/example/serialization.cpp +namespace ex_serialization { +#define _CRT_SECURE_NO_WARNINGS + + +namespace app +{ + +template<class Archive, class T, + class D1 = boost::describe::describe_bases<T, boost::describe::mod_public>, + class D2 = boost::describe::describe_bases<T, + boost::describe::mod_protected | boost::describe::mod_private>, + class D3 = boost::describe::describe_members<T, + boost::describe::mod_public | boost::describe::mod_protected>, + class D4 = boost::describe::describe_members<T, boost::describe::mod_private>, + class En = std::enable_if_t< boost::mp11::mp_empty<D2>::value && + boost::mp11::mp_empty<D4>::value && !std::is_union<T>::value> > + + void serialize( Archive & ar, T & t, boost::serialization::version_type ) +{ + int k = 0; + + // public bases: use base_object + + boost::mp11::mp_for_each<D1>([&](auto D){ + + using B = typename decltype(D)::type; + + char name[ 32 ]; + std::sprintf( name, "base.%d", ++k ); + + ar & boost::make_nvp( name, boost::serialization::base_object<B>( t ) ); + + }); + + // public (and protected) members + + boost::mp11::mp_for_each<D3>([&](auto D){ + + ar & boost::make_nvp( D.name, t.*D.pointer ); + + }); +} + +struct A1 +{ + int x; +}; + +BOOST_DESCRIBE_STRUCT(A1, (), (x)) + +struct A2 +{ + int y; +}; + +BOOST_DESCRIBE_STRUCT(A2, (), (y)) + +struct B: public A1, public A2 +{ + // these constructors aren't needed in C++17 + B(): A1(), A2() {} + B( int x, int y ): A1{ x }, A2{ y } {} +}; + +BOOST_DESCRIBE_STRUCT(B, (A1, A2), ()) + +struct C +{ + std::vector<B> v; +}; + +BOOST_DESCRIBE_STRUCT(C, (), (v)) + +} // namespace app + + +int example_main() +{ + app::C c1{ { { 1, 2 }, { 3, 4 }, { 5, 6 } } }; + + std::ostringstream os; + + { + boost::archive::xml_oarchive ar( os ); + ar << boost::make_nvp( "c1", c1 ); + } + + std::string s = os.str(); + + ex_out << s << std::endl; + + app::C c2; + + { + std::istringstream is( s ); + boost::archive::xml_iarchive ar( is ); + ar >> boost::make_nvp( "c2", c2 ); + } + + { + boost::archive::text_oarchive ar( ex_out ); + ar << c2; + } + return 0; // main's implicit return +} + +TEST_CASE("example: serialization") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="20"> +<c1 class_id="0" tracking_level="0" version="0"> + <v class_id="1" tracking_level="0" version="0"> + <count>3</count> + <item_version>0</item_version> + <item class_id="2" tracking_level="0" version="0"> + <base.1 class_id="3" tracking_level="0" version="0"> + <x>1</x> + </base.1> + <base.2 class_id="4" tracking_level="0" version="0"> + <y>2</y> + </base.2> + </item> + <item> + <base.1> + <x>3</x> + </base.1> + <base.2> + <y>4</y> + </base.2> + </item> + <item> + <base.1> + <x>5</x> + </base.1> + <base.2> + <y>6</y> + </base.2> + </item> + </v> +</c1> +</boost_serialization> + + +22 serialization::archive 20 0 0 0 0 3 0 0 0 0 0 1 0 0 2 3 4 5 6 +)ex")); +} +} // namespace ex_serialization diff --git a/tests/describe/include_custom_types.cpp b/tests/describe/include_custom_types.cpp new file mode 100644 index 00000000..3a41b4b5 --- /dev/null +++ b/tests/describe/include_custom_types.cpp @@ -0,0 +1,146 @@ +// boost.describe — user types described with the BOOST_DESCRIBE_* macros, the +// library's whole purpose: the class and enum pages of the documentation +// (libs/describe/doc/describe/{classes,enums}.adoc) plus the queries over the +// descriptors (members, bases, modifiers, static and function members, +// overloads, nested enums). Pure include: describe has no module. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <string> +#include <string_view> +#include <type_traits> +#include <vector> +#include <boost/describe.hpp> +#include <boost/mp11.hpp> + +using namespace boost::describe; + +namespace user { +struct X { + int m1; + int m2; +}; +BOOST_DESCRIBE_STRUCT(X, (), (m1, m2)) + +class Y : private X { +public: + int m3 = 3; +protected: + int m4 = 4; +private: + int m5 = 5; +public: + int f() const { return m5; } + static int s; +private: + BOOST_DESCRIBE_CLASS(Y, (X), (m3, f, s), (m4), (m5)) +}; +inline int Y::s = 9; + +struct O { + int f() { return 1; } + int f() const { return 2; } + void f(int) {} +}; +BOOST_DESCRIBE_STRUCT(O, (), ((int()) f, (int() const) f, (void(int)) f)) + +class S { +public: + static void f(int) {} + static void f(int, int) {} + BOOST_DESCRIBE_CLASS(S, (), ((void(int)) f, (void(int, int)) f), (), ()) +}; + +enum E { v1 = 11, v2, v3 = 5 }; +BOOST_DESCRIBE_ENUM(E, v1, v2, v3) +enum class F : char { a = 'a', b = 'b' }; +BOOST_DESCRIBE_ENUM(F, a, b) +BOOST_DEFINE_ENUM_CLASS(G, red, green, blue) + +struct Outer { + enum class Inner { p, q }; + BOOST_DESCRIBE_NESTED_ENUM(Inner, p, q) +}; + +template <class T, unsigned M = mod_any_access> +std::vector<std::string> member_names() { + std::vector<std::string> out; + boost::mp11::mp_for_each<describe_members<T, M>>([&](auto D) { out.push_back(D.name); }); + return out; +} +} // namespace user + +TEST_CASE("doc: classes#class-types-with-public-members") { + using user::X; + static_assert(has_describe_members<X>::value); + CHECK(user::member_names<X, mod_public>() == std::vector<std::string>{"m1", "m2"}); + X x{1, 2}; + int sum = 0; + boost::mp11::mp_for_each<describe_members<X, mod_public>>([&](auto D) { sum += x.*D.pointer; }); + CHECK(sum == 3); +} + +TEST_CASE("doc: classes#class-types-with-protected-or-private-members") { + using user::Y; + CHECK(user::member_names<Y, mod_public>() == std::vector<std::string>{"m3"}); + CHECK(user::member_names<Y, mod_protected>() == std::vector<std::string>{"m4"}); + CHECK(user::member_names<Y, mod_private>() == std::vector<std::string>{"m5"}); + CHECK(user::member_names<Y, mod_any_access | mod_inherited>().size() == 5); // + X's m1, m2 + using B = describe_bases<Y, mod_any_access>; + static_assert(boost::mp11::mp_size<B>::value == 1); + using D1 = boost::mp11::mp_front<B>; + static_assert(std::is_same_v<D1::type, user::X>); + static_assert(D1::modifiers == mod_private); +} + +TEST_CASE("doc: classes#retrieving-class-properties") { + using user::Y; + Y y; + std::vector<std::string> fns; + boost::mp11::mp_for_each<describe_members<Y, mod_public | mod_function>>([&](auto D) { + fns.push_back(D.name); + CHECK((y.*D.pointer)() == 5); + }); + CHECK(fns == std::vector<std::string>{"f"}); + boost::mp11::mp_for_each<describe_members<Y, mod_public | mod_static>>([&](auto D) { + CHECK(std::string(D.name) == "s"); + CHECK(*D.pointer == 9); + }); + static_assert(!has_describe_members<int>::value); + static_assert(has_describe_bases<Y>::value); +} + +TEST_CASE("doc: classes#overloaded-member-functions") { + user::O o; + const user::O& co = o; + int calls = 0; + boost::mp11::mp_for_each<describe_members<user::O, mod_public | mod_function>>([&](auto D) { + using P = std::remove_cv_t<decltype(D.pointer)>; + if constexpr (std::is_same_v<P, int (user::O::*)()>) calls += (o.*D.pointer)(); + if constexpr (std::is_same_v<P, int (user::O::*)() const>) calls += (co.*D.pointer)() * 10; + if constexpr (std::is_same_v<P, void (user::O::*)(int)>) (o.*D.pointer)(0); + }); + CHECK(calls == 21); + int statics = 0; + boost::mp11::mp_for_each<describe_members<user::S, mod_public | mod_static | mod_function>>( + [&](auto) { ++statics; }); + CHECK(statics == 2); +} + +TEST_CASE("doc: enums#enums") { + std::vector<std::string> names; + std::vector<int> values; + boost::mp11::mp_for_each<describe_enumerators<user::E>>([&](auto D) { + names.push_back(D.name); + values.push_back(D.value); + }); + CHECK(names == std::vector<std::string>{"v1", "v2", "v3"}); + CHECK(values == std::vector<int>{11, 12, 5}); + CHECK(std::string_view(enum_to_string(user::F::b, "?")) == "b"); + user::F f{}; + CHECK(enum_from_string("a", f)); + CHECK(f == user::F::a); + CHECK_FALSE(enum_from_string("z", f)); + CHECK(std::string_view(enum_to_string(user::G::green, "?")) == "green"); + CHECK(std::string_view(enum_to_string(user::Outer::Inner::q, "?")) == "q"); + static_assert(has_describe_enumerators<user::G>::value); +} diff --git a/tests/describe/include_examples.cpp b/tests/describe/include_examples.cpp new file mode 100644 index 00000000..3d39d510 --- /dev/null +++ b/tests/describe/include_examples.cpp @@ -0,0 +1,661 @@ +// boost.describe — the official examples (libs/describe/example/*.cpp) that use +// describe with header-only companions (mp11, container_hash, core, +// type_traits, variant2, utility): describe has no module (its API is the +// BOOST_DESCRIBE_* macros, docs/usage.md §2.3), so this TU includes and does +// not import. Each program runs as a case; its output must equal what the +// same code printed when built from upstream/boost headers alone. The examples that +// need a compiled library (json, serialization) are examples_json.cpp and +// examples_serialization.cpp; the fmtlib ones are excluded (fmt is not +// vendored). +// pm_to_string.cpp names its own ::detail namespace; in the port it is the +// case namespace's detail. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdio> +#include <boost/describe.hpp> +#include <boost/mp11.hpp> +#include <iostream> +#include <boost/variant2/variant.hpp> +#include <vector> +#include <boost/container_hash/hash.hpp> +#include <boost/version.hpp> +#include <boost/core/type_name.hpp> +#include <string> +#include <ostream> +#include <array> +#include <stdexcept> +#include <typeinfo> +#include <cstring> +#include <tuple> +#include <sstream> +#include <string> +#include <string_view> + +// Output of the ported examples is captured here; each case compares it with +// what the original program printed (built from upstream/boost alone). +inline std::ostringstream ex_out; +inline std::istringstream ex_in; +inline char ex_arg0[] = "example"; +inline char* ex_argv[] = {ex_arg0, nullptr}; +template <class... A> +int ex_printf(const char* fmt, A... a) { + int n = std::snprintf(nullptr, 0, fmt, a...); + std::string s(std::size_t(n) + 1, '\0'); + std::snprintf(s.data(), s.size(), fmt, a...); + s.resize(std::size_t(n)); + ex_out << s; + return n; +} +inline int ex_puts(const char* s) { + ex_out << s << '\n'; + return 0; +} + +// libs/describe/example/enum_to_string.cpp +namespace ex_enum_to_string { +template<class E> char const * enum_to_string( E e ) +{ + char const * r = "(unnamed)"; + + boost::mp11::mp_for_each< boost::describe::describe_enumerators<E> >([&](auto D){ + + if( e == D.value ) r = D.name; + + }); + + return r; +} + + +enum E +{ + v1 = 3, + v2, + v3 = 11 +}; + +BOOST_DESCRIBE_ENUM(E, v1, v2, v3) + +int example_main() +{ + ex_out << "E(" << v1 << "): " << enum_to_string( v1 ) << std::endl; + ex_out << "E(" << 0 << "): " << enum_to_string( E(0) ) << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: enum_to_string") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(E(3): v1 +E(0): (unnamed) +)ex")); +} +} // namespace ex_enum_to_string + +// libs/describe/example/equality.cpp +namespace ex_equality { +using namespace boost::describe; + +namespace app +{ +template<class T, + class Bd = describe_bases<T, mod_any_access>, + class Md = describe_members<T, mod_any_access>, + class En = std::enable_if_t<!std::is_union<T>::value> > + bool operator==( T const& t1, T const& t2 ) +{ + bool r = true; + + boost::mp11::mp_for_each<Bd>([&](auto D){ + + using B = typename decltype(D)::type; + r = r && (B const&)t1 == (B const&)t2; + + }); + + boost::mp11::mp_for_each<Md>([&](auto D){ + + r = r && t1.*D.pointer == t2.*D.pointer; + + }); + + return r; +} + +struct A +{ + int x = 1; +}; + +BOOST_DESCRIBE_STRUCT(A, (), (x)) + +struct B +{ + int y = 2; +}; + +BOOST_DESCRIBE_STRUCT(B, (), (y)) + +struct C +{ + std::vector<boost::variant2::variant<A, B>> v; +}; + +BOOST_DESCRIBE_STRUCT(C, (), (v)) + +} // namespace app + + +int example_main() +{ + app::C c1, c2, c3; + + c1.v.push_back( app::A{} ); + c2.v.push_back( app::A{} ); + c3.v.push_back( app::B{} ); + + ex_out << std::boolalpha + << ( c1 == c2 ) << ' ' + << ( c1 == c3 ) << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: equality") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(true false +)ex")); +} +} // namespace ex_equality + +// libs/describe/example/hash_value.cpp +namespace ex_hash_value { +using namespace boost::describe; + +namespace app +{ + +#if BOOST_VERSION < 108100 + +template<class T, + class Bd = describe_bases<T, mod_any_access>, + class Md = describe_members<T, mod_any_access>, + class En = std::enable_if_t<!std::is_union<T>::value> > + std::size_t hash_value( T const & t ) +{ + std::size_t r = 0; + + boost::mp11::mp_for_each<Bd>([&](auto D){ + + using B = typename decltype(D)::type; + boost::hash_combine( r, (B const&)t ); + + }); + + boost::mp11::mp_for_each<Md>([&](auto D){ + + boost::hash_combine( r, t.*D.pointer ); + + }); + + return r; +} + +#endif + +struct A +{ + int x = 1; +}; + +BOOST_DESCRIBE_STRUCT(A, (), (x)) + +struct B +{ + int y = 2; +}; + +BOOST_DESCRIBE_STRUCT(B, (), (y)) + +struct C +{ + std::vector<boost::variant2::variant<A, B>> v; +}; + +BOOST_DESCRIBE_STRUCT(C, (), (v)) + +} // namespace app + + +int example_main() +{ + app::C c; + + c.v.push_back( app::A{} ); + c.v.push_back( app::B{} ); + + ex_out << boost::hash<app::C>()( c ) << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: hash_value") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(14636833987593068677 +)ex")); +} +} // namespace ex_hash_value + +// libs/describe/example/pm_to_string.cpp +namespace ex_pm_to_string { +#if defined(__GNUC__) && __GNUC__ == 12 +# pragma GCC diagnostic ignored "-Wrestrict" // false positive +#endif + + +namespace detail +{ + +template<class T1, class T2> +constexpr auto equals( T1 t1, T2 t2, int ) -> decltype( t1 == t2 ) +{ + return t1 == t2; +} + +template<class T1, class T2> +constexpr bool equals( T1 /*t1*/, T2 /*t2*/, long ) +{ + return false; +} + +using namespace boost::describe; + +template<class T, class C, + class L = describe_members<C, mod_any_access | mod_any_member>> +char const* get_member_name( T C::* pm, int ) +{ + char const * name = nullptr; + + boost::mp11::mp_for_each<L>([&](auto D){ + + if( equals( D.pointer, pm, 0 ) ) name = D.name; + + }); + + return name; +} + +template<class T, class C> +char const* get_member_name( T C::* /*pm*/, long ) +{ + return nullptr; +} + +} // namespace detail + +template<class T, class C> +std::string pm_to_string( T C::* pm ) +{ + char const * name = detail::get_member_name( pm, 0 ); + + if( name == nullptr ) name = "(unknown)"; + + return "&" + boost::core::type_name<C>() + "::" + name + + " [" + boost::core::type_name<T>() + "]"; +} + +template<class T, class C> +std::ostream& operator<<( std::ostream& os, T C::* pm ) +{ + os << pm_to_string( pm ); + return os; +} + +struct X +{ + int m; + int f() const { return m; } +}; + +BOOST_DESCRIBE_STRUCT(X, (), (m, f)) + +struct Y: public X +{ + int m; + int g() const { return -m; } +}; + +BOOST_DESCRIBE_STRUCT(Y, (X), (m, g)) + +struct Z +{ + void h() {} +}; + +#if !defined(_MSC_VER) || defined(__clang__) + +// MSVC doesn't support BOOST_DESCRIBE_CLASS inside +// templates until 2022 in C++20 mode + +template<class T1, class T2> struct pair +{ + T1 first; + T2 second; + + BOOST_DESCRIBE_CLASS(pair, (), (first, second), (), ()) +}; + +#endif + + +int example_main() +{ + ex_out << &X::m << std::endl; + ex_out << &X::f << std::endl; + + ex_out << &Y::m << std::endl; + ex_out << &Y::f << std::endl; + ex_out << &Y::g << std::endl; + + ex_out << &Z::h << std::endl; + +#if !defined(_MSC_VER) || defined(__clang__) + ex_out << &pair<int, float>::second << std::endl; +#endif + return 0; // main's implicit return +} + +TEST_CASE("example: pm_to_string") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(&ex_pm_to_string::X::m [int] +&ex_pm_to_string::X::f [int() const] +&ex_pm_to_string::Y::m [int] +&ex_pm_to_string::X::f [int() const] +&ex_pm_to_string::Y::g [int() const] +&ex_pm_to_string::Z::(unknown) [void()] +&ex_pm_to_string::pair<int, float>::second [float] +)ex")); +} +} // namespace ex_pm_to_string + +// libs/describe/example/print_function.cpp +namespace ex_print_function { +using namespace boost::describe; + +template<class T, + class Bd = describe_bases<T, mod_any_access>, + class Md = describe_members<T, mod_any_access>, + class En = std::enable_if_t<!std::is_union<T>::value> > + std::ostream& operator<<( std::ostream & os, T const & t ) +{ + os << "{"; + + bool first = true; + + boost::mp11::mp_for_each<Bd>([&](auto D){ + + if( !first ) { os << ", "; } first = false; + + using B = typename decltype(D)::type; + os << (B const&)t; + + }); + + boost::mp11::mp_for_each<Md>([&](auto D){ + + if( !first ) { os << ", "; } first = false; + + os << "." << D.name << " = " << t.*D.pointer; + + }); + + os << "}"; + return os; +} + +struct X +{ + int m1 = 1; +}; + +BOOST_DESCRIBE_STRUCT(X, (), (m1)) + +struct Y +{ + int m2 = 2; +}; + +BOOST_DESCRIBE_STRUCT(Y, (), (m2)) + +class Z: public X, private Y +{ + int m1 = 3; + int m2 = 4; + + BOOST_DESCRIBE_CLASS(Z, (X, Y), (), (), (m1, m2)) +}; + + +int example_main() +{ + ex_out << Z() << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: print_function") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex({{.m1 = 1}, {.m2 = 2}, .m1 = 3, .m2 = 4} +)ex")); +} +} // namespace ex_print_function + +// libs/describe/example/printing_enums_ct.cpp +namespace ex_printing_enums_ct { +enum E +{ + v1 = 11, + v2, + v3 = 5 +}; + +BOOST_DESCRIBE_ENUM(E, v1, v2, v3) + +int example_main() +{ + boost::mp11::mp_for_each< boost::describe::describe_enumerators<E> >([](auto D){ + + ex_printf( "%s: %d\n", D.name, D.value ); + + }); + return 0; // main's implicit return +} + +TEST_CASE("example: printing_enums_ct") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(v1: 11 +v2: 12 +v3: 5 +)ex")); +} +} // namespace ex_printing_enums_ct + +// libs/describe/example/printing_enums_rt.cpp +namespace ex_printing_enums_rt { +template<class E> struct enum_descriptor +{ + E value; + char const * name; +}; + +template<class E, template<class... T> class L, class... T> + constexpr std::array<enum_descriptor<E>, sizeof...(T)> + describe_enumerators_as_array_impl( L<T...> ) +{ + return { { { T::value, T::name }... } }; +} + +template<class E> constexpr auto describe_enumerators_as_array() +{ + return describe_enumerators_as_array_impl<E>( boost::describe::describe_enumerators<E>() ); +} + +BOOST_DEFINE_ENUM(E, v1, v2, v3, v4, v5, v6) + +int example_main() +{ + constexpr auto D = describe_enumerators_as_array<E>(); + + for( auto const& x: D ) + { + ex_printf( "%s: %d\n", x.name, x.value ); + } + return 0; // main's implicit return +} + +TEST_CASE("example: printing_enums_rt") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(v1: 0 +v2: 1 +v3: 2 +v4: 3 +v5: 4 +v6: 5 +)ex")); +} +} // namespace ex_printing_enums_rt + +// libs/describe/example/string_to_enum.cpp +namespace ex_string_to_enum { +[[noreturn]] void throw_invalid_name( char const * name, char const * type ) +{ + throw std::runtime_error( + std::string( "Invalid enumerator name '" ) + name + + "' for enum type '" + type + "'" ); +} + +template<class E> E string_to_enum( char const * name ) +{ + bool found = false; + E r = {}; + + boost::mp11::mp_for_each< boost::describe::describe_enumerators<E> >([&](auto D){ + + if( !found && std::strcmp( D.name, name ) == 0 ) + { + found = true; + r = D.value; + } + + }); + + if( found ) + { + return r; + } + else + { + throw_invalid_name( name, typeid( E ).name() ); + } +} + + +BOOST_DEFINE_ENUM(E, v1, v2, v3) + +int example_main() +{ + try + { + ex_out << "v1: " << string_to_enum<E>( "v1" ) << std::endl; + ex_out << "v2: " << string_to_enum<E>( "v2" ) << std::endl; + ex_out << "v3: " << string_to_enum<E>( "v3" ) << std::endl; + ex_out << "v4: " << string_to_enum<E>( "v4" ) << std::endl; + } + catch( std::exception const & x ) + { + ex_out << x.what() << std::endl; + } + return 0; // main's implicit return +} + +TEST_CASE("example: string_to_enum") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(v1: 0 +v2: 1 +v3: 2 +v4: Invalid enumerator name 'v4' for enum type 'N17ex_string_to_enum1EE' +)ex")); +} +} // namespace ex_string_to_enum + +// libs/describe/example/struct_to_tuple.cpp +namespace ex_struct_to_tuple { +namespace desc = boost::describe; + +template<class T, template<class...> class L, class... D> +auto struct_to_tuple_impl( T const& t, L<D...> ) +{ + return std::make_tuple( t.*D::pointer... ); +} + +template<class T, + class Dm = desc::describe_members<T, + desc::mod_public | desc::mod_inherited>, + class En = std::enable_if_t<!std::is_union<T>::value> > +auto struct_to_tuple( T const& t ) +{ + return struct_to_tuple_impl( t, Dm() ); +} + + +struct X +{ + int a = 1; +}; + +BOOST_DESCRIBE_STRUCT(X, (), (a)) + +struct Y +{ + float b = 3.14f; +}; + +BOOST_DESCRIBE_STRUCT(Y, (), (b)) + +struct Z: X, Y +{ +}; + +BOOST_DESCRIBE_STRUCT(Z, (X, Y), ()) + +int example_main() +{ + Z z; + + auto tp = struct_to_tuple( z ); + + ex_out << + boost::core::type_name<decltype(tp)>() << ": " + << std::get<0>(tp) << ", " << std::get<1>(tp); + return 0; // main's implicit return +} + +TEST_CASE("example: struct_to_tuple") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(std::tuple<int, float>: 1, 3.14)ex")); +} +} // namespace ex_struct_to_tuple diff --git a/tools/ci/test_coverage.py b/tools/ci/test_coverage.py index 2bb50d71..63021378 100644 --- a/tools/ci/test_coverage.py +++ b/tools/ci/test_coverage.py @@ -37,6 +37,8 @@ libs/<lib>/**/<page>.qbk#<section> TEST_CASE("doc: <page>#<section>"), or TEST_CASE("doc: <section>") when no other page of the library has <section> + ...#example_<stem> TEST_CASE("example: <stem>") (sections + that present an example program) Documentation sections are the granularity for snippets: a qbk ``[section ...]`` or an AsciiDoc / Markdown heading whose body has a code @@ -174,9 +176,11 @@ def flush(): fence = None continue s = line.strip() - if s.startswith("```") or s == "----" or s == "....": + # ``` fences, and AsciiDoc ---- (listing) / .... (literal) blocks of + # four or more delimiter characters. + if s.startswith("```") or re.fullmatch(r"-{4,}|\.{4,}", s): fence = "```" if s.startswith("```") else s - if s != "....": + if not s.startswith("."): has_code = True continue m = re.match(r"^\[#([\w.-]+)\]\s*$", s) @@ -403,6 +407,8 @@ def cmd_link(args): wanted = ["doc: {}#{}".format(Path(doc).stem, sid)] if sids[sid] == 1: wanted.append("doc: " + sid) + if sid.startswith("example_"): # a page section per example + wanted.append("example: " + sid[len("example_"):]) hit = next((w for w in wanted if w in cases), None) if hit is None: todo.append(e[key]) @@ -436,7 +442,7 @@ def cmd_check(_args): totals[lib] = counts if lib in CUSTOM_TYPES: base = TESTS / lib - hits = list(base.rglob("custom_types*.cpp")) if base.is_dir() else [] + hits = list(base.rglob("*custom_types*.cpp")) if base.is_dir() else [] if not hits: errors.append("tests/{}/custom_types.cpp is missing (the " "library has customization points)".format(lib)) From c660a79d6e3925cfc5eda1c65cc67a8156dd6f71 Mon Sep 17 00:00:00 2001 From: Zhe Feng <zhefeng7110@outlook.com> Date: Mon, 9 Nov 2026 14:42:51 +0000 Subject: [PATCH 7/9] tests: hash2 suite (11 examples, every algorithm, hash_append over each kind, user types/algorithms/flavors) --- tests/hash2/api.cpp | 192 +++++++++ tests/hash2/coverage.toml | 146 ++++--- tests/hash2/custom_types.cpp | 123 ++++++ tests/hash2/examples.cpp | 760 +++++++++++++++++++++++++++++++++ tests/hash2/mixed_describe.cpp | 31 ++ 5 files changed, 1181 insertions(+), 71 deletions(-) create mode 100644 tests/hash2/api.cpp create mode 100644 tests/hash2/custom_types.cpp create mode 100644 tests/hash2/examples.cpp create mode 100644 tests/hash2/mixed_describe.cpp diff --git a/tests/hash2/api.cpp b/tests/hash2/api.cpp new file mode 100644 index 00000000..d0b3ec0a --- /dev/null +++ b/tests/hash2/api.cpp @@ -0,0 +1,192 @@ +// mcpplibs.mwrap.boost.hash2 — the "Hashing Bytes" and "Hashing C++ Objects" +// pages (libs/hash2/doc/hash2/{hashing_bytes,hashing_objects}.adoc): the +// HashAlgorithm requirements over every provided algorithm, update/result +// semantics, compile-time hashing, and hash_append over each kind of type. +// Described classes are mixed_describe.cpp, user types custom_types.cpp. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdint> +import std; +import mcpplibs.mwrap.boost.hash2; + +namespace h2 = boost::hash2; + +TEST_CASE_TEMPLATE("doc: hashing_bytes#hash-algorithm-requirements", H, h2::fnv1a_32, h2::fnv1a_64, h2::siphash_32, + h2::siphash_64, h2::xxhash_32, h2::xxhash_64, h2::xxh3_128, h2::md5_128, h2::sha1_160, + h2::sha2_256, h2::sha2_224, h2::sha2_512, h2::sha2_384, h2::sha2_512_224, h2::sha2_512_256, + h2::sha3_256, h2::sha3_512, h2::shake_128, h2::ripemd_160, h2::ripemd_128, h2::blake2b_512, + h2::blake2s_256, h2::hmac_md5_128, h2::hmac_sha2_256) { + using R = typename H::result_type; + H a; // default construction + H b(std::uint64_t(7)); // integral seed + const unsigned char seed[3] = {1, 2, 3}; + H c(seed, sizeof seed); // byte seed + H d(a); // copy + d = b; // assignment + const char msg[] = "message"; + a.update(msg, 7); + H e; + e.update(msg, 7); + R ra = a.result(), re = e.result(); + CHECK(ra == re); // deterministic + CHECK(R(b.result()) != R(H().result())); // the seed matters + (void)c; + (void)d; +} + +TEST_CASE("doc: hashing_bytes#update") { + // Splitting the message across update calls does not change the result. + unsigned char message[6] = {'a', 'b', 'c', 'd', 'e', 'f'}; + h2::sha2_256 one, two, three; + one.update(message, 6); + two.update(message, 4); + two.update(message + 4, 2); + for (int i = 0; i < 6; ++i) three.update(&message[i], 1); + auto r1 = one.result(); + CHECK(r1 == two.result()); + CHECK(r1 == three.result()); +} + +TEST_CASE("doc: hashing_bytes#result") { + // result() may be called repeatedly: each call continues a pseudo-random + // sequence (the documented random() helper). + static h2::fnv1a_64 hash; + auto random = [] { return hash.result(); }; + std::uint64_t a = random(), b = random(); + CHECK(a != b); + h2::fnv1a_64 h; + h.update("x", 1); + CHECK(h2::get_integral_result<std::uint32_t>(h) != 0u); + h2::md5_128 m; + CHECK(h2::to_string(m.result()) == "d41d8cd98f00b204e9800998ecf8427e"); // MD5("") +} + +TEST_CASE("doc: hashing_bytes#compile-time-hashing") { + constexpr auto r = [] { + h2::fnv1a_32 h; + constexpr unsigned char data[] = {'a', 'b', 'c'}; + h.update(data, 3); + return h.result(); + }(); + static_assert(r == 0x1A47E90Bu); // FNV-1a 32 of "abc" + h2::fnv1a_32 rt; + rt.update("abc", 3); + CHECK(rt.result() == r); +} + +TEST_CASE("doc: hashing_objects#hashing_objects") { + // Flavors: the byte order and size_type used for scalars and sizes. + static_assert(std::is_same_v<h2::default_flavor::size_type, std::uint32_t>); + static_assert(h2::little_endian_flavor::byte_order == h2::endian::little); + static_assert(h2::big_endian_flavor::byte_order == h2::endian::big); + h2::fnv1a_32 a, b; + h2::hash_append(a, h2::big_endian_flavor(), std::uint16_t(0x0102)); + const unsigned char bytes[] = {0x01, 0x02}; + b.update(bytes, 2); + CHECK(a.result() == b.result()); +} + +TEST_CASE("doc: hashing_objects#integral-types") { + h2::fnv1a_32 h1; + std::uint32_t v1 = 0x01020304; + h2::hash_append(h1, h2::little_endian_flavor(), v1); + h2::fnv1a_32 h2_; + unsigned char v2[] = {0x04, 0x03, 0x02, 0x01}; + h2_.update(v2, sizeof(v2)); + CHECK(h1.result() == h2_.result()); +} + +TEST_CASE("doc: hashing_objects#floating-point-types") { + h2::fnv1a_32 h1, h2_; + h2::hash_append(h1, {}, 3.14f); + h2::hash_append(h2_, {}, std::uint32_t(0x4048f5c3)); + CHECK(h1.result() == h2_.result()); + h2::fnv1a_32 p, n; + h2::hash_append(p, {}, +0.0); + h2::hash_append(n, {}, -0.0); + CHECK(p.result() == n.result()); // +0 and -0 hash equal +} + +namespace { +enum E : int { v1 = 123 }; +int x1 = 0; +} // namespace + +TEST_CASE("doc: hashing_objects#enumeration-types") { + h2::fnv1a_32 h1, h2_; + h2::hash_append(h1, {}, v1); + h2::hash_append(h2_, {}, 123); + CHECK(h1.result() == h2_.result()); +} + +TEST_CASE("doc: hashing_objects#pointers") { + h2::fnv1a_32 h1, h2_; + h2::hash_append(h1, {}, &x1); + h2::hash_append(h2_, {}, reinterpret_cast<std::uintptr_t>(&x1)); + CHECK(h1.result() == h2_.result()); +} + +// result() advances the algorithm (repeated calls give a pseudo-random +// sequence), so each result is taken once. The documentation's arrays and +// ranges examples call h1.result() twice and their second assert does not +// hold, with the headers as through the module. +TEST_CASE("doc: hashing_objects#arrays") { + h2::fnv1a_32 h1, h2_, h3; + int v[4] = {1, 2, 3, 4}; + h2::hash_append(h1, {}, v); + h2::hash_append_range(h2_, {}, v + 0, v + 4); + for (int x : v) h2::hash_append(h3, {}, x); + auto r1 = h1.result(); + CHECK(r1 == h2_.result()); + CHECK(r1 == h3.result()); +} + +TEST_CASE("doc: hashing_objects#ranges") { + h2::fnv1a_32 h1, h2_, h3, h4; + std::vector<int> v1 = {1, 2, 3, 4}; + std::list<int> v2 = {1, 2, 3, 4}; + h2::hash_append(h1, {}, v1); + h2::hash_append(h2_, {}, v2); + h2::hash_append_range(h3, {}, v1.data(), v1.data() + v1.size()); + h2::hash_append_size(h3, {}, v1.size()); + h2::hash_append_range(h4, {}, v2.begin(), v2.end()); + h2::hash_append_size(h4, {}, std::distance(v2.begin(), v2.end())); + auto r1 = h1.result(), r2 = h2_.result(); + CHECK(r1 == r2); + CHECK(r1 == h3.result()); + CHECK(r2 == h4.result()); + // Unordered ranges hash order-independently. + std::unordered_set<int> s1{1, 2, 3}, s2{3, 2, 1}; + h2::fnv1a_32 u1, u2; + h2::hash_append(u1, {}, s1); + h2::hash_append(u2, {}, s2); + CHECK(u1.result() == u2.result()); + std::string str = "abc"; + h2::fnv1a_32 st1, st2; + h2::hash_append(st1, {}, str); + h2::hash_append(st2, {}, std::string_view(str)); + CHECK(st1.result() == st2.result()); +} + +TEST_CASE("doc: hashing_objects#tuples") { + h2::fnv1a_32 h1, h2_; + std::tuple<int, int, int> v1 = {1, 2, 3}; + h2::hash_append(h1, {}, v1); + h2::hash_append(h2_, {}, std::get<0>(v1)); + h2::hash_append(h2_, {}, std::get<1>(v1)); + h2::hash_append(h2_, {}, std::get<2>(v1)); + CHECK(h1.result() == h2_.result()); + h2::fnv1a_32 p1, p2; + h2::hash_append(p1, {}, std::pair(1, 2)); + h2::hash_append(p2, {}, std::array<int, 2>{1, 2}); + CHECK(p1.result() == p2.result()); +} + +TEST_CASE("hash_append_unordered_range") { + std::vector<int> v{3, 1, 2}; + h2::fnv1a_64 a, b; + h2::hash_append_unordered_range(a, {}, v.begin(), v.end()); + std::sort(v.begin(), v.end()); + h2::hash_append_unordered_range(b, {}, v.begin(), v.end()); + CHECK(a.result() == b.result()); +} diff --git a/tests/hash2/coverage.toml b/tests/hash2/coverage.toml index 14b60d95..f8bf2426 100644 --- a/tests/hash2/coverage.toml +++ b/tests/hash2/coverage.toml @@ -6,140 +6,144 @@ upstream = "1.92.0" library = "hash2" [[example]] -path = "libs/hash2/example/compile_time.cpp" -status = "todo" +path = "libs/hash2/example/compile_time.cpp" +case = "hash2/examples:example: compile_time" [[example]] -path = "libs/hash2/example/compile_time_2.cpp" -status = "todo" +path = "libs/hash2/example/compile_time_2.cpp" +case = "hash2/examples:example: compile_time_2" [[example]] -path = "libs/hash2/example/hash2sum.cpp" -status = "todo" +path = "libs/hash2/example/hash2sum.cpp" +case = "hash2/examples:example: hash2sum" [[example]] -path = "libs/hash2/example/hash_32_64.cpp" -status = "todo" +path = "libs/hash2/example/hash_32_64.cpp" +case = "hash2/examples:example: hash_32_64" [[example]] -path = "libs/hash2/example/hash_with_any_seed.cpp" -status = "todo" +path = "libs/hash2/example/hash_with_any_seed.cpp" +case = "hash2/examples:example: hash_with_any_seed" [[example]] -path = "libs/hash2/example/hash_with_byte_seed.cpp" -status = "todo" +path = "libs/hash2/example/hash_with_byte_seed.cpp" +case = "hash2/examples:example: hash_with_byte_seed" [[example]] -path = "libs/hash2/example/hash_with_uint64_seed.cpp" -status = "todo" +path = "libs/hash2/example/hash_with_uint64_seed.cpp" +case = "hash2/examples:example: hash_with_uint64_seed" [[example]] -path = "libs/hash2/example/hash_without_seed.cpp" -status = "todo" +path = "libs/hash2/example/hash_without_seed.cpp" +case = "hash2/examples:example: hash_without_seed" [[example]] -path = "libs/hash2/example/json_value.cpp" -status = "todo" +path = "libs/hash2/example/json_value.cpp" +case = "hash2/examples:example: json_value" [[example]] -path = "libs/hash2/example/md5sum.cpp" -status = "todo" +path = "libs/hash2/example/md5sum.cpp" +case = "hash2/examples:example: md5sum" [[example]] -path = "libs/hash2/example/xxh128_from_xxh64.cpp" -status = "todo" +path = "libs/hash2/example/xxh128_from_xxh64.cpp" +case = "hash2/examples:example: xxh128_from_xxh64" [[snippet]] doc = "libs/hash2/doc/hash2/examples.adoc#compile-time-hashing" title = "Compile Time Hashing" -status = "todo" +case = "hash2/examples:example: compile_time" +reason = "the section shows compile_time.cpp" [[snippet]] doc = "libs/hash2/doc/hash2/examples.adoc#hash2sum" title = "hash2sum" -status = "todo" +case = "hash2/examples:example: hash2sum" +reason = "the section shows hash2sum.cpp" [[snippet]] doc = "libs/hash2/doc/hash2/examples.adoc#md5sum" title = "md5sum" -status = "todo" +case = "hash2/examples:example: md5sum" +reason = "the section shows md5sum.cpp" [[snippet]] doc = "libs/hash2/doc/hash2/examples.adoc#result-extension" title = "Result Extension" -status = "todo" +case = "hash2/examples:example: xxh128_from_xxh64" +reason = "the section shows xxh128_from_xxh64.cpp" [[snippet]] -doc = "libs/hash2/doc/hash2/examples.adoc#use-with-unordered-containers" -title = "Use with Unordered Containers" -status = "todo" +doc = "libs/hash2/doc/hash2/examples.adoc#use-with-unordered-containers" +title = "Use with Unordered Containers" +case = "hash2/custom_types:doc: examples#use-with-unordered-containers" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#compile-time-hashing" -title = "Compile Time Hashing" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#compile-time-hashing" +title = "Compile Time Hashing" +case = "hash2/api:doc: hashing_bytes#compile-time-hashing" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#hash-algorithm-requirements" -title = "Hash Algorithm Requirements" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#hash-algorithm-requirements" +title = "Hash Algorithm Requirements" +case = "hash2/api:doc: hashing_bytes#hash-algorithm-requirements" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#result" -title = "result" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#result" +title = "result" +case = "hash2/api:doc: hashing_bytes#result" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#update" -title = "update" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_bytes.adoc#update" +title = "update" +case = "hash2/api:doc: hashing_bytes#update" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#arrays" -title = "Arrays" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#arrays" +title = "Arrays" +case = "hash2/api:doc: hashing_objects#arrays" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#described-classes" -title = "Described Classes" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#described-classes" +title = "Described Classes" +case = "hash2/mixed_describe:doc: hashing_objects#described-classes" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#enumeration-types" -title = "Enumeration Types" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#enumeration-types" +title = "Enumeration Types" +case = "hash2/api:doc: hashing_objects#enumeration-types" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#floating-point-types" -title = "Floating Point Types" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#floating-point-types" +title = "Floating Point Types" +case = "hash2/api:doc: hashing_objects#floating-point-types" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#hashing_objects" -title = "Hashing {cpp} Objects" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#hashing_objects" +title = "Hashing {cpp} Objects" +case = "hash2/api:doc: hashing_objects#hashing_objects" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#integral-types" -title = "Integral Types" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#integral-types" +title = "Integral Types" +case = "hash2/api:doc: hashing_objects#integral-types" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#pointers" -title = "Pointers" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#pointers" +title = "Pointers" +case = "hash2/api:doc: hashing_objects#pointers" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#ranges" -title = "Ranges" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#ranges" +title = "Ranges" +case = "hash2/api:doc: hashing_objects#ranges" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#tuples" -title = "Tuples" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#tuples" +title = "Tuples" +case = "hash2/api:doc: hashing_objects#tuples" [[snippet]] -doc = "libs/hash2/doc/hash2/hashing_objects.adoc#user-defined-types" -title = "User Defined Types" -status = "todo" +doc = "libs/hash2/doc/hash2/hashing_objects.adoc#user-defined-types" +title = "User Defined Types" +case = "hash2/custom_types:doc: hashing_objects#user-defined-types" diff --git a/tests/hash2/custom_types.cpp b/tests/hash2/custom_types.cpp new file mode 100644 index 00000000..eb0245aa --- /dev/null +++ b/tests/hash2/custom_types.cpp @@ -0,0 +1,123 @@ +// mcpplibs.mwrap.boost.hash2 — the library's customization points through the +// module: tag_invoke(hash_append_tag) for a user type with non-salient state +// (hashing_objects.adoc#user-defined-types), a user HashAlgorithm satisfying +// the documented requirements and used with hash_append and the result +// helpers, a user flavor (byte order and size_type), and a user hash adapter +// for unordered containers (examples.adoc#use-with-unordered-containers). +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdint> +import std; +import mcpplibs.mwrap.boost.hash2; +import mcpplibs.mwrap.boost.unordered; + +namespace h2 = boost::hash2; + +namespace user { +class X { + std::string a; + int b; + void const* c; // not part of the salient state +public: + X(std::string a, int b, void const* c) : a(std::move(a)), b(b), c(c) {} + friend bool operator==(X const& x1, X const& x2) { return x1.a == x2.a && x1.b == x2.b; } + template <class Provider, class Hash, class Flavor> + friend void tag_invoke(boost::hash2::hash_append_tag const&, Provider const&, Hash& h, Flavor const& f, + X const* v) { + boost::hash2::hash_append(h, f, v->a); + boost::hash2::hash_append(h, f, v->b); + } +}; + +// A user HashAlgorithm (djb2-xor, 32 bits) meeting the requirements. +class djb2 { +public: + using result_type = std::uint32_t; + static constexpr std::size_t block_size = 1; + constexpr djb2() = default; + explicit constexpr djb2(std::uint64_t seed) : st_(std::uint32_t(5381 ^ seed ^ (seed >> 32))) {} + djb2(void const* seed, std::size_t n) { update(seed, n); } + void update(void const* data, std::size_t n) { + auto p = static_cast<unsigned char const*>(data); + for (std::size_t i = 0; i < n; ++i) st_ = (st_ * 33) ^ p[i]; + } + constexpr result_type result() { + result_type r = st_; + st_ = st_ * 33 + 1; // further calls give further values + return r; + } +private: + std::uint32_t st_ = 5381; +}; + +struct net_flavor { // big-endian, 64-bit sizes + using size_type = std::uint64_t; + static constexpr auto byte_order = boost::hash2::endian::big; +}; + +// examples.adoc#use-with-unordered-containers: a std::hash-like adapter. +template <class T, class H = h2::siphash_64> +struct hash2_adapter { + std::uint64_t seed = 0x1234; + std::size_t operator()(T const& v) const { + H h(seed); + h2::hash_append(h, {}, v); + return h2::get_integral_result<std::size_t>(h); + } +}; +} // namespace user + +TEST_CASE("doc: hashing_objects#user-defined-types") { + int i = 0, j = 0; + user::X a("k", 1, &i), b("k", 1, &j), c("k", 2, &i); + auto hash = [](user::X const& x) { + h2::sha2_256 h; + h2::hash_append(h, {}, x); + return h.result(); + }; + CHECK(a == b); + CHECK(hash(a) == hash(b)); // c (non-salient) is not hashed + CHECK(hash(a) != hash(c)); + std::vector<user::X> v{a, c}; // ranges of user types + h2::fnv1a_32 r1, r2; + h2::hash_append(r1, {}, v); + h2::hash_append_range(r2, {}, v.begin(), v.end()); + h2::hash_append_size(r2, {}, v.size()); + CHECK(r1.result() == r2.result()); +} + +TEST_CASE("a user HashAlgorithm") { + user::djb2 h; + h2::hash_append(h, {}, std::string("abc")); + user::djb2 g; + h2::hash_append(g, {}, std::string("abc")); + CHECK(h.result() == g.result()); + user::djb2 seeded(std::uint64_t(42)); + h2::hash_append(seeded, {}, std::string("abc")); + CHECK(seeded.result() != user::djb2(h).result()); + CHECK(h2::get_integral_result<std::uint64_t>(g) != 0u); // extended from 32 bits + int k = 0; + user::X x("x", 1, &k); + user::djb2 hx; + h2::hash_append(hx, {}, x); // user algorithm x user type + (void)hx.result(); +} + +TEST_CASE("a user flavor") { + h2::fnv1a_64 a, b; + std::vector<std::uint16_t> v{0x0102}; + h2::hash_append(a, user::net_flavor{}, v); + const unsigned char expected[] = {0x01, 0x02, 0, 0, 0, 0, 0, 0, 0, 1}; // element, then 64-bit size + b.update(expected, sizeof expected); + CHECK(a.result() == b.result()); +} + +TEST_CASE("doc: examples#use-with-unordered-containers") { + int k = 0; + boost::unordered_flat_map<user::X, int, user::hash2_adapter<user::X>> m; + m.emplace(user::X("a", 1, &k), 10); + m.emplace(user::X("a", 1, nullptr), 20); // equal salient state: same key + CHECK(m.size() == 1); + std::unordered_set<std::string, user::hash2_adapter<std::string, h2::xxhash_64>> s{"x", "y"}; + CHECK(s.count("x") == 1); +} diff --git a/tests/hash2/examples.cpp b/tests/hash2/examples.cpp new file mode 100644 index 00000000..ae70fe7c --- /dev/null +++ b/tests/hash2/examples.cpp @@ -0,0 +1,760 @@ +// mcpplibs.mwrap.boost.hash2 — the official examples (libs/hash2/example/*.cpp) +// ported mechanically: each program runs as a case and its output must equal +// what the same code printed when built from upstream/boost headers alone. +// md5sum and hash2sum (command-line tools) run on two files written to the +// temporary directory; json_value hashes a boost::json::value through the +// json module and adds its hash_append overload to namespace boost. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdio> +#include <cerrno> +import std; +import mcpplibs.mwrap.boost.hash2; +import mcpplibs.mwrap.boost.json; +import mcpplibs.mwrap.boost.unordered; + +// Output of the ported examples is captured here; each case compares it with +// what the original program printed (built from upstream/boost alone). +inline std::ostringstream ex_out; +inline std::istringstream ex_in; +inline std::vector<std::string> ex_args; // argv of the ported main +inline std::vector<char*> ex_argv_store; +inline char** ex_argv = nullptr; +inline int ex_argc = 0; +struct ex_argv_t { // converts to char** and char const** (both main signatures) + char** p; + operator char**() const { return p; } + operator const char**() const { return const_cast<const char**>(p); } +}; +template <class... A> +int ex_printf(const char* fmt, A... a) { + int n = std::snprintf(nullptr, 0, fmt, a...); + std::string s(std::size_t(n) + 1, '\0'); + std::snprintf(s.data(), s.size(), fmt, a...); + s.resize(std::size_t(n)); + ex_out << s; + return n; +} +inline int ex_puts(const char* s) { + ex_out << s << '\n'; + return 0; +} + +// libs/hash2/example/compile_time.cpp +namespace ex_compile_time { +#if defined(BOOST_MSVC) && BOOST_MSVC < 1920 +# pragma warning(disable: 4307) // integral constant overflow +#endif + + +// xxd -i resource +constexpr unsigned char resource[] = { + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x6d, 0x90, + 0xcf, 0x6e, 0x83, 0x30, 0x0c, 0xc6, 0xcf, 0x45, 0xea, 0x3b, 0x78, 0x9c, + 0x4b, 0x02, 0x3d, 0x6e, 0xd0, 0x43, 0xff, 0x1c, 0x26, 0x55, 0x3b, 0x14, + 0x6d, 0xd7, 0x2a, 0x04, 0x43, 0x22, 0x95, 0x84, 0x25, 0x66, 0x8c, 0x47, + 0xda, 0x5b, 0x2e, 0x91, 0xd6, 0xcb, 0xd4, 0x93, 0x2d, 0xdb, 0xdf, 0xef, + 0xb3, 0x5d, 0x2a, 0x1a, 0x6e, 0xbb, 0x75, 0x52, 0x2a, 0x14, 0x6d, 0x8c, + 0x03, 0x92, 0x00, 0x45, 0x34, 0x66, 0xf8, 0x39, 0xe9, 0xaf, 0x2a, 0x75, + 0xd8, 0x39, 0xf4, 0x2a, 0x05, 0x69, 0x0d, 0xa1, 0xa1, 0x2a, 0xcd, 0x5f, + 0xe0, 0xfd, 0x72, 0xae, 0x5a, 0x2b, 0x79, 0x54, 0x73, 0x25, 0xbc, 0xda, + 0xb2, 0x98, 0xa6, 0x91, 0xc0, 0xef, 0xa8, 0xc6, 0xb6, 0x4b, 0x88, 0x17, + 0x6c, 0xb5, 0x43, 0x49, 0xda, 0xf4, 0x40, 0x16, 0xca, 0x80, 0x0f, 0xcc, + 0x2a, 0x7d, 0xa8, 0x7f, 0x50, 0x2c, 0xb9, 0xd8, 0x31, 0xc6, 0x22, 0xf9, + 0x8f, 0x58, 0xf2, 0xfb, 0xd6, 0x4f, 0x59, 0xb6, 0x4e, 0x56, 0x3f, 0x70, + 0xb0, 0xe3, 0xe2, 0x74, 0xaf, 0x08, 0xf6, 0x38, 0x08, 0x03, 0x47, 0x31, + 0xa3, 0xdf, 0xc0, 0x36, 0xcf, 0x8b, 0xd0, 0x3f, 0x6a, 0x4f, 0x4e, 0x37, + 0x13, 0x61, 0x0b, 0x93, 0x69, 0xd1, 0x01, 0x29, 0x84, 0xbd, 0xb5, 0x9e, + 0xa0, 0xb6, 0x1d, 0xcd, 0xc2, 0x21, 0x9c, 0xb5, 0x44, 0xe3, 0x71, 0x03, + 0x1f, 0xe8, 0xbc, 0xb6, 0x06, 0x0a, 0x96, 0x07, 0xd3, 0x55, 0x8d, 0x08, + 0x42, 0x4a, 0x3b, 0x8c, 0xc2, 0x2c, 0xf1, 0x86, 0x4e, 0xdf, 0xc2, 0xf4, + 0xeb, 0xe1, 0xf4, 0x56, 0x9f, 0xae, 0xc5, 0x35, 0x67, 0xf4, 0x4d, 0x60, + 0x5d, 0xf8, 0xcf, 0xb8, 0x80, 0xa0, 0x20, 0x89, 0xef, 0x7b, 0xe6, 0x7c, + 0x9e, 0x67, 0xd6, 0x44, 0x13, 0x66, 0x5d, 0xcf, 0xff, 0x29, 0xd6, 0x49, + 0x96, 0x85, 0x0b, 0x7e, 0x01, 0x36, 0x66, 0x95, 0x6b, 0x80, 0x01, 0x00, + 0x00 +}; + +template<std::size_t N> constexpr auto md5( unsigned char const(&a)[ N ] ) +{ + boost::hash2::md5_128 hash; + hash.update( a, N ); + return hash.result(); +} + +constexpr auto resource_digest = md5( resource ); + +int example_main() +{ + ex_out << "Resource digest: " << resource_digest << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: compile_time") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(Resource digest: 07e59d263e2a423ab48a1e2f630e9f15 +)ex")); +} +} // namespace ex_compile_time + +// libs/hash2/example/compile_time_2.cpp +namespace ex_compile_time_2 { +#if defined(BOOST_MSVC) && BOOST_MSVC < 1920 +# pragma warning(disable: 4307) // integral constant overflow +#endif + + +extern constexpr char const license[] = + +"Boost Software License - Version 1.0 - August 17th, 2003\n" +"\n" +"Permission is hereby granted, free of charge, to any person or organization\n" +"obtaining a copy of the software and accompanying documentation covered by\n" +"this license (the \"Software\") to use, reproduce, display, distribute,\n" +"execute, and transmit the Software, and to prepare derivative works of the\n" +"Software, and to permit third-parties to whom the Software is furnished to\n" +"do so, all subject to the following:\n" +"\n" +"The copyright notices in the Software and this entire statement, including\n" +"the above license grant, this restriction and the following disclaimer,\n" +"must be included in all copies of the Software, in whole or in part, and\n" +"all derivative works of the Software, unless such copies or derivative\n" +"works are solely in the form of machine-executable object code generated by\n" +"a source language processor.\n" +"\n" +"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" +"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" +"FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\n" +"SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\n" +"FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\n" +"ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n" +"DEALINGS IN THE SOFTWARE.\n" + +; + +constexpr unsigned char secret[] = { + 0xA4, 0x80, 0x0E, 0xE8, 0x20, 0x0B, 0x7C, 0x9A, + 0xF1, 0x3E, 0x3D, 0xEC, 0x64, 0x4F, 0x64, 0xCA, + 0x33, 0xCC, 0x84, 0xC8, 0x34, 0xE3, 0x08, 0xAE, + 0x92, 0x89, 0xEB, 0xD0, 0x47, 0x39, 0x87, 0xD8, +}; + +template<std::size_t N> constexpr auto hmac_sha2_256( char const(&s)[ N ] ) +{ + boost::hash2::hmac_sha2_256 hmac( secret, sizeof(secret) ); + + // N-1, in order to not include the null terminator + boost::hash2::hash_append_range( hmac, {}, s, s + N - 1 ); + + return hmac.result(); +} + +constexpr auto license_mac = hmac_sha2_256( license ); + +int example_main() +{ + ex_out << "License authentication code: " << license_mac << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: compile_time_2") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(License authentication code: 86795556f991dff7df6dbcdb7fa417670a27ad512b2927d4dc5abdc6c71b1559 +)ex")); +} +} // namespace ex_compile_time_2 + +// libs/hash2/example/hash2sum.cpp +namespace ex_hash2sum { +#define _CRT_SECURE_NO_WARNINGS + + +template<class Hash> void hash2sum( std::FILE* f, char const* fn ) +{ + Hash hash; + + int const N = 4096; + unsigned char buffer[ N ]; + + for( ;; ) + { + std::size_t n = std::fread( buffer, 1, N, f ); + + if( std::ferror( f ) ) + { + std::fprintf( stderr, "'%s': read error: %s\n", fn, std::strerror( errno ) ); + return; + } + + if( n == 0 ) break; + + hash.update( buffer, n ); + } + + std::string digest = to_string( hash.result() ); + + ex_printf( "%s *%s\n", digest.c_str(), fn ); +} + +template<class Hash> void hash2sum( char const* fn ) +{ + std::FILE* f = std::fopen( fn, "rb" ); + + if( f == 0 ) + { + std::fprintf( stderr, "'%s': open error: %s\n", fn, std::strerror( errno ) ); + } + else + { + hash2sum<Hash>( f, fn ); + std::fclose( f ); + } +} + +using namespace boost::mp11; +using namespace boost::hash2; + +using hashes = mp_list< + + md5_128, + sha1_160, + sha2_256, + sha2_224, + sha2_512, + sha2_384, + sha2_512_256, + sha2_512_224, + sha3_256, + sha3_224, + sha3_512, + sha3_384, + ripemd_160, + ripemd_128, + blake2b_512, + blake2s_256 + +>; + +constexpr char const* names[] = { + + "md5_128", + "sha1_160", + "sha2_256", + "sha2_224", + "sha2_512", + "sha2_384", + "sha2_512_256", + "sha2_512_224", + "sha3_256", + "sha3_224", + "sha3_512", + "sha3_384", + "ripemd_160", + "ripemd_128", + "blake2b_512", + "blake2s_256" +}; + +int example_main( int argc, char const* argv[] ) +{ + if( argc < 2 ) + { + std::fputs( "usage: hash2sum <hash> <files...>\n", stderr ); + return 2; + } + + std::string hash( argv[1] ); + bool found = false; + + mp_for_each< mp_iota<mp_size<hashes>> >([&](auto I){ + + if( hash == names[I] ) + { + using Hash = mp_at_c<hashes, I>; + + for( int i = 2; i < argc; ++i ) + { + hash2sum<Hash>( argv[i] ); + } + + found = true; + } + + }); + + if( !found ) + { + std::fprintf( stderr, "hash2sum: unknown hash algorithm name '%s'; use one of the following:\n\n", hash.c_str() ); + + for( char const* name: names ) + { + std::fprintf( stderr, " %s\n", name ); + } + + return 1; + } + return 0; // main's implicit return +} + +TEST_CASE("example: hash2sum") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + ex_args = {"example"}; + { std::ofstream(std::filesystem::temp_directory_path() / "boost-m-hash2-a.txt", std::ios::binary) << "The quick brown fox jumps over the lazy dog"; std::ofstream(std::filesystem::temp_directory_path() / "boost-m-hash2-b.txt", std::ios::binary) << ""; } + ex_args.push_back(std::string("md5_128")); + ex_args.push_back((std::filesystem::temp_directory_path() / "boost-m-hash2-a.txt").string()); + ex_args.push_back((std::filesystem::temp_directory_path() / "boost-m-hash2-b.txt").string()); + ex_argv_store.clear(); + for (auto& a : ex_args) ex_argv_store.push_back(a.data()); + ex_argv_store.push_back(nullptr); + ex_argv = ex_argv_store.data(); + ex_argc = int(ex_args.size()); + int rc = example_main(ex_argc, ex_argv_t{ex_argv}); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(9e107d9d372bb6826bd81d3542a419d6 */tmp/boost-m-hash2-a.txt +d41d8cd98f00b204e9800998ecf8427e */tmp/boost-m-hash2-b.txt +)ex")); +} +} // namespace ex_hash2sum + +// libs/hash2/example/hash_32_64.cpp +namespace ex_hash_32_64 { +template<class T, class H1, class H2 = H1> class hash +{ +public: + + using hash_type = typename std::conditional< + sizeof(typename H1::result_type) == sizeof(std::size_t), H1, H2 + >::type; + +private: + + hash_type h_; + +public: + + hash(): h_() + { + } + + explicit hash( std::uint64_t seed ): h_( seed ) + { + } + + hash( unsigned char const* p, std::size_t n ): h_( p, n ) + { + } + + std::size_t operator()( T const& v ) const + { + hash_type h( h_ ); + boost::hash2::hash_append( h, {}, v ); + return boost::hash2::get_integral_result<std::size_t>( h ); + } +}; + +int example_main() +{ + { + using hasher = hash<std::string, boost::hash2::siphash_32, boost::hash2::siphash_64>; + + ex_out << boost::core::type_name<hasher>() << " uses " + << boost::core::type_name<hasher::hash_type>() << std::endl; + + boost::unordered_flat_map<std::string, int, hasher> map; + + map[ "foo" ] = 1; + map[ "bar" ] = 2; + } + + { + using hasher = hash<std::string, boost::hash2::md5_128>; + + ex_out << boost::core::type_name<hasher>() << " uses " + << boost::core::type_name<hasher::hash_type>() << std::endl; + + boost::unordered_flat_map<std::string, int, hasher> map; + + map[ "foo" ] = 1; + map[ "bar" ] = 2; + } + return 0; // main's implicit return +} + +TEST_CASE("example: hash_32_64") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(ex_hash_32_64::hash<std::string, boost::hash2::siphash_32, boost::hash2::siphash_64> uses boost::hash2::siphash_64 +ex_hash_32_64::hash<std::string, boost::hash2::md5_128, boost::hash2::md5_128> uses boost::hash2::md5_128 +)ex")); +} +} // namespace ex_hash_32_64 + +// libs/hash2/example/hash_with_any_seed.cpp +namespace ex_hash_with_any_seed { +template<class T, class H> class hash +{ +private: + + H h_; + +public: + + hash(): h_() + { + } + + explicit hash( std::uint64_t seed ): h_( seed ) + { + } + + hash( unsigned char const* p, std::size_t n ): h_( p, n ) + { + } + + std::size_t operator()( T const& v ) const + { + H h( h_ ); + boost::hash2::hash_append( h, {}, v ); + return boost::hash2::get_integral_result<std::size_t>( h ); + } +}; + +int example_main() +{ + using hasher = hash<std::string, boost::hash2::siphash_64>; + + { + boost::unordered_flat_map<std::string, int, hasher> map; + + map[ "foo" ] = 1; + map[ "bar" ] = 2; + } + + { + std::uint64_t seed = 0x0102030405060708ull; + + boost::unordered_flat_map<std::string, int, hasher> map( 0, hasher( seed ) ); + + map[ "foo" ] = 1; + map[ "bar" ] = 2; + } + + { + unsigned char const seed[ 16 ] = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 + }; + + boost::unordered_flat_map<std::string, int, hasher> map( 0, hasher(seed, sizeof(seed)) ); + + map[ "foo" ] = 1; + map[ "bar" ] = 2; + } + return 0; // main's implicit return +} + +TEST_CASE("example: hash_with_any_seed") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_hash_with_any_seed + +// libs/hash2/example/hash_with_byte_seed.cpp +namespace ex_hash_with_byte_seed { +template<class T, class H> class hash +{ +private: + + H h_; + +public: + + hash( unsigned char const* p, std::size_t n ): h_( p, n ) + { + } + + std::size_t operator()( T const& v ) const + { + H h( h_ ); + boost::hash2::hash_append( h, {}, v ); + return boost::hash2::get_integral_result<std::size_t>( h ); + } +}; + +int example_main() +{ + unsigned char const seed[ 16 ] = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 + }; + + using hasher = hash<std::string, boost::hash2::siphash_64>; + + boost::unordered_flat_map<std::string, int, hasher> map( 0, hasher( seed, sizeof(seed) ) ); + + map[ "foo" ] = 1; + map[ "bar" ] = 2; + return 0; // main's implicit return +} + +TEST_CASE("example: hash_with_byte_seed") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_hash_with_byte_seed + +// libs/hash2/example/hash_with_uint64_seed.cpp +namespace ex_hash_with_uint64_seed { +template<class T, class H> class hash +{ +private: + + std::uint64_t seed_; + +public: + + explicit hash( std::uint64_t seed ): seed_( seed ) + { + } + + std::size_t operator()( T const& v ) const + { + H h( seed_ ); + boost::hash2::hash_append( h, {}, v ); + return boost::hash2::get_integral_result<std::size_t>( h ); + } +}; + +int example_main() +{ + std::uint64_t seed = 0x0102030405060708ull; + + using hasher = hash<std::string, boost::hash2::siphash_64>; + + boost::unordered_flat_map<std::string, int, hasher> map( 0, hasher( seed ) ); + + map[ "foo" ] = 1; + map[ "bar" ] = 2; + return 0; // main's implicit return +} + +TEST_CASE("example: hash_with_uint64_seed") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_hash_with_uint64_seed + +// libs/hash2/example/hash_without_seed.cpp +namespace ex_hash_without_seed { +template<class T, class H> class hash +{ +public: + + std::size_t operator()( T const& v ) const + { + H h; + boost::hash2::hash_append( h, {}, v ); + return boost::hash2::get_integral_result<std::size_t>( h ); + } +}; + +int example_main() +{ + using hasher = hash<std::string, boost::hash2::fnv1a_64>; + + boost::unordered_flat_map<std::string, int, hasher> map; + + map[ "foo" ] = 1; + map[ "bar" ] = 2; + return 0; // main's implicit return +} + +TEST_CASE("example: hash_without_seed") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_hash_without_seed + +// libs/hash2/example/json_value.cpp: its additions to namespace boost/std, outside the case namespace +namespace boost +{ +namespace json +{ + +template<class Provider, class Hash, class Flavor> +void tag_invoke( boost::hash2::hash_append_tag const&, Provider const&, + Hash& h, Flavor const& f, boost::json::value const* v ) +{ + boost::hash2::hash_append( h, f, v->kind() ); // TODO: int64 vs uint64 + boost::json::visit( [&](auto const& w){ + boost::hash2::hash_append( h, f, w ); }, *v ); +} + +} // namespace json +} +// libs/hash2/example/json_value.cpp +namespace ex_json_value { + // namespace boost + +template<class T> auto md5( T const& v ) +{ + boost::hash2::md5_128 hash; + boost::hash2::hash_append( hash, {}, v ); + return hash.result(); +} + +static constexpr char const* json = R"( +{ + "x1": -5, + "x2": 3.14, + "x3": "str", + "x4": [ null, true, false ] +} +)"; + +int example_main() +{ + auto jv = boost::json::parse( json ); + ex_out << md5( jv ) << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: json_value") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(e33268fc9e1dc055ee3d5e2783d2a0a2 +)ex")); +} +} // namespace ex_json_value + +// libs/hash2/example/md5sum.cpp +namespace ex_md5sum { +#define _CRT_SECURE_NO_WARNINGS + + +static void md5sum( std::FILE* f, char const* fn ) +{ + boost::hash2::md5_128 hash; + + int const N = 4096; + unsigned char buffer[ N ]; + + for( ;; ) + { + std::size_t n = std::fread( buffer, 1, N, f ); + + if( std::ferror( f ) ) + { + std::fprintf( stderr, "'%s': read error: %s\n", fn, std::strerror( errno ) ); + return; + } + + if( n == 0 ) break; + + hash.update( buffer, n ); + } + + std::string digest = to_string( hash.result() ); + + ex_printf( "%s *%s\n", digest.c_str(), fn ); +} + +int example_main( int argc, char const* argv[] ) +{ + for( int i = 1; i < argc; ++i ) + { + std::FILE* f = std::fopen( argv[i], "rb" ); + + if( f == 0 ) + { + std::fprintf( stderr, "'%s': open error: %s\n", argv[i], std::strerror( errno ) ); + continue; + } + + md5sum( f, argv[i] ); + + std::fclose( f ); + } + return 0; // main's implicit return +} + +TEST_CASE("example: md5sum") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + ex_args = {"example"}; + { std::ofstream(std::filesystem::temp_directory_path() / "boost-m-hash2-a.txt", std::ios::binary) << "The quick brown fox jumps over the lazy dog"; std::ofstream(std::filesystem::temp_directory_path() / "boost-m-hash2-b.txt", std::ios::binary) << ""; } + ex_args.push_back((std::filesystem::temp_directory_path() / "boost-m-hash2-a.txt").string()); + ex_args.push_back((std::filesystem::temp_directory_path() / "boost-m-hash2-b.txt").string()); + ex_argv_store.clear(); + for (auto& a : ex_args) ex_argv_store.push_back(a.data()); + ex_argv_store.push_back(nullptr); + ex_argv = ex_argv_store.data(); + ex_argc = int(ex_args.size()); + int rc = example_main(ex_argc, ex_argv_t{ex_argv}); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(9e107d9d372bb6826bd81d3542a419d6 */tmp/boost-m-hash2-a.txt +d41d8cd98f00b204e9800998ecf8427e */tmp/boost-m-hash2-b.txt +)ex")); +} +} // namespace ex_md5sum + +// libs/hash2/example/xxh128_from_xxh64.cpp +namespace ex_xxh128_from_xxh64 { +class xxhash_128: private boost::hash2::xxhash_64 +{ +public: + + using result_type = boost::hash2::digest<16>; + + using xxhash_64::xxhash_64; + using xxhash_64::update; + + result_type result() + { + std::uint64_t r1 = xxhash_64::result(); + std::uint64_t r2 = xxhash_64::result(); + + result_type r = {}; + + boost::endian::store_little_u64( r.data() + 0, r1 ); + boost::endian::store_little_u64( r.data() + 8, r2 ); + + return r; + } +}; + + +int example_main() +{ + std::string tv( "The quick brown fox jumps over the lazy dog" ); + + xxhash_128 hash( 43 ); + hash.update( tv.data(), tv.size() ); + + ex_out << hash.result() << std::endl; + return 0; // main's implicit return +} + +TEST_CASE("example: xxh128_from_xxh64") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex(25748d46e9f5119ab7c2a4551965441c +)ex")); +} +} // namespace ex_xxh128_from_xxh64 diff --git a/tests/hash2/mixed_describe.cpp b/tests/hash2/mixed_describe.cpp new file mode 100644 index 00000000..dcb64b76 --- /dev/null +++ b/tests/hash2/mixed_describe.cpp @@ -0,0 +1,31 @@ +// boost.hash2 with described classes — libs/hash2/doc/hash2/ +// hashing_objects.adoc#described-classes: the BOOST_DESCRIBE_* macros come from +// #include (no module carries them), hash_append from the hash2 module, which +// hashes a described class member by member and base by base (design §4.4: +// includes precede imports). +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <boost/describe/class.hpp> +import std; +import mcpplibs.mwrap.boost.hash2; + +namespace user { +struct X { + int a; +}; +BOOST_DESCRIBE_STRUCT(X, (), (a)) +struct Y : public X { + int b; +}; +BOOST_DESCRIBE_STRUCT(Y, (X), (b)) +} // namespace user + +TEST_CASE("doc: hashing_objects#described-classes") { + boost::hash2::fnv1a_32 h1; + user::Y v1 = {{1}, 2}; + boost::hash2::hash_append(h1, {}, v1); + boost::hash2::fnv1a_32 h2; + boost::hash2::hash_append(h2, {}, v1.a); + boost::hash2::hash_append(h2, {}, v1.b); + CHECK(h1.result() == h2.result()); +} From 522729860a3591efe430b9ae4250d67479064e50 Mon Sep 17 00:00:00 2001 From: Zhe Feng <zhefeng7110@outlook.com> Date: Mon, 9 Nov 2026 14:45:32 +0000 Subject: [PATCH 8/9] tests: unordered suite (example, documentation sections, concurrent visitation, stats as headers, user types) --- tests/unordered/api.cpp | 193 ++++++++++++++++++++++++++++++ tests/unordered/coverage.toml | 76 ++++++------ tests/unordered/custom_types.cpp | 132 ++++++++++++++++++++ tests/unordered/examples.cpp | 70 +++++++++++ tests/unordered/include_stats.cpp | 28 +++++ 5 files changed, 462 insertions(+), 37 deletions(-) create mode 100644 tests/unordered/api.cpp create mode 100644 tests/unordered/custom_types.cpp create mode 100644 tests/unordered/examples.cpp create mode 100644 tests/unordered/include_stats.cpp diff --git a/tests/unordered/api.cpp b/tests/unordered/api.cpp new file mode 100644 index 00000000..e6f299e0 --- /dev/null +++ b/tests/unordered/api.cpp @@ -0,0 +1,193 @@ +// mcpplibs.mwrap.boost.unordered — the documentation's code sections +// (libs/unordered/doc/modules/ROOT/pages/*.adoc): the regular containers +// (closed addressing, open addressing flat/node), iterator invalidation and +// reserve/rehash, piecewise emplacement, the concurrent containers' +// visitation API, bulk visitation and interoperability with the +// non-concurrent containers, and erase_if / merge / extract. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +import std; +import mcpplibs.mwrap.boost.unordered; +import mcpplibs.mwrap.boost.tuple; + +TEST_CASE("doc: regular#iterator-invalidation") { + using map = boost::unordered_map<std::string, int>; + map x; + x["one"] = 1; + x["two"] = 2; + x["three"] = 3; + CHECK(x.at("one") == 1); + CHECK(x.find("missing") == x.end()); + int sum = 0; + for (const map::value_type& i : x) sum += i.second; + CHECK(sum == 6); + std::size_t n = 1000; + x.reserve(n); + CHECK(x.bucket_count() >= std::size_t(std::ceil(n / x.max_load_factor()))); + auto it = x.find("two"); + for (std::size_t i = 0; i < 500; ++i) x.emplace(std::to_string(i), int(i)); // no rehash: it stays valid + CHECK(it->second == 2); + x.rehash(std::size_t(std::ceil(4000 / x.max_load_factor()))); + CHECK(x.at("two") == 2); +} + +TEST_CASE("doc: structures#closed-addressing-containers") { + boost::unordered_set<int> s{1, 2, 3}; + CHECK(s.bucket_count() > 0); + CHECK(s.bucket_size(s.bucket(1)) >= 1); + auto b = s.bucket(2); + CHECK(std::find(s.begin(b), s.end(b), 2) != s.end(b)); + boost::unordered_multimap<int, char> mm{{1, 'a'}, {1, 'b'}}; + CHECK(mm.count(1) == 2); + boost::unordered_flat_set<int> fs{1, 2}; + boost::unordered_node_map<int, std::string> nm{{1, "x"}}; + auto node = nm.extract(1); + CHECK(node.key() == 1); + CHECK(nm.empty()); + nm.insert(std::move(node)); + CHECK(nm.size() == 1); + CHECK(fs.contains(2)); +} + +TEST_CASE("doc: compliance#piecewise-pair-emplacement") { + boost::unordered_multimap<std::string, std::complex<double>> x; + x.emplace(std::piecewise_construct, std::make_tuple("key"), std::make_tuple(1, 2)); + x.emplace(boost::unordered::piecewise_construct, boost::make_tuple("key"), boost::make_tuple(1, 2)); + CHECK(x.count("key") == 2); + CHECK(x.find("key")->second == std::complex<double>(1, 2)); +} + +TEST_CASE("doc: compliance#concurrent-containers") { + // No iterators: lookups return counts and act through visitation. + boost::concurrent_flat_map<int, int> m; + CHECK(m.insert({1, 10})); + CHECK_FALSE(m.insert({1, 11})); + CHECK(m.visit(1, [](auto& x) { x.second += 1; }) == 1); + CHECK(m.insert_or_visit({1, 0}, [](auto& y) { ++y.second; }) == false); + int v = 0; + m.cvisit(1, [&](const auto& x) { v = x.second; }); + CHECK(v == 12); +} + +TEST_CASE("doc: concurrent#visitation-based-api") { + boost::concurrent_flat_map<int, int> m; + std::vector<int> input(10000); + std::iota(input.begin(), input.end(), 0); + auto calculation = [](int x) { return x * 2; }; + const int num_threads = 8; + std::vector<std::jthread> threads; + std::size_t chunk = input.size() / num_threads; + for (int i = 0; i < num_threads; ++i) { + threads.emplace_back([&, i] { + std::size_t start = i * chunk; + std::size_t end = (i == num_threads - 1) ? input.size() : (i + 1) * chunk; + for (std::size_t n = start; n < end; ++n) m.emplace(input[n], calculation(input[n])); + }); + } + threads.clear(); // join + CHECK(m.size() == input.size()); + std::optional<std::pair<const int, int>> o; + bool found = m.visit(42, [&](const auto& x) { o.emplace(x); }); // the pair has a const key: emplace, not = + CHECK(found); + CHECK(o->second == 84); + boost::concurrent_flat_map<int, int> m2; + m.visit(7, [&](const auto& x) { + if (x.second == 14) m2.insert(x); // a different container: allowed + }); + CHECK(m2.size() == 1); + std::ostringstream log; + m.insert_and_cvisit( + std::pair{1, 1}, [&](const auto& y) { log << "(" << y.first << ", " << y.second << ") inserted\n"; }, + [&](const auto& y) { log << "(" << y.first << ", " << y.second << ") already exists\n"; }); + CHECK(log.str() == "(1, 2) already exists\n"); +} + +TEST_CASE("doc: concurrent#whole-table-visitation") { + boost::concurrent_flat_map<int, int> m; + for (int i = 0; i < 100; ++i) m.emplace(i, i); + m.visit_all([](auto& x) { x.second = 0; }); + int sum = 0; + m.cvisit_all([&](const auto& x) { sum += x.second; }); + CHECK(sum == 0); + int key = 0, value = 0; + m.visit(5, [](auto& x) { x.second = 77; }); + m.cvisit_while([&](const auto& x) { + if (x.second == 77) { + key = x.first; + value = x.second; + return false; // stop + } + return true; + }); + CHECK(key == 5); + CHECK(value == 77); + CHECK(m.erase_if([](auto& x) { return x.first >= 50; }) == 50); +} + +TEST_CASE("doc: concurrent#bulk-visitation") { + boost::concurrent_flat_map<int, int> m; + for (int i = 0; i < 1000; ++i) m.emplace(i, i * i); + std::vector<int> keys{3, 5, 2000, 7}; + int found = 0; + m.visit(keys.begin(), keys.end(), [&](const auto&) { ++found; }); + CHECK(found == 3); +} + +TEST_CASE("doc: concurrent#interoperability-with-non-concurrent-containers") { + boost::concurrent_flat_map<int, int> cm; + cm.emplace(1, 1); + boost::unordered_flat_map<int, int> m(std::move(cm)); // O(1) move + CHECK(m.size() == 1); + CHECK(cm.empty()); + m.emplace(2, 2); + boost::concurrent_flat_map<int, int> back(std::move(m)); + CHECK(back.size() == 2); +} + +TEST_CASE("doc: hash_equality#hash_equality") { + struct point { + int x; + int y; + bool operator==(point const&) const = default; + }; + struct point_hash { + std::size_t operator()(point const& p) const { + std::size_t seed = 0; + boost::hash_combine(seed, p.x); + boost::hash_combine(seed, p.y); + return seed; + } + }; + boost::unordered_multiset<point, point_hash> points; + points.insert({1, 2}); + points.insert({1, 2}); + CHECK(points.count({1, 2}) == 2); +} + +TEST_CASE("doc: hash_quality#hash-post-mixing-and-the-avalanching-property") { + struct my_string_hash_function { + using is_avalanching = std::true_type; // no post-mixing + std::size_t operator()(const std::string& x) const { return boost::hash<std::string>()(x); } + }; + boost::unordered_flat_map<std::string, int, my_string_hash_function> m{{"a", 1}}; + CHECK(m.at("a") == 1); + static_assert(boost::unordered::hash_is_avalanching<my_string_hash_function>::value); + static_assert(!boost::unordered::hash_is_avalanching<std::hash<int>>::value); +} + +namespace { +struct sv_hash { + using is_transparent = void; + std::size_t operator()(std::string_view s) const { return boost::hash<std::string_view>()(s); } +}; +} // namespace + +TEST_CASE("erase_if, merge, transparent lookup") { + boost::unordered_flat_map<std::string, int, sv_hash, std::equal_to<>> m{{"a", 1}, {"bb", 2}}; + CHECK(m.find(std::string_view("bb"))->second == 2); // heterogeneous lookup + CHECK(boost::unordered::erase_if(m, [](auto& p) { return p.second > 1; }) == 1); + boost::unordered_map<int, int> a{{1, 1}}, b{{2, 2}, {1, 9}}; + a.merge(b); + CHECK(a.size() == 2); + CHECK(b.size() == 1); // the duplicate key stays in b +} diff --git a/tests/unordered/coverage.toml b/tests/unordered/coverage.toml index c2ed60bb..d47ed433 100644 --- a/tests/unordered/coverage.toml +++ b/tests/unordered/coverage.toml @@ -6,70 +6,72 @@ upstream = "1.92.0" library = "unordered" [[example]] -path = "libs/unordered/examples/case_insensitive_test.cpp" -status = "todo" +path = "libs/unordered/examples/case_insensitive_test.cpp" +case = "unordered/examples:example: case_insensitive_test" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/compliance.adoc#concurrent-containers" -title = "Concurrent Containers" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/compliance.adoc#concurrent-containers" +title = "Concurrent Containers" +case = "unordered/api:doc: compliance#concurrent-containers" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/compliance.adoc#piecewise-pair-emplacement" -title = "Piecewise Pair Emplacement" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/compliance.adoc#piecewise-pair-emplacement" +title = "Piecewise Pair Emplacement" +case = "unordered/api:doc: compliance#piecewise-pair-emplacement" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#bulk-visitation" -title = "Bulk visitation" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#bulk-visitation" +title = "Bulk visitation" +case = "unordered/api:doc: concurrent#bulk-visitation" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#interoperability-with-non-concurrent-containers" -title = "Interoperability with non-concurrent containers" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#interoperability-with-non-concurrent-containers" +title = "Interoperability with non-concurrent containers" +case = "unordered/api:doc: concurrent#interoperability-with-non-concurrent-containers" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#visitation-based-api" -title = "Visitation-based API" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#visitation-based-api" +title = "Visitation-based API" +case = "unordered/api:doc: concurrent#visitation-based-api" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#whole-table-visitation" -title = "Whole-Table Visitation" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/concurrent.adoc#whole-table-visitation" +title = "Whole-Table Visitation" +case = "unordered/api:doc: concurrent#whole-table-visitation" [[snippet]] doc = "libs/unordered/doc/modules/ROOT/pages/debuggability.adoc#using-in-your-project" title = "Using in your project" -status = "todo" +status = "excluded" +reason = "GDB pretty-printer setup and the printers' output layout, no C++ API use" [[snippet]] doc = "libs/unordered/doc/modules/ROOT/pages/debuggability.adoc#visualization-structure" title = "Visualization structure" -status = "todo" +status = "excluded" +reason = "GDB pretty-printer setup and the printers' output layout, no C++ API use" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/hash_equality.adoc#hash_equality" -title = "Custom Types" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/hash_equality.adoc#hash_equality" +title = "Custom Types" +case = "unordered/api:doc: hash_equality#hash_equality" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/hash_quality.adoc#container-statistics" -title = "Container Statistics" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/hash_quality.adoc#container-statistics" +title = "Container Statistics" +case = "unordered/include_stats:doc: hash_quality#container-statistics" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/hash_quality.adoc#hash-post-mixing-and-the-avalanching-property" -title = "Hash Post-mixing and the Avalanching Property" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/hash_quality.adoc#hash-post-mixing-and-the-avalanching-property" +title = "Hash Post-mixing and the Avalanching Property" +case = "unordered/api:doc: hash_quality#hash-post-mixing-and-the-avalanching-property" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/regular.adoc#iterator-invalidation" -title = "Iterator Invalidation" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/regular.adoc#iterator-invalidation" +title = "Iterator Invalidation" +case = "unordered/api:doc: regular#iterator-invalidation" [[snippet]] -doc = "libs/unordered/doc/modules/ROOT/pages/structures.adoc#closed-addressing-containers" -title = "Closed-addressing Containers" -status = "todo" +doc = "libs/unordered/doc/modules/ROOT/pages/structures.adoc#closed-addressing-containers" +title = "Closed-addressing Containers" +case = "unordered/api:doc: structures#closed-addressing-containers" diff --git a/tests/unordered/custom_types.cpp b/tests/unordered/custom_types.cpp new file mode 100644 index 00000000..0f3bbe77 --- /dev/null +++ b/tests/unordered/custom_types.cpp @@ -0,0 +1,132 @@ +// mcpplibs.mwrap.boost.unordered — user types in every container family: +// user keys hashed through boost::hash (hash_value by ADL) and through user +// hasher/equality types (including stateful ones), a user hasher marked +// is_avalanching, transparent user hashers for heterogeneous lookup, a user +// allocator, and move-only / non-default-constructible mapped types; the +// node and concurrent containers holding user types. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +import std; +import mcpplibs.mwrap.boost.unordered; + +namespace user { +struct point { + int x, y; + bool operator==(const point&) const = default; + friend std::size_t hash_value(const point& p) { + std::size_t seed = 0; + boost::hash_combine(seed, p.x); + boost::hash_combine(seed, p.y); + return seed; + } +}; + +// examples/fnv1.hpp-style user hasher (FNV-1a), stateful seed. +struct fnv1a { + std::uint64_t basis = 0xcbf29ce484222325ull; + std::size_t operator()(std::string_view s) const { + std::uint64_t h = basis; + for (unsigned char c : s) { + h ^= c; + h *= 0x100000001b3ull; + } + return std::size_t(h); + } +}; + +struct mod_hash { // terrible distribution unless post-mixed + std::size_t operator()(int v) const { return std::size_t(v) * 1024; } +}; + +struct id_hash { + using is_transparent = void; + using is_avalanching = std::true_type; + std::size_t operator()(std::string_view s) const { return std::hash<std::string_view>()(s); } +}; + +struct resource { + std::unique_ptr<int> data; + explicit resource(int v) : data(std::make_unique<int>(v)) {} +}; + +inline std::size_t allocs = 0; +template <class T> +struct counting_allocator { + using value_type = T; + counting_allocator() = default; + template <class U> + counting_allocator(const counting_allocator<U>&) noexcept {} + T* allocate(std::size_t n) { + ++allocs; + return std::allocator<T>().allocate(n); + } + void deallocate(T* p, std::size_t n) noexcept { std::allocator<T>().deallocate(p, n); } + template <class U> + bool operator==(const counting_allocator<U>&) const noexcept { return true; } +}; +} // namespace user + +TEST_CASE_TEMPLATE("user keys through boost::hash in every set family", Set, boost::unordered_set<user::point>, + boost::unordered_flat_set<user::point>, boost::unordered_node_set<user::point>) { + Set s; + s.insert({1, 2}); + s.insert({1, 2}); + s.insert({2, 1}); + CHECK(s.size() == 2); + CHECK(s.contains({2, 1})); +} + +TEST_CASE("stateful user hasher") { + boost::unordered_map<std::string, int, user::fnv1a> m(16, user::fnv1a{12345}); + m["a"] = 1; + CHECK(m.hash_function().basis == 12345); + CHECK(m.at("a") == 1); +} + +TEST_CASE("post-mixing rescues a poor hash; avalanching skips it") { + boost::unordered_flat_set<int, user::mod_hash> s; + for (int i = 0; i < 2000; ++i) s.insert(i); + CHECK(s.size() == 2000); + boost::unordered_flat_map<std::string, int, user::id_hash, std::equal_to<>> m{{"k", 1}}; + CHECK(m.find(std::string_view("k")) != m.end()); // transparent lookup + CHECK(m.count("k") == 1); +} + +TEST_CASE("move-only mapped types") { + boost::unordered_node_map<int, user::resource> m; + m.emplace(1, 10); + m.try_emplace(2, 20); + CHECK(*m.at(2).data == 20); + auto nh = m.extract(1); + CHECK(*nh.mapped().data == 10); + boost::unordered_flat_map<int, user::resource> f; + f.emplace(3, 30); + CHECK(*f.at(3).data == 30); +} + +TEST_CASE("user allocator") { + user::allocs = 0; + boost::unordered_flat_map<int, int, boost::hash<int>, std::equal_to<int>, + user::counting_allocator<std::pair<const int, int>>> + m; + for (int i = 0; i < 100; ++i) m.emplace(i, i); + CHECK(user::allocs > 0); +} + +TEST_CASE("concurrent containers of user types") { + boost::concurrent_flat_map<user::point, std::string> m; + m.emplace(user::point{0, 0}, "origin"); + std::vector<std::jthread> ts; + for (int t = 0; t < 4; ++t) + ts.emplace_back([&m, t] { + for (int i = 0; i < 250; ++i) m.emplace(user::point{t, i + 1}, "p"); + }); + ts.clear(); + CHECK(m.size() == 1001); + std::string name; + m.cvisit(user::point{0, 0}, [&](const auto& kv) { name = kv.second; }); + CHECK(name == "origin"); + boost::concurrent_flat_set<user::point> s; + s.insert({1, 1}); + CHECK(s.contains({1, 1})); +} diff --git a/tests/unordered/examples.cpp b/tests/unordered/examples.cpp new file mode 100644 index 00000000..c072eaf9 --- /dev/null +++ b/tests/unordered/examples.cpp @@ -0,0 +1,70 @@ +// mcpplibs.mwrap.boost.unordered — libs/unordered/examples/ +// case_insensitive_test.cpp. The example's helper header +// (examples/case_insensitive.hpp: ihash / iequal_to over +// boost::algorithm::iequals) is copied into the case namespace, and its +// BOOST_TEST checks are doctest CHECKs (the same conditions); both keep the +// TU on imports only. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +import std; +import mcpplibs.mwrap.boost.unordered; +import mcpplibs.mwrap.boost.algorithm; + +namespace hash_examples { +struct iequal_to { + iequal_to() {} + explicit iequal_to(std::locale const& l) : locale_(l) {} + template <typename String1, typename String2> + bool operator()(String1 const& x1, String2 const& x2) const { + return boost::algorithm::iequals(x1, x2, locale_); + } +private: + std::locale locale_; +}; +struct ihash { + ihash() {} + explicit ihash(std::locale const& l) : locale_(l) {} + template <typename String> + std::size_t operator()(String const& x) const { + std::size_t seed = 0; + for (typename String::const_iterator it = x.begin(); it != x.end(); ++it) + boost::hash_combine(seed, std::toupper(*it, locale_)); + return seed; + } +private: + std::locale locale_; +}; +} // namespace hash_examples + +namespace { +struct word_info { + int tag; + explicit word_info(int t = 0) : tag(t) {} +}; + +template <class S> +void run(S one, S ONE, S One, S two, S TWO, S Two) { + boost::unordered_map<S, word_info, hash_examples::ihash, hash_examples::iequal_to> idictionary; + CHECK(idictionary.empty()); + idictionary[one] = word_info(1); + CHECK(idictionary.size() == 1); + CHECK((idictionary.find(ONE) != idictionary.end() && idictionary.find(ONE) == idictionary.find(one))); + idictionary.insert(std::make_pair(ONE, word_info(2))); + CHECK(idictionary.size() == 1); + CHECK((idictionary.find(ONE) != idictionary.end() && idictionary.find(ONE)->first == one && + idictionary.find(ONE)->second.tag == 1)); + idictionary[One] = word_info(3); + CHECK(idictionary.size() == 1); + CHECK((idictionary.find(ONE) != idictionary.end() && idictionary.find(ONE)->first == one && + idictionary.find(ONE)->second.tag == 3)); + idictionary[two] = word_info(4); + CHECK(idictionary.size() == 2); + CHECK((idictionary.find(two) != idictionary.end() && idictionary.find(TWO)->first == two && + idictionary.find(Two)->second.tag == 4)); +} +} // namespace + +TEST_CASE("example: case_insensitive_test") { + run<std::string>("one", "ONE", "One", "two", "TWO", "Two"); // test1 + run<std::wstring>(L"one", L"ONE", L"One", L"two", L"TWO", L"Two"); // test2 +} diff --git a/tests/unordered/include_stats.cpp b/tests/unordered/include_stats.cpp new file mode 100644 index 00000000..b219e371 --- /dev/null +++ b/tests/unordered/include_stats.cpp @@ -0,0 +1,28 @@ +// boost.unordered container statistics — pure include: +// libs/unordered/doc/modules/ROOT/pages/hash_quality.adoc#container-statistics +// needs BOOST_UNORDERED_ENABLE_STATS defined before the headers, which changes +// the containers' layout; the module is built without it, so this TU uses the +// headers alone (and does not import). +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#define BOOST_UNORDERED_ENABLE_STATS +#include <string> +#include <boost/unordered/unordered_flat_map.hpp> + +TEST_CASE("doc: hash_quality#container-statistics") { + boost::unordered_flat_map<std::string, int> m; + for (int i = 0; i < 1000; ++i) m.emplace(std::to_string(i), i); + for (int i = 0; i < 1000; ++i) (void)m.find(std::to_string(i)); + for (int i = 1000; i < 1100; ++i) (void)m.find(std::to_string(i)); + auto stats = m.get_stats(); + // Insertions include the re-insertions of every rehash; each emplace of a + // new key first does an unsuccessful lookup. + CHECK(stats.insertion.count >= 1000); + CHECK(stats.successful_lookup.count == 1000); + CHECK(stats.unsuccessful_lookup.count == 1000 + 100); + CHECK(stats.insertion.probe_length.average >= 1.0); + CHECK(stats.successful_lookup.num_comparisons.average >= 1.0); + CHECK(stats.unsuccessful_lookup.probe_length.deviation >= 0.0); + m.reset_stats(); + CHECK(m.get_stats().insertion.count == 0); +} From a3afc841d5e28d3320a693921fbe623e7691fefc Mon Sep 17 00:00:00 2001 From: Zhe Feng <zhefeng7110@outlook.com> Date: Mon, 9 Nov 2026 15:00:07 +0000 Subject: [PATCH 9/9] container: fix the pmr/devector.hpp include guard; container suite patches/container.patch: boost/container/pmr/devector.hpp reused the include guard of pmr/vector.hpp, so the module (whose GMF includes devector first) never exported boost::container::pmr::vector / vector_of. The 19 examples (output oracle), the documentation's inline code (hub, devector, stable_vector, pmr, const-reference parameters, null iterators), user types, options and a user memory_resource. doc_extended_allocators is recorded as a gap: the package does not build the dlmalloc TUs. --- generated/exports/container.inc | 4 +- generated/meta/api-surface.json | 2 +- patches/README.md | 5 + patches/container.patch | 25 + tests/bimap/examples.cpp | 11 +- tests/bimap/examples_with_boost.cpp | 11 +- tests/bimap/include_with_boost.cpp | 12 +- tests/container/api.cpp | 155 ++ tests/container/coverage.toml | 139 +- tests/container/custom_types.cpp | 135 ++ tests/container/examples.cpp | 1347 +++++++++++++++++ tests/describe/examples_json.cpp | 12 +- tests/describe/examples_serialization.cpp | 11 +- tests/describe/include_examples.cpp | 12 +- tests/hash2/examples.cpp | 1 + tools/apply_patches.py | 6 +- .../boost/boost/container/pmr/devector.hpp | 10 +- 17 files changed, 1809 insertions(+), 89 deletions(-) create mode 100644 patches/container.patch create mode 100644 tests/container/api.cpp create mode 100644 tests/container/custom_types.cpp create mode 100644 tests/container/examples.cpp diff --git a/generated/exports/container.inc b/generated/exports/container.inc index 0eeda546..1489468f 100644 --- a/generated/exports/container.inc +++ b/generated/exports/container.inc @@ -1,5 +1,5 @@ // GENERATED by tools/gen_exports.py - DO NOT EDIT -// lib=container boost=1.92.0 target=x86_64-w64-mingw32 entities=976 +// lib=container boost=1.92.0 target=x86_64-w64-mingw32 entities=978 export namespace boost { namespace container { using boost::container::DeepSwapAllocNMaxStorage; @@ -1006,6 +1006,8 @@ export namespace boost { namespace container { namespace pmr { using boost::container::pmr::string; using boost::container::pmr::synchronized_pool_resource; using boost::container::pmr::unsynchronized_pool_resource; + using boost::container::pmr::vector; + using boost::container::pmr::vector_of; using boost::container::pmr::wstring; }}} diff --git a/generated/meta/api-surface.json b/generated/meta/api-surface.json index a8aa6e65..6f368f3f 100644 --- a/generated/meta/api-surface.json +++ b/generated/meta/api-surface.json @@ -398,7 +398,7 @@ "intrusive", "move" ], - "exported": 976, + "exported": 978, "gmf": [ "boost/container/adaptive_pool.hpp", "boost/container/allocator.hpp", diff --git a/patches/README.md b/patches/README.md index b6ec1e23..0ba8c66c 100644 --- a/patches/README.md +++ b/patches/README.md @@ -34,6 +34,11 @@ second one. The algorithm module's GMF includes them in that order, and `boost::algorithm::transform_reduce` was missing from its export list; plain `#include` consumers hit the same bug. The patch gives the header its own guard. +`container.patch` is the same upstream bug as `algorithm.patch`: +`boost/container/pmr/devector.hpp` reuses the include guard of +`pmr/vector.hpp`, so `boost::container::pmr::vector` and `vector_of` were +missing from the container module (and from any TU including devector first). + `convert.patch` is the TU-local-object case: `boost::throw_on_failure` was a namespace-scope `const` object (internal linkage), which a module cannot export; it becomes `inline constexpr`. diff --git a/patches/container.patch b/patches/container.patch new file mode 100644 index 00000000..3546e4ba --- /dev/null +++ b/patches/container.patch @@ -0,0 +1,25 @@ +diff --git a/upstream/boost/boost/container/pmr/devector.hpp b/upstream/boost/boost/container/pmr/devector.hpp +index 9ecbb4fa..140f19a5 100644 +--- a/upstream/boost/boost/container/pmr/devector.hpp ++++ b/upstream/boost/boost/container/pmr/devector.hpp +@@ -8,8 +8,12 @@ + // + ////////////////////////////////////////////////////////////////////////////// + +-#ifndef BOOST_CONTAINER_PMR_VECTOR_HPP +-#define BOOST_CONTAINER_PMR_VECTOR_HPP ++// boost-m vendored edit: upstream reuses pmr/vector.hpp's include guard here, ++// so a TU that includes devector.hpp first loses boost::container::pmr::vector ++// (the container module's GMF did: pmr::vector / vector_of were never ++// exported). Replay after re-running import_boost. ++#ifndef BOOST_CONTAINER_PMR_DEVECTOR_HPP ++#define BOOST_CONTAINER_PMR_DEVECTOR_HPP + + #if defined (_MSC_VER) + # pragma once +@@ -50,4 +54,4 @@ struct devector_of + } //namespace container { + } //namespace boost { + +-#endif //BOOST_CONTAINER_PMR_VECTOR_HPP ++#endif //BOOST_CONTAINER_PMR_DEVECTOR_HPP diff --git a/tests/bimap/examples.cpp b/tests/bimap/examples.cpp index ce0de51f..ce4efc76 100644 --- a/tests/bimap/examples.cpp +++ b/tests/bimap/examples.cpp @@ -16,8 +16,15 @@ import mcpplibs.mwrap.boost.tokenizer; // what the original program printed (built from upstream/boost alone). inline std::ostringstream ex_out; inline std::istringstream ex_in; -inline char ex_arg0[] = "example"; -inline char* ex_argv[] = {ex_arg0, nullptr}; +inline std::vector<std::string> ex_args; // argv of the ported main +inline std::vector<char*> ex_argv_store; +inline char** ex_argv = nullptr; +inline int ex_argc = 0; +struct ex_argv_t { // converts to char** and char const** (both main signatures) + char** p; + operator char**() const { return p; } + operator const char**() const { return const_cast<const char**>(p); } +}; template <class... A> int ex_printf(const char* fmt, A... a) { int n = std::snprintf(nullptr, 0, fmt, a...); diff --git a/tests/bimap/examples_with_boost.cpp b/tests/bimap/examples_with_boost.cpp index 281feab2..7c4e15d2 100644 --- a/tests/bimap/examples_with_boost.cpp +++ b/tests/bimap/examples_with_boost.cpp @@ -35,8 +35,15 @@ import mcpplibs.mwrap.boost.optional; // what the original program printed (built from upstream/boost alone). inline std::ostringstream ex_out; inline std::istringstream ex_in; -inline char ex_arg0[] = "example"; -inline char* ex_argv[] = {ex_arg0, nullptr}; +inline std::vector<std::string> ex_args; // argv of the ported main +inline std::vector<char*> ex_argv_store; +inline char** ex_argv = nullptr; +inline int ex_argc = 0; +struct ex_argv_t { // converts to char** and char const** (both main signatures) + char** p; + operator char**() const { return p; } + operator const char**() const { return const_cast<const char**>(p); } +}; template <class... A> int ex_printf(const char* fmt, A... a) { int n = std::snprintf(nullptr, 0, fmt, a...); diff --git a/tests/bimap/include_with_boost.cpp b/tests/bimap/include_with_boost.cpp index bfe35169..8afe30f7 100644 --- a/tests/bimap/include_with_boost.cpp +++ b/tests/bimap/include_with_boost.cpp @@ -18,13 +18,21 @@ #include <sstream> #include <string> #include <string_view> +#include <vector> // Output of the ported examples is captured here; each case compares it with // what the original program printed (built from upstream/boost alone). inline std::ostringstream ex_out; inline std::istringstream ex_in; -inline char ex_arg0[] = "example"; -inline char* ex_argv[] = {ex_arg0, nullptr}; +inline std::vector<std::string> ex_args; // argv of the ported main +inline std::vector<char*> ex_argv_store; +inline char** ex_argv = nullptr; +inline int ex_argc = 0; +struct ex_argv_t { // converts to char** and char const** (both main signatures) + char** p; + operator char**() const { return p; } + operator const char**() const { return const_cast<const char**>(p); } +}; template <class... A> int ex_printf(const char* fmt, A... a) { int n = std::snprintf(nullptr, 0, fmt, a...); diff --git a/tests/container/api.cpp b/tests/container/api.cpp new file mode 100644 index 00000000..2ffa55b0 --- /dev/null +++ b/tests/container/api.cpp @@ -0,0 +1,155 @@ +// mcpplibs.mwrap.boost.container — the documentation's inline code +// (libs/container/doc/container.qbk) and the container families: vector, +// devector, stable_vector, small/static_vector, deque, list/slist, the flat +// associative containers, string, hub, the pmr aliases and memory resources, +// const-reference parameters into the same container, null iterators. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +import std; +import mcpplibs.mwrap.boost.container; + +namespace bc = boost::container; + +TEST_CASE("doc: container_const_reference_parameters") { + // Arguments referring into the container itself are safe. + bc::vector<int> v{0, 1, 2, 3}; + v.insert(v.begin(), v[2]); + CHECK(v == bc::vector<int>{2, 0, 1, 2, 3}); + bc::list<int> l{5, 5, 6}; + l.remove(*l.begin()); + CHECK(l.size() == 1); + CHECK(l.front() == 6); +} + +TEST_CASE("doc: devector") { + bc::devector<int> d; + d.reserve_front(10); + d.reserve_back(10); + for (int i = 0; i < 5; ++i) { + d.push_front(-i); + d.push_back(i); + } + CHECK(d.front() == -4); + CHECK(d.back() == 4); + CHECK(d.front_free_capacity() + d.back_free_capacity() + d.size() >= 20); + d.pop_front(); + d.pop_back(); + CHECK(d.size() == 8); + // capacity() is the minimum number of elements insertable without + // reallocating; size() may exceed it. + d.reserve(32); + CHECK(d.capacity() >= 32); +} + +TEST_CASE("doc: stable_vector") { + bc::stable_vector<std::string> sv{"a", "b", "c"}; + auto it = sv.begin() + 2; + std::string* p = &*it; + sv.insert(sv.begin(), "z"); // iterators and references stay valid + sv.erase(sv.begin() + 1); + CHECK(*it == "c"); + CHECK(p == &sv.back()); + CHECK(sv.size() == 3); +} + +TEST_CASE("doc: hub_introduction") { + bc::hub<int> h; + for (int i = 0; i < 100; ++i) h.insert(i); + auto it = h.insert(100); + for (int i = 101; i < 200; ++i) h.insert(i); + erase_if(h, [](int x) { return x % 2 != 0; }); + CHECK(*it == 100); // the documentation writes `*it = 100` in assert: a typo for == + for (int i = 200; i < 10000; ++i) h.insert(i); + CHECK(*it == 100); +} + +TEST_CASE("doc: hub_capacity") { + bc::hub<int> h; + h.reserve(1000); + CHECK(h.capacity() == 1024); // rounded to a multiple of 64 + for (int i = 0; i < 500; ++i) h.insert(i); + h.trim_capacity(750); + CHECK(h.capacity() >= 750); + CHECK(h.capacity() % 64 == 0); + h.trim_capacity(); + erase_if(h, [](int x) { return x % 2 != 0; }); + h.shrink_to_fit(); + long sum = 0; + for (const auto& x : h) sum += x; + CHECK(sum == 249 * 250); // 0 + 2 + ... + 498 +} + +TEST_CASE("doc: hub_unordered_insertion") { + bc::hub<int> h = {0, 1, 2}; + h.erase(h.begin()); + h.insert({3, 4, 5}); + std::multiset<int> seen(h.begin(), h.end()); + CHECK(seen == std::multiset<int>{1, 2, 3, 4, 5}); // insertion order is not kept +} + +TEST_CASE("doc: hub_std_hive_operations") { + bc::hub<int> h1 = {0, 2, 3, 4, 6}, h2 = {1, 4, 6, 7, 9}; + h1.splice(h2); + CHECK(h2.empty()); + h1.sort(); + h1.unique(); + CHECK(std::vector<int>(h1.begin(), h1.end()) == std::vector<int>{0, 1, 2, 3, 4, 6, 7, 9}); + bc::hub<int> h; + int* p = std::addressof(*h.insert(50)); + bc::hub<int>::iterator it = h.get_iterator(p); + h.erase(it); + CHECK(h.empty()); +} + +TEST_CASE("doc: hub_visitation") { + bc::hub<int> h = {1, 2, 3}; + for (auto& x : h) x *= 2; + for_each(h, [](auto& x) { x *= 2; }); // the hub's own for_each + CHECK(std::accumulate(h.begin(), h.end(), 0) == 24); +} + +TEST_CASE("doc: null_iterators") { + auto ni = bc::vector<int>::iterator(); + auto nd = bc::vector<double>::iterator(); + CHECK(ni == ni); + CHECK_FALSE(nd != nd); + static_assert(!std::is_same_v<decltype(ni), decltype(nd)>); // ni == nd does not compile +} + +TEST_CASE("doc: polymorphic_memory_resources") { + char buffer[1024]; + bc::pmr::monotonic_buffer_resource mr(buffer, sizeof buffer); + bc::pmr::vector<bc::pmr::string> v(&mr); + v.emplace_back("a string longer than the small buffer optimization"); + CHECK(v.get_allocator().resource() == &mr); + CHECK(v.back().get_allocator().resource() == &mr); // uses-allocator propagation + static_assert(std::is_same_v<bc::pmr::vector_of<int>::type, bc::pmr::vector<int>>); + bc::pmr::unsynchronized_pool_resource pool; + bc::pmr::map<int, bc::pmr::string> m(&pool); + m.emplace(1, "x"); + CHECK(m.at(1) == "x"); + CHECK(bc::pmr::get_default_resource() == bc::pmr::new_delete_resource()); +} + +TEST_CASE("flat containers, small/static vector, string, deque, slist") { + bc::flat_map<int, std::string> fm{{3, "c"}, {1, "a"}}; + CHECK(fm.begin()->second == "a"); + CHECK(fm.nth(1)->first == 3); + bc::flat_multiset<int> fms{2, 1, 2}; + CHECK(fms.count(2) == 2); + bc::small_vector<int, 4> sv{1, 2, 3, 4}; + sv.push_back(5); // spills to the heap + CHECK(sv.size() == 5); + bc::static_vector<int, 3> stv{1, 2, 3}; + CHECK_THROWS_AS(stv.push_back(4), bc::bad_alloc); // the library's own bad_alloc + bc::string s("hello"); + s += " world"; + CHECK(s.find("world") == 6); + CHECK(bc::string("a") < bc::string("b")); + bc::deque<int> dq{1, 2}; + dq.push_front(0); + CHECK(dq[0] == 0); + bc::slist<int> sl{3, 2, 1}; + sl.sort(); + CHECK(sl.front() == 1); +} diff --git a/tests/container/coverage.toml b/tests/container/coverage.toml index 807f46cc..3809362f 100644 --- a/tests/container/coverage.toml +++ b/tests/container/coverage.toml @@ -6,131 +6,132 @@ upstream = "1.92.0" library = "container" [[example]] -path = "libs/container/example/doc_custom_deque.cpp" -status = "todo" +path = "libs/container/example/doc_custom_deque.cpp" +case = "container/examples:example: doc_custom_deque" [[example]] -path = "libs/container/example/doc_custom_devector.cpp" -status = "todo" +path = "libs/container/example/doc_custom_devector.cpp" +case = "container/examples:example: doc_custom_devector" [[example]] -path = "libs/container/example/doc_custom_segtor.cpp" -status = "todo" +path = "libs/container/example/doc_custom_segtor.cpp" +case = "container/examples:example: doc_custom_segtor" [[example]] -path = "libs/container/example/doc_custom_small_vector.cpp" -status = "todo" +path = "libs/container/example/doc_custom_small_vector.cpp" +case = "container/examples:example: doc_custom_small_vector" [[example]] -path = "libs/container/example/doc_custom_static_vector.cpp" -status = "todo" +path = "libs/container/example/doc_custom_static_vector.cpp" +case = "container/examples:example: doc_custom_static_vector" [[example]] -path = "libs/container/example/doc_custom_string.cpp" -status = "todo" +path = "libs/container/example/doc_custom_string.cpp" +case = "container/examples:example: doc_custom_string" [[example]] -path = "libs/container/example/doc_custom_tree.cpp" -status = "todo" +path = "libs/container/example/doc_custom_tree.cpp" +case = "container/examples:example: doc_custom_tree" [[example]] -path = "libs/container/example/doc_custom_vector.cpp" -status = "todo" +path = "libs/container/example/doc_custom_vector.cpp" +case = "container/examples:example: doc_custom_vector" [[example]] -path = "libs/container/example/doc_devector.cpp" -status = "todo" +path = "libs/container/example/doc_devector.cpp" +case = "container/examples:example: doc_devector" [[example]] -path = "libs/container/example/doc_emplace.cpp" -status = "todo" +path = "libs/container/example/doc_emplace.cpp" +case = "container/examples:example: doc_emplace" [[example]] path = "libs/container/example/doc_extended_allocators.cpp" -status = "todo" +status = "gap" +reason = "uses the dlmalloc-based allocators (allocator<T,2>, adaptive_pool, node_allocator); the package does not build libs/container/src/alloc_lib.c + dlmalloc.cpp (tools/gen_features.py)" [[example]] -path = "libs/container/example/doc_flat_map.cpp" -status = "todo" +path = "libs/container/example/doc_flat_map.cpp" +case = "container/examples:example: doc_flat_map" [[example]] -path = "libs/container/example/doc_hub_basic.cpp" -status = "todo" +path = "libs/container/example/doc_hub_basic.cpp" +case = "container/examples:example: doc_hub_basic" [[example]] -path = "libs/container/example/doc_move_containers.cpp" -status = "todo" +path = "libs/container/example/doc_move_containers.cpp" +case = "container/examples:example: doc_move_containers" [[example]] -path = "libs/container/example/doc_pmr.cpp" -status = "todo" +path = "libs/container/example/doc_pmr.cpp" +case = "container/examples:example: doc_pmr" [[example]] -path = "libs/container/example/doc_recursive_containers.cpp" -status = "todo" +path = "libs/container/example/doc_recursive_containers.cpp" +case = "container/examples:example: doc_recursive_containers" [[example]] -path = "libs/container/example/doc_slist.cpp" -status = "todo" +path = "libs/container/example/doc_slist.cpp" +case = "container/examples:example: doc_slist" [[example]] -path = "libs/container/example/doc_small_vector_base.cpp" -status = "todo" +path = "libs/container/example/doc_small_vector_base.cpp" +case = "container/examples:example: doc_small_vector_base" [[example]] -path = "libs/container/example/doc_static_vector.cpp" -status = "todo" +path = "libs/container/example/doc_static_vector.cpp" +case = "container/examples:example: doc_static_vector" [[example]] -path = "libs/container/example/doc_type_erasure.cpp" -status = "todo" +path = "libs/container/example/doc_type_erasure.cpp" +case = "container/examples:example: doc_type_erasure" [[snippet]] -doc = "libs/container/doc/container.qbk#container_const_reference_parameters" -title = "Parameter taken by const reference that can be changed" -status = "todo" +doc = "libs/container/doc/container.qbk#container_const_reference_parameters" +title = "Parameter taken by const reference that can be changed" +case = "container/api:doc: container_const_reference_parameters" [[snippet]] -doc = "libs/container/doc/container.qbk#devector" -title = "['devector]" -status = "todo" +doc = "libs/container/doc/container.qbk#devector" +title = "['devector]" +case = "container/api:doc: devector" [[snippet]] -doc = "libs/container/doc/container.qbk#hub_capacity" -title = "Capacity" -status = "todo" +doc = "libs/container/doc/container.qbk#hub_capacity" +title = "Capacity" +case = "container/api:doc: hub_capacity" [[snippet]] -doc = "libs/container/doc/container.qbk#hub_introduction" -title = "Introduction" -status = "todo" +doc = "libs/container/doc/container.qbk#hub_introduction" +title = "Introduction" +case = "container/api:doc: hub_introduction" [[snippet]] -doc = "libs/container/doc/container.qbk#hub_std_hive_operations" -title = "`std::hive` operations" -status = "todo" +doc = "libs/container/doc/container.qbk#hub_std_hive_operations" +title = "`std::hive` operations" +case = "container/api:doc: hub_std_hive_operations" [[snippet]] -doc = "libs/container/doc/container.qbk#hub_unordered_insertion" -title = "Unordered insertion" -status = "todo" +doc = "libs/container/doc/container.qbk#hub_unordered_insertion" +title = "Unordered insertion" +case = "container/api:doc: hub_unordered_insertion" [[snippet]] -doc = "libs/container/doc/container.qbk#hub_visitation" -title = "Visitation" -status = "todo" +doc = "libs/container/doc/container.qbk#hub_visitation" +title = "Visitation" +case = "container/api:doc: hub_visitation" [[snippet]] -doc = "libs/container/doc/container.qbk#null_iterators" -title = "Null Forward Iterators" -status = "todo" +doc = "libs/container/doc/container.qbk#null_iterators" +title = "Null Forward Iterators" +case = "container/api:doc: null_iterators" [[snippet]] -doc = "libs/container/doc/container.qbk#polymorphic_memory_resources" -title = "Polymorphic Memory Resources" -status = "todo" +doc = "libs/container/doc/container.qbk#polymorphic_memory_resources" +title = "Polymorphic Memory Resources" +case = "container/api:doc: polymorphic_memory_resources" [[snippet]] -doc = "libs/container/doc/container.qbk#stable_vector" -title = "['stable_vector]" -status = "todo" +doc = "libs/container/doc/container.qbk#stable_vector" +title = "['stable_vector]" +case = "container/api:doc: stable_vector" diff --git a/tests/container/custom_types.cpp b/tests/container/custom_types.cpp new file mode 100644 index 00000000..285a9d78 --- /dev/null +++ b/tests/container/custom_types.cpp @@ -0,0 +1,135 @@ +// mcpplibs.mwrap.boost.container — user types and the containers' options: +// move-only and non-default-constructible elements, recursive containers of an +// incomplete user type, user comparators for the flat containers, a user +// allocator through allocator_traits, vector/deque/small_vector options +// (growth factor, stored size, block size, inplace alignment), and a user +// memory_resource for the pmr aliases. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +import std; +import mcpplibs.mwrap.boost.container; + +namespace bc = boost::container; + +namespace user { +struct widget { + explicit widget(int v) : v(v) {} + widget(widget&&) noexcept = default; + widget& operator=(widget&&) noexcept = default; + int v; +}; + +struct node { // recursive: a vector of the type being defined + std::string name; + bc::vector<node> children; + bc::flat_map<std::string, node> named; +}; + +struct by_len { + bool operator()(const std::string& a, const std::string& b) const { + return a.size() != b.size() ? a.size() < b.size() : a < b; + } +}; + +inline int allocs = 0; +template <class T> +struct tracking_allocator { + using value_type = T; + tracking_allocator() = default; + template <class U> + tracking_allocator(const tracking_allocator<U>&) noexcept {} + T* allocate(std::size_t n) { + ++allocs; + return std::allocator<T>().allocate(n); + } + void deallocate(T* p, std::size_t n) noexcept { std::allocator<T>().deallocate(p, n); } + template <class U> + bool operator==(const tracking_allocator<U>&) const noexcept { return true; } +}; + +class counting_resource : public bc::pmr::memory_resource { +public: + std::size_t bytes = 0; +private: + void* do_allocate(std::size_t n, std::size_t align) override { + bytes += n; + return bc::pmr::new_delete_resource()->allocate(n, align); + } + void do_deallocate(void* p, std::size_t n, std::size_t align) override { + bc::pmr::new_delete_resource()->deallocate(p, n, align); + } + bool do_is_equal(const bc::pmr::memory_resource& o) const noexcept override { return this == &o; } +}; +} // namespace user + +TEST_CASE("move-only, non-default-constructible elements") { + bc::vector<user::widget> v; + v.emplace_back(1); + v.emplace(v.begin(), 0); + CHECK(v[0].v == 0); + bc::stable_vector<user::widget> sv; + sv.emplace_back(5); + bc::devector<user::widget> d; + d.emplace_front(7); + CHECK(d.front().v == 7); + bc::flat_map<int, user::widget> fm; + fm.emplace(1, user::widget(9)); + CHECK(fm.at(1).v == 9); +} + +TEST_CASE("recursive containers of an incomplete user type") { + user::node root{"root", {}, {}}; + root.children.push_back({"a", {}, {}}); + root.children[0].children.push_back({"a1", {}, {}}); + root.named.emplace("x", user::node{"x", {}, {}}); + CHECK(root.children[0].children[0].name == "a1"); + CHECK(root.named.at("x").name == "x"); +} + +TEST_CASE("user comparator in flat containers") { + bc::flat_set<std::string, user::by_len> s{"ccc", "a", "bb"}; + CHECK(*s.begin() == "a"); + CHECK(*(s.end() - 1) == "ccc"); + bc::flat_multimap<std::string, int, user::by_len> m{{"zz", 1}, {"q", 2}}; + CHECK(m.begin()->second == 2); +} + +TEST_CASE("user allocator") { + user::allocs = 0; + bc::vector<int, user::tracking_allocator<int>> v; + for (int i = 0; i < 100; ++i) v.push_back(i); + CHECK(user::allocs > 0); + bc::list<int, user::tracking_allocator<int>> l{1, 2, 3}; + CHECK(l.size() == 3); +} + +TEST_CASE("container options") { + using growth = bc::vector_options_t<bc::growth_factor<bc::growth_factor_100>, bc::stored_size<std::uint16_t>>; + bc::vector<int, void, growth> v; + for (int i = 0; i < 1000; ++i) v.push_back(i); + static_assert(sizeof(v) < sizeof(bc::vector<int>)); // the size/capacity are stored as uint16_t + CHECK(v.size() == 1000); + using dq = bc::deque_options_t<bc::block_size<128u>>; + bc::deque<int, void, dq> d(300, 1); + CHECK(d.size() == 300); + using svo = bc::small_vector_options_t<bc::inplace_alignment<16u>>; + bc::small_vector<char, 8, void, svo> s(8, 'x'); + CHECK(reinterpret_cast<std::uintptr_t>(s.data()) % 16 == 0); + using sto = bc::static_vector_options_t<bc::throw_on_overflow<false>>; + bc::static_vector<int, 2, sto> st{1, 2}; + CHECK(st.size() == st.capacity()); +} + +TEST_CASE("a user memory_resource") { + user::counting_resource mr; + { + bc::pmr::vector<bc::pmr::string> v(&mr); + v.emplace_back(200, 'x'); + v.emplace_back(300, 'y'); + bc::pmr::flat_map<int, int> fm(&mr); + fm.emplace(1, 1); + } + CHECK(mr.bytes >= 500); + bc::pmr::polymorphic_allocator<int> a(&mr); + CHECK(a.resource() == &mr); +} diff --git a/tests/container/examples.cpp b/tests/container/examples.cpp new file mode 100644 index 00000000..e9a8c332 --- /dev/null +++ b/tests/container/examples.cpp @@ -0,0 +1,1347 @@ +// mcpplibs.mwrap.boost.container — the official examples +// (libs/container/example/*.cpp) ported mechanically: each program runs as a +// case and its output must equal what the same code printed when built from +// upstream/boost headers alone. The BOOST_CONTAINER_TRY/CATCH and +// BOOST_RV_REF / BOOST_MOVABLE_BUT_NOT_COPYABLE macros the examples use come +// from their upstream headers (container/detail/workaround.hpp, move/core.hpp), +// included before the imports (docs/usage.md, macros). doc_extended_allocators +// needs the dlmalloc-based allocators, which the package does not build +// (tools/gen_features.py: alloc_lib.c is a C TU): coverage.toml, gap. +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> +#include <cstdio> +#include <cassert> +#include <boost/container/detail/workaround.hpp> +#include <boost/move/core.hpp> +#include <cstddef> +#include <boost/config.hpp> +import std; +import mcpplibs.mwrap.boost.container; +import mcpplibs.mwrap.boost.move; + +// Output of the ported examples is captured here; each case compares it with +// what the original program printed (built from upstream/boost alone). +inline std::ostringstream ex_out; +inline std::istringstream ex_in; +inline std::vector<std::string> ex_args; // argv of the ported main +inline std::vector<char*> ex_argv_store; +inline char** ex_argv = nullptr; +inline int ex_argc = 0; +struct ex_argv_t { // converts to char** and char const** (both main signatures) + char** p; + operator char**() const { return p; } + operator const char**() const { return const_cast<const char**>(p); } +}; +template <class... A> +int ex_printf(const char* fmt, A... a) { + int n = std::snprintf(nullptr, 0, fmt, a...); + std::string s(std::size_t(n) + 1, '\0'); + std::snprintf(s.data(), s.size(), fmt, a...); + s.resize(std::size_t(n)); + ex_out << s; + return n; +} +inline int ex_puts(const char* s) { + ex_out << s << '\n'; + return 0; +} + +// libs/container/example/doc_custom_deque.cpp +namespace ex_doc_custom_deque { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + +//-------------------------------------------- +// 'stored_size' option +//-------------------------------------------- + + //This option specifies that a deque that will use "unsigned char" as + //the type to store capacity or size internally. + typedef deque_options< stored_size<unsigned char> >::type size_option_t; + + //Size-optimized deque is smaller than the default one. + typedef deque<int, new_allocator<int>, size_option_t > size_optimized_deque_t; + assert(( sizeof(size_optimized_deque_t) < sizeof(deque<int>) )); + + //Requesting capacity for more elements than representable by "unsigned char" + //is an error in the size optimized deque. + bool exception_thrown = false; + /*<-*/ + #ifndef BOOST_NO_EXCEPTIONS + BOOST_CONTAINER_TRY{ size_optimized_deque_t v(256); } BOOST_CONTAINER_CATCH(...){ exception_thrown = true; } BOOST_CONTAINER_CATCH_END + #else + exception_thrown = true; + #endif //BOOST_NO_EXCEPTIONS + /*->*/ + //=try { size_optimized_deque_t v(256); } + //=catch(...){ exception_thrown = true; } + + assert(exception_thrown == true); + +//-------------------------------------------- +// 'block_size/segment_size' option +//-------------------------------------------- + + //This option specifies the desired block size for deque + typedef deque_options< block_size<128u> >::type block_128_option_t; + + //segment_size is an alias for block_size (an alias for block_size) + typedef deque_options< segment_size<128u> >::type segment_128_option_t; + + //This deque will allocate blocks of 128 elements + typedef deque<int, void, block_128_option_t > block_128_deque_t; + assert(block_128_deque_t::get_block_size() == 128u); + + //This deque will allocate segments of 128 elements (an alias for block_size) + typedef deque<int, void, segment_128_option_t > segment_128_deque_t; + assert(segment_128_deque_t::get_block_size() == 128u); + +//-------------------------------------------- +// 'block_bytes/segment_bytes' option +//-------------------------------------------- + + //This option specifies the maximum block size for deque + //in bytes + typedef deque_options< block_bytes<1024u> >::type block_1024_bytes_option_t; + + //This option specifies the maximum segment size for deque + //in bytes (an alias for block_bytes) + typedef deque_options< segment_bytes<1024u> >::type segment_1024_bytes_option_t; + + //This deque will allocate blocks of 1024 bytes + typedef deque<int, void, block_1024_bytes_option_t > block_1024_bytes_deque_t; + assert(block_1024_bytes_deque_t::get_block_size() == 1024u/sizeof(int)); + + //This deque will allocate blocks of 1024 bytes (an alias for block_bytes) + typedef deque<int, void, segment_1024_bytes_option_t > segment_1024_bytes_deque_t; + assert(segment_1024_bytes_deque_t::get_block_size() == 1024u/sizeof(int)); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_custom_deque") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_custom_deque + +// libs/container/example/doc_custom_devector.cpp +namespace ex_doc_custom_devector { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + +//-------------------------------------------- +// 'stored_size' option +//-------------------------------------------- + + //Specify that a devector will use "unsigned char" as the type to store size/capacity + typedef devector_options< stored_size<unsigned char> >::type size_option_t; + + //Size-optimized devector is smaller than the default one. + typedef devector<int, new_allocator<int>, size_option_t > size_optimized_devector_t; + assert(( sizeof(size_optimized_devector_t) < sizeof(devector<int>) )); + + //Requesting capacity for more elements than representable by "unsigned char" is an error + bool exception_thrown = false; + /*<-*/ + #ifndef BOOST_NO_EXCEPTIONS + BOOST_CONTAINER_TRY{ size_optimized_devector_t v(256); } BOOST_CONTAINER_CATCH(...){ exception_thrown = true; } BOOST_CONTAINER_CATCH_END + #else + exception_thrown = true; + #endif //BOOST_NO_EXCEPTIONS + /*->*/ + //=try { size_optimized_devector_t v(256); } + //=catch(...){ exception_thrown = true; } + assert(exception_thrown == true); + +//-------------------------------------------- +// 'growth_factor' option +//-------------------------------------------- + + //Specify that a devector will increase its capacity 50% when reallocating + typedef devector_options< growth_factor<growth_factor_50> >::type growth_50_option_t; + + //Fill the devector until full capacity is reached + devector<int, new_allocator<int>, growth_50_option_t > growth_50_dv(5, 0); + std::size_t old_cap = growth_50_dv.capacity(); + growth_50_dv.resize(old_cap); + + //Now insert an additional item and check the new buffer is 50% bigger + growth_50_dv.push_back(1); + assert(growth_50_dv.capacity() == old_cap*3/2); + +//-------------------------------------------- +// 'relocate_on' option +//-------------------------------------------- + + //Specifies that a devector will not reallocate but relocate elements if the free space + //at one end is exhausted and the total load factor is below the 66% threshold. + typedef devector_options< relocate_on_66 >::type relocation_66_option_t; + + //Configure devector to have equal free space at both ends + devector<int, new_allocator<int>, relocation_66_option_t > reloc_66_dv + (16u, 16u, reserve_only_tag_t()); + old_cap = reloc_66_dv.capacity(); + const std::size_t front_free_cap = reloc_66_dv.front_free_capacity(); + + //Fill vector at the back end + while (reloc_66_dv.back_free_capacity() > 0) + reloc_66_dv.push_back(0); + + //Front free capacity is intact + assert(reloc_66_dv.front_free_capacity() == front_free_cap); + + //Now insert new element, values should relocated to the middle as the + //load factor is near 50% + reloc_66_dv.push_back(0); + assert(reloc_66_dv.capacity() == old_cap); + assert(reloc_66_dv.front_free_capacity() < front_free_cap); + + //Fill the back end again + while (reloc_66_dv.back_free_capacity() > 0) + reloc_66_dv.push_back(1); + + //New insertion should reallocate as load factor is higher than 66% + reloc_66_dv.push_back(-1); + assert(reloc_66_dv.capacity() > old_cap); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_custom_devector") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_custom_devector + +// libs/container/example/doc_custom_segtor.cpp +namespace ex_doc_custom_segtor { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + +//-------------------------------------------- +// 'stored_size' option +//-------------------------------------------- + + //This option specifies that a segtor will use "unsigned char" as + //the type to store capacity or size internally. + typedef segtor_options< stored_size<unsigned char> >::type size_option_t; + + //Size-optimized segtor is smaller than the default one. + typedef segtor<int, new_allocator<int>, size_option_t > size_optimized_segtor_t; + assert(( sizeof(size_optimized_segtor_t) < sizeof(segtor<int>) )); + + //Requesting capacity for more elements than representable by "unsigned char" + //is an error in the size optimized segtor. + bool exception_thrown = false; + /*<-*/ + #ifndef BOOST_NO_EXCEPTIONS + BOOST_CONTAINER_TRY{ size_optimized_segtor_t v(256); } BOOST_CONTAINER_CATCH(...){ exception_thrown = true; } BOOST_CONTAINER_CATCH_END + #else + exception_thrown = true; + #endif //BOOST_NO_EXCEPTIONS + /*->*/ + //=try { size_optimized_segtor_t v(256); } + //=catch(...){ exception_thrown = true; } + + assert(exception_thrown == true); + +//-------------------------------------------- +// 'block_size/segment_size' option +//-------------------------------------------- + + //This option specifies the desired block size for segtor + typedef segtor_options< block_size<128u> >::type block_128_option_t; + + //segment_size is an alias for block_size (an alias for block_size) + typedef segtor_options< segment_size<128u> >::type segment_128_option_t; + + //This segtor will allocate blocks of 128 elements + typedef segtor<int, void, block_128_option_t > block_128_segtor_t; + assert(block_128_segtor_t::get_block_size() == 128u); + + //This segtor will allocate segments of 128 elements (an alias for block_size) + typedef segtor<int, void, segment_128_option_t > segment_128_segtor_t; + assert(segment_128_segtor_t::get_block_size() == 128u); + +//-------------------------------------------- +// 'block_bytes/segment_bytes' option +//-------------------------------------------- + + //This option specifies the maximum block size for segtor + //in bytes + typedef segtor_options< block_bytes<1024u> >::type block_1024_bytes_option_t; + + //This option specifies the maximum segment size for segtor + //in bytes (an alias for block_bytes) + typedef segtor_options< segment_bytes<1024u> >::type segment_1024_bytes_option_t; + + //This segtor will allocate blocks of 1024 bytes + typedef segtor<int, void, block_1024_bytes_option_t > block_1024_bytes_segtor_t; + assert(block_1024_bytes_segtor_t::get_block_size() == 1024u/sizeof(int)); + + //This segtor will allocate blocks of 1024 bytes (an alias for block_bytes) + typedef segtor<int, void, segment_1024_bytes_option_t > segment_1024_bytes_segtor_t; + assert(segment_1024_bytes_segtor_t::get_block_size() == 1024u/sizeof(int)); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_custom_segtor") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_custom_segtor + +// libs/container/example/doc_custom_small_vector.cpp +namespace ex_doc_custom_small_vector { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + +//-------------------------------------------- +// 'inplace_alignment' option +//-------------------------------------------- + + //This option specifies the desired alignment for the internal value_type + typedef small_vector_options< inplace_alignment<16u> >::type alignment_16_option_t; + + //Check 16 byte alignment option + small_vector<int, 10, void, alignment_16_option_t > sv; + assert(((std::size_t)sv.data() % 16u) == 0); + +//-------------------------------------------- +// 'growth_factor' option +//-------------------------------------------- + + //This option specifies that a vector will increase its capacity 50% + //each time the previous capacity was exhausted. + typedef small_vector_options< growth_factor<growth_factor_50> >::type growth_50_option_t; + + //Fill the vector until full capacity is reached + small_vector<int, 10, void, growth_50_option_t > growth_50_vector(10, 0); + const std::size_t old_cap = growth_50_vector.capacity(); + growth_50_vector.resize(old_cap); + + //Now insert an additional item and check the new buffer is 50% bigger + growth_50_vector.push_back(1); + assert(growth_50_vector.capacity() == old_cap*3/2); + +//-------------------------------------------- +// 'stored_size' option +//-------------------------------------------- + + //This option specifies that a vector that will use "unsigned char" as + //the type to store capacity or size internally. + typedef small_vector_options< stored_size<unsigned char> >::type size_option_t; + + //Size-optimized vector is smaller than the default one. + typedef small_vector<int, 10, new_allocator<int>, size_option_t > size_optimized_vector_t; + assert((sizeof(size_optimized_vector_t) < sizeof(small_vector<int, 10>))); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_custom_small_vector") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_custom_small_vector + +// libs/container/example/doc_custom_static_vector.cpp +namespace ex_doc_custom_static_vector { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + +//-------------------------------------------- +// 'inplace_alignment' option +//-------------------------------------------- + + //This option specifies the desired alignment for value_type + typedef static_vector_options< inplace_alignment<16u> >::type alignment_16_option_t; + + //Check 16 byte alignment option + static_vector<int, 10, alignment_16_option_t > sv; + assert(((std::size_t)sv.data() % 16u) == 0); + +//-------------------------------------------- +// 'throw_on_overflow' option +//-------------------------------------------- + + //This static_vector won't throw on overflow, for maximum performance + typedef static_vector_options< throw_on_overflow<false> >::type no_throw_options_t; + + //Create static_vector with no throw on overflow + static_vector<int, 10, no_throw_options_t > sv2; + +//-------------------------------------------- +// 'stored_size' option +//-------------------------------------------- + + //This options specifies that internal `size()` can be represented by an unsigned char + //instead of the default `std::size_t`. + typedef static_vector_options< stored_size<unsigned char> >::type stored_size_uchar_t; + + //Create static_vector that internally stores `size()` in an unsigned char + //because `capacity()` will be less thatn 256. + static_vector<unsigned char, 10, stored_size_uchar_t> sv3; + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_custom_static_vector") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_custom_static_vector + +// libs/container/example/doc_custom_string.cpp +namespace ex_doc_custom_string { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + +//-------------------------- +// Option "stored_size" +//-------------------------- + + //This option specifies that a string that will use "unsigned char" as + //the type to store capacity or size internally. + typedef string_options< stored_size<unsigned char> >::type size_option_t; + + //Size-optimized string is smaller than the default one. + typedef basic_string<char, std::char_traits<char>, void, size_option_t > size_optimized_string_t; + assert(( sizeof(size_optimized_string_t) < sizeof(basic_string<char>) )); + + //The internal small string optimization buffer is also smaller + assert(( size_optimized_string_t().capacity() < basic_string<char>().capacity() )); + + //Requesting capacity for more elements than representable by half of the + //"unsigned char" max (due to the need to a bit to indicate short/long representation) + //minus 1 (due to the terminating null) is an error in the size optimized string. + bool exception_thrown = false; + /*<-*/ + #ifndef BOOST_NO_EXCEPTIONS + BOOST_CONTAINER_TRY{ size_optimized_string_t v(127, 'a');} BOOST_CONTAINER_CATCH(...) { exception_thrown = true; } BOOST_CONTAINER_CATCH_END + #else + exception_thrown = true; + #endif //BOOST_NO_EXCEPTIONS + /*->*/ + //=try { size_optimized_string_t v(127, 'a'); } + //=catch(...){ exception_thrown = true; } + + assert(exception_thrown == true); + +//-------------------------- +// Option "inline_chars" +//-------------------------- + + //This option specifies the capacity of the internally stored buffer + //The maximum value due to internal data organization is 127 chars + typedef string_options< inline_chars<100> >::type inline_chars_option_t; + typedef basic_string<char, std::char_traits<char>, void, inline_chars_option_t > inline100_string_t; + + //The size of the object will grow accordingly + assert(( sizeof(inline100_string_t) > sizeof(basic_string<char>) )); + assert(( sizeof(inline100_string_t) > 100 )); + + //Internal capacity will be at least the specified one + assert((inline100_string_t().capacity() >= 100)); + +//-------------------------- +// Option "growth_factor" +//-------------------------- + + //This option specifies that a string will increase its capacity 50% + //each time the previous capacity was exhausted. + typedef string_options< growth_factor<growth_factor_50> >::type growth_50_option_t; + + //Fill the string until full capacity is reached + basic_string<char, std::char_traits<char>, void, growth_50_option_t > growth_50_string(5, 0); + const std::size_t old_cap = growth_50_string.capacity(); + growth_50_string.resize(old_cap); + + //Now insert an additional item and check the new buffer is aproximately + //50% bigger (rounding due to the null terminator might round down) + growth_50_string.push_back(1); + assert(growth_50_string.capacity() == old_cap*3/2); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_custom_string") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_custom_string + +// libs/container/example/doc_custom_tree.cpp +namespace ex_doc_custom_tree { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + + //First define several options + +//-------------------------------------------- +// 'tree_type' option +//-------------------------------------------- + + //This option specifies an AVL tree based associative container + typedef tree_assoc_options< tree_type<avl_tree> >::type AVLTree; + + //This option specifies an Splay tree based associative container + typedef tree_assoc_options< tree_type<splay_tree> >::type SplayTree; + + //Splay tree based multiset container + typedef multiset<int, std::less<int>, new_allocator<int>, SplayTree> SplayMultiset; + + //AVLTree based set container + typedef set<int, std::less<int>, new_allocator<int>, AVLTree> AvlSet; + + AvlSet avl_set; + avl_set.insert(0); + assert(avl_set.find(0) != avl_set.end()); + + SplayMultiset splay_mset; + splay_mset.insert(2); + splay_mset.insert(2); + assert(splay_mset.count(2) == 2); + +//-------------------------------------------- +// 'optimize_size' option +//-------------------------------------------- + + //This option specifies an AVL tree based associative container + //disabling node size optimization. + typedef tree_assoc_options< tree_type<avl_tree> + , optimize_size<false> >::type AVLTreeNoSizeOpt; + + //AVLTree based set container without size optimization + typedef set<int, std::less<int>, new_allocator<int>, AVLTreeNoSizeOpt> AvlSetNoSizeOpt; + + AvlSetNoSizeOpt avl_set_no_szopt; + avl_set_no_szopt.insert(1); + avl_set_no_szopt.insert(1); + assert(avl_set_no_szopt.count(1) == 1); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_custom_tree") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_custom_tree + +// libs/container/example/doc_custom_vector.cpp +namespace ex_doc_custom_vector { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + +//-------------------------------------------- +// 'stored_size' option +//-------------------------------------------- + + //This option specifies that a vector that will use "unsigned char" as + //the type to store capacity or size internally. + typedef vector_options< stored_size<unsigned char> >::type size_option_t; + + //Size-optimized vector is smaller than the default one. + typedef vector<int, new_allocator<int>, size_option_t > size_optimized_vector_t; + assert(( sizeof(size_optimized_vector_t) < sizeof(vector<int>) )); + + //Requesting capacity for more elements than representable by "unsigned char" + //is an error in the size optimized vector. + bool exception_thrown = false; + /*<-*/ + #ifndef BOOST_NO_EXCEPTIONS + BOOST_CONTAINER_TRY{ size_optimized_vector_t v(256); } BOOST_CONTAINER_CATCH(...){ exception_thrown = true; } BOOST_CONTAINER_CATCH_END + #else + exception_thrown = true; + #endif //BOOST_NO_EXCEPTIONS + /*->*/ + //=try { size_optimized_vector_t v(256); } + //=catch(...){ exception_thrown = true; } + + assert(exception_thrown == true); + +//-------------------------------------------- +// 'growth_factor' option +//-------------------------------------------- + + //This option specifies that a vector will increase its capacity 50% + //each time the previous capacity was exhausted. + typedef vector_options< growth_factor<growth_factor_50> >::type growth_50_option_t; + + //Fill the vector until full capacity is reached + vector<int, new_allocator<int>, growth_50_option_t > growth_50_vector(5, 0); + const std::size_t old_cap = growth_50_vector.capacity(); + growth_50_vector.resize(old_cap); + + //Now insert an additional item and check the new buffer is 50% bigger + growth_50_vector.push_back(1); + assert(growth_50_vector.capacity() == old_cap*3/2); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_custom_vector") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_custom_vector + +// libs/container/example/doc_devector.cpp +namespace ex_doc_devector { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + + devector<int> dv; + + //Unlike vector, a devector can grow cheaply (amortized constant time) + //at *both* ends, while keeping all elements in contiguous memory. + dv.push_back(2); // {2} + dv.push_back(3); // {2, 3} + dv.push_front(1); // {1, 2, 3} + dv.push_front(0); // {0, 1, 2, 3} + + assert(dv.size() == 4); + assert(dv.front() == 0); + assert(dv.back() == 3); + + //Random access and contiguous storage, just like vector. + assert(dv[0] == 0 && dv[3] == 3); + const int *p = dv.data(); + for(std::size_t i = 0, n = dv.size(); i != n; ++i) + assert(p[i] == (int)i); + + //We can ask for free capacity at each end independently, so that the + //following insertions at that end do not trigger a reallocation. + dv.reserve_front(dv.size() + 4); + dv.reserve_back(dv.size() + 4); + + //emplace variants construct the element in place at either end. + dv.emplace_front(-1); // {-1, 0, 1, 2, 3} + dv.emplace_back(4); // {-1, 0, 1, 2, 3, 4} + assert(dv.front() == -1); + assert(dv.back() == 4); + + //Removal at both ends is constant time and never reallocates. + dv.pop_front(); // {0, 1, 2, 3, 4} + dv.pop_back(); // {0, 1, 2, 3} + assert(dv.size() == 4); + assert(dv.front() == 0); + assert(dv.back() == 3); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_devector") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_devector + +// libs/container/example/doc_emplace.cpp +namespace ex_doc_emplace { +class non_copy_movable +{ + non_copy_movable(const non_copy_movable &); + non_copy_movable& operator=(const non_copy_movable &); + + public: + non_copy_movable(int = 0) {} +}; + +int example_main() +{ + using namespace boost::container; + + //Store non-copyable and non-movable objects in a list + list<non_copy_movable> l; + non_copy_movable ncm; + + //A new element will be built calling non_copy_movable(int) constructor + l.emplace(l.begin(), 0); + assert(l.size() == 1); + + //A new element will be value initialized + l.emplace(l.begin()); + assert(l.size() == 2); + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_emplace") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_emplace + +// libs/container/example/doc_flat_map.cpp +namespace ex_doc_flat_map { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + + typedef flat_map<int, int> map_t; + + map_t m; + + //Like vector, we can reserve storage to avoid reallocations while filling. + m.reserve(8); + + //Insertions keep the underlying vector sorted by key. They are O(N) + //because the elements after the insertion point must be shifted. + m[30] = 3; + m[10] = 1; + m[20] = 2; + assert(m.size() == 3); + + //Iteration is in key order and over contiguous memory, using random-access + //iterators, so it is much faster than a node-based std::map. + { + map_t::const_iterator it = m.begin(); + assert(it->first == 10 && it->second == 1); ++it; + assert(it->first == 20 && it->second == 2); ++it; + assert(it->first == 30 && it->second == 3); + } + + //Lookup uses binary search: O(log N). + map_t::iterator f = m.find(20); + assert(f != m.end() && f->second == 2); + + //All values live in a single underlying sequence of value_type, which is + //std::pair<Key, T> (an array of structs). This is a key difference with the + //C++23 std::flat_map, a container *adaptor* that keeps keys and mapped + //values in two separate, parallel containers (a structure of arrays). + const map_t::value_type *raw = &*m.begin(); + assert(raw[0].first == 10 && raw[2].first == 30); + + //The underlying sorted vector can be moved out with extract_sequence() and + //moved back in with adopt_sequence(). Because the extracted sequence is + //already ordered and free of duplicates, we can re-adopt it in O(1) using + //the ordered_unique_range_t overload. + map_t::sequence_type seq = m.extract_sequence(); + assert(m.empty()); + m.adopt_sequence(ordered_unique_range_t(), boost::move(seq)); + assert(m.size() == 3 && m.find(30) != m.end()); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_flat_map") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_flat_map + +// libs/container/example/doc_hub_basic.cpp +namespace ex_doc_hub_basic { +#if BOOST_CXX_VERSION < 201103L + +int example_main() { return 0; return 0; // main's implicit return +} + +#else + +#ifdef NDEBUG +# undef NDEBUG +#endif + +int example_main() +{ + boost::container::hub<int> h; + + // Insert some elements and keep an iterator to one of them + for(int i = 0; i < 100; ++i) h.insert(i); + auto it = h.insert(100); + for(int i = 101; i < 200; ++i) h.insert(i); + + // Erase some of the elements + erase_if(h, [](int x) { return x % 2 != 0;}); + assert(*it == 100); // iterator still valid + + // Insert many more elements + for(int i = 200; i < 10000; ++i) h.insert(i); + assert(*it == 100); // iterator still valid + return 0; // main's implicit return +} + +#endif + +TEST_CASE("example: doc_hub_basic") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_hub_basic + +// libs/container/example/doc_move_containers.cpp +namespace ex_doc_move_containers { +class non_copyable +{ + BOOST_MOVABLE_BUT_NOT_COPYABLE(non_copyable) + + public: + non_copyable(){} + non_copyable(BOOST_RV_REF(non_copyable)) {} + non_copyable& operator=(BOOST_RV_REF(non_copyable)) { return *this; } +}; + +int example_main() +{ + using namespace boost::container; + + //Store non-copyable objects in a vector + vector<non_copyable> v; + non_copyable nc; + v.push_back(boost::move(nc)); + assert(v.size() == 1); + + //Reserve no longer needs copy-constructible + v.reserve(100); + assert(v.capacity() >= 100); + + //This resize overload only needs movable and default constructible + v.resize(200); + assert(v.size() == 200); + + //Containers are also movable + vector<non_copyable> v_other(boost::move(v)); + assert(v_other.size() == 200); + assert(v.empty()); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_move_containers") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_move_containers + +// libs/container/example/doc_pmr.cpp +namespace ex_doc_pmr { +class ShoppingList +{ + // A vector of strings using polymorphic allocators. Every element + // of the vector will use the same allocator as the vector itself. + boost::container::pmr::vector_of + <boost::container::pmr::string>::type m_strvec; + //Alternatively in compilers that support template aliases: + // boost::container::pmr::vector<boost::container::pmr::string> m_strvec; + public: + + // This makes uses_allocator<ShoppingList, memory_resource*>::value true + typedef boost::container::pmr::memory_resource* allocator_type; + + // If the allocator is not specified, "m_strvec" uses pmr::get_default_resource(). + explicit ShoppingList(allocator_type alloc = 0) + : m_strvec(alloc) {} + + // Copy constructor. As allocator is not specified, + // "m_strvec" uses pmr::get_default_resource(). + ShoppingList(const ShoppingList& other) + : m_strvec(other.m_strvec) {} + + // Copy construct using the given memory_resource. + ShoppingList(const ShoppingList& other, allocator_type a) + : m_strvec(other.m_strvec, a) {} + + allocator_type get_allocator() const + { return m_strvec.get_allocator().resource(); } + + void add_item(const char *item) + { m_strvec.emplace_back(item); } + + //... +}; + +//]] + +//[doc_pmr_main_cpp + +//=#include "ShoppingList.hpp" + +void processShoppingList(const ShoppingList&) +{ /**/ } + +int example_main() +{ + using namespace boost::container; + //All memory needed by folder and its contained objects will + //be allocated from the default memory resource (usually new/delete) + pmr::list_of<ShoppingList>::type folder; // Default allocator resource + //Alternatively in compilers that support template aliases: + // boost::container::pmr::list<ShoppingList> folder; + { + char buffer[1024]; + pmr::monotonic_buffer_resource buf_rsrc(&buffer, 1024); + + //All memory needed by temporaryShoppingList will be allocated + //from the local buffer (speeds up "processShoppingList") + ShoppingList temporaryShoppingList(&buf_rsrc); + assert(&buf_rsrc == temporaryShoppingList.get_allocator()); + + //list nodes, and strings "salt" and "pepper" will be allocated + //in the stack thanks to "monotonic_buffer_resource". + temporaryShoppingList.add_item("salt"); + temporaryShoppingList.add_item("pepper"); + //... + + //All modifications and additions to "temporaryShoppingList" + //will use memory from "buffer" until it's exhausted. + processShoppingList(temporaryShoppingList); + + //Processing done, now insert it in "folder", + //which uses the default memory resource + folder.push_back(temporaryShoppingList); + assert(pmr::get_default_resource() == folder.back().get_allocator()); + //temporaryShoppingList, buf_rsrc, and buffer go out of scope + } + return 0; + return 0; // main's implicit return +} + +//] + +TEST_CASE("example: doc_pmr") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_pmr + +// libs/container/example/doc_recursive_containers.cpp +namespace ex_doc_recursive_containers { +using namespace boost::container; + +struct data +{ + int i_; + //A vector holding still undefined class 'data' + vector<data> v_; + vector<data>::iterator vi_; + //A stable_vector holding still undefined class 'data' + stable_vector<data> sv_; + stable_vector<data>::iterator svi_; + //A stable_vector holding still undefined class 'data' + deque<data> d_; + deque<data>::iterator di_; + //A list holding still undefined 'data' + list<data> l_; + list<data>::iterator li_; + //A map holding still undefined 'data' + map<data, data> m_; + map<data, data>::iterator mi_; + + friend bool operator <(const data &l, const data &r) + { return l.i_ < r.i_; } +}; + +struct tree_node +{ + string name; + string value; + + //children nodes of this node + list<tree_node> children_; + list<tree_node>::iterator selected_child_; +}; + + + +int example_main() +{ + //a container holding a recursive data type + stable_vector<data> sv; + sv.resize(100); + + //Let's build a tree based in + //a recursive data type + tree_node root; + root.name = "root"; + root.value = "root_value"; + root.children_.resize(7); + root.selected_child_ = root.children_.begin(); + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_recursive_containers") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_recursive_containers + +// libs/container/example/doc_slist.cpp +namespace ex_doc_slist { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + + typedef slist<int> slist_t; + + slist_t l; + + //A singly linked list offers constant-time insertion/removal at the front. + l.push_front(3); + l.push_front(2); + l.push_front(1); // {1, 2, 3} + + //Unlike std::forward_list, Boost.Container slist keeps track of its length, + //so size() is a constant-time operation. + assert(l.size() == 3); + assert(l.front() == 1); + + //The efficient way to insert into a singly linked list is *after* a known + //position (constant time). insert_after/erase_after avoid the linear scan + //that plain insert/erase would need to locate the previous node. + slist_t::iterator it = l.begin(); // -> 1 + ++it; // -> 2 + l.insert_after(it, 20); // {1, 2, 20, 3} + assert(l.size() == 4); + + //erase_after removes the element following the iterator in constant time. + l.erase_after(it); // removes 20 -> {1, 2, 3} + assert(l.size() == 3); + + //before_begin() denotes the position just before the first element, so + //inserting after it is equivalent to push_front. + l.insert_after(l.before_begin(), 0); // {0, 1, 2, 3} + assert(l.front() == 0); + + //slist provides a size-aware splice_after overload that merges a range from + //another list in constant time when the number of elements is known. + slist_t other; + other.push_front(6); + other.push_front(5); // {5, 6} + + slist_t::iterator before_last = other.begin(); + ++before_last; // points to the last element (6) + //Move the whole 'other' list to the front of 'l' in O(1). + l.splice_after(l.before_begin(), other, + other.before_begin(), before_last, other.size()); + assert(other.empty()); // {5, 6, 0, 1, 2, 3} + + //Forward, in-order traversal. + const int expected[] = { 5, 6, 0, 1, 2, 3 }; + int i = 0; + for(slist_t::const_iterator b = l.begin(), e = l.end(); b != e; ++b, ++i) + assert(*b == expected[i]); + assert(l.size() == 6); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_slist") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_slist + +// libs/container/example/doc_small_vector_base.cpp +namespace ex_doc_small_vector_base { +#ifdef NDEBUG +#undef NDEBUG +#endif + +using namespace boost::container; + +//small_vector_base<T> erases the preallocated element count "N" from the type, +//so the following non-template functions accept any small_vector<int, N>. + +//Reads any small_vector<int, N> (taken by const reference to the base). +int sum(const small_vector_base<int> &sv) +{ + int res = 0; + for(std::size_t i = 0, n = sv.size(); i != n; ++i) + res += sv[i]; + return res; +} + +//Modifies any small_vector<int, N> (taken by reference to the base). +void push_range(small_vector_base<int> &sv, int first, int last) +{ + for(int i = first; i < last; ++i) + sv.push_back(i); +} + +int example_main() +{ + //Two small_vectors with different in-place capacities (N = 4 and N = 64)... + small_vector<int, 4> sv_small; + small_vector<int, 64> sv_big; + + //...both implicitly convert to small_vector_base<int>, so the very same + //non-template functions can operate on either of them. + push_range(sv_small, 0, 5); //grows beyond its 4 preallocated elements + push_range(sv_big, 0, 5); //stays within its 64 preallocated elements + + //The behaviour is identical regardless of the preallocated capacity N. + assert(sum(sv_small) == (0+1+2+3+4)); + assert(sum(sv_small) == sum(sv_big)); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_small_vector_base") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_small_vector_base + +// libs/container/example/doc_static_vector.cpp +namespace ex_doc_static_vector { +#ifdef NDEBUG +#undef NDEBUG +#endif + +int example_main() +{ + using namespace boost::container; + + //A static_vector<int> that can hold at most 5 elements. All the storage + //lives inside the object itself, so creating it performs no dynamic + //allocation and the capacity is fixed at compile time. + static_vector<int, 5> sv; + + assert(sv.empty()); + assert(sv.capacity() == 5); //fixed capacity, known at compile time + assert(sv.max_size() == 5); + + //Elements are inserted just like with a vector (constant time at the end). + //Unlike std::array, elements are constructed only as they are inserted. + for(int i = 0; i < 5; ++i) + sv.push_back(i); + + assert(sv.size() == 5); + assert(sv.size() == sv.capacity()); //the container is now full + + //Contiguous storage and random access, just like vector/array. + assert(sv[0] == 0 && sv[4] == 4); + assert(sv.data()[2] == 2); + + #ifndef BOOST_NO_EXCEPTIONS + //By default (throw_on_overflow<true>) inserting beyond the fixed capacity + //throws instead of allocating more memory. + bool overflowed = false; + try { + sv.push_back(5); //capacity already reached + } + catch(const std::exception &) { + overflowed = true; + } + assert(overflowed); + #endif + + //Removal at the end is constant time and never reallocates. + sv.pop_back(); + assert(sv.size() == 4); + + return 0; + return 0; // main's implicit return +} +//] + +TEST_CASE("example: doc_static_vector") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_static_vector + +// libs/container/example/doc_type_erasure.cpp +namespace ex_doc_type_erasure { +class MyClass; + +class MyClassHolder +{ + public: + + void AddNewObject(const MyClass &o); + const MyClass & GetLastObject() const; + + private: + ::boost::container::vector<MyClass> vector_; +}; + +//] + +//[doc_type_erasure_MyClass_h + +//MyClass.h + +class MyClass +{ + private: + int value_; + + public: + MyClass(int val = 0) : value_(val){} + + friend bool operator==(const MyClass &l, const MyClass &r) + { return l.value_ == r.value_; } + //... +}; + +//] + + + +//[doc_type_erasure_main_cpp + +//Main.cpp + +//=#include "MyClassHolder.h" +//=#include "MyClass.h" + + +int example_main() +{ + MyClass mc(7); + MyClassHolder myclassholder; + myclassholder.AddNewObject(mc); + return myclassholder.GetLastObject() == mc ? 0 : 1; + return 0; // main's implicit return +} +//] + +//[doc_type_erasure_MyClassHolder_cpp + +//MyClassHolder.cpp + +//=#include "MyClassHolder.h" + +//In the implementation MyClass must be a complete +//type so we include the appropriate header +//=#include "MyClass.h" + +void MyClassHolder::AddNewObject(const MyClass &o) +{ vector_.push_back(o); } + +const MyClass & MyClassHolder::GetLastObject() const +{ return vector_.back(); } + +//] + +TEST_CASE("example: doc_type_erasure") { + ex_out = std::ostringstream(); // fresh state: no flags left by another example + int rc = example_main(); + CHECK(rc == 0); + CHECK(ex_out.str() == std::string_view(R"ex()ex")); +} +} // namespace ex_doc_type_erasure diff --git a/tests/describe/examples_json.cpp b/tests/describe/examples_json.cpp index c5ba4d27..fdecc1af 100644 --- a/tests/describe/examples_json.cpp +++ b/tests/describe/examples_json.cpp @@ -12,6 +12,7 @@ #include <cstdio> #include <boost/describe.hpp> #include <boost/version.hpp> +#include <boost/config.hpp> import std; import mcpplibs.mwrap.boost.json; import mcpplibs.mwrap.boost.mp11; @@ -24,8 +25,15 @@ import mcpplibs.mwrap.boost.core; // what the original program printed (built from upstream/boost alone). inline std::ostringstream ex_out; inline std::istringstream ex_in; -inline char ex_arg0[] = "example"; -inline char* ex_argv[] = {ex_arg0, nullptr}; +inline std::vector<std::string> ex_args; // argv of the ported main +inline std::vector<char*> ex_argv_store; +inline char** ex_argv = nullptr; +inline int ex_argc = 0; +struct ex_argv_t { // converts to char** and char const** (both main signatures) + char** p; + operator char**() const { return p; } + operator const char**() const { return const_cast<const char**>(p); } +}; template <class... A> int ex_printf(const char* fmt, A... a) { int n = std::snprintf(nullptr, 0, fmt, a...); diff --git a/tests/describe/examples_serialization.cpp b/tests/describe/examples_serialization.cpp index 002deb25..426836cc 100644 --- a/tests/describe/examples_serialization.cpp +++ b/tests/describe/examples_serialization.cpp @@ -15,8 +15,15 @@ import mcpplibs.mwrap.boost.core; // what the original program printed (built from upstream/boost alone). inline std::ostringstream ex_out; inline std::istringstream ex_in; -inline char ex_arg0[] = "example"; -inline char* ex_argv[] = {ex_arg0, nullptr}; +inline std::vector<std::string> ex_args; // argv of the ported main +inline std::vector<char*> ex_argv_store; +inline char** ex_argv = nullptr; +inline int ex_argc = 0; +struct ex_argv_t { // converts to char** and char const** (both main signatures) + char** p; + operator char**() const { return p; } + operator const char**() const { return const_cast<const char**>(p); } +}; template <class... A> int ex_printf(const char* fmt, A... a) { int n = std::snprintf(nullptr, 0, fmt, a...); diff --git a/tests/describe/include_examples.cpp b/tests/describe/include_examples.cpp index 3d39d510..b4eb6f13 100644 --- a/tests/describe/include_examples.cpp +++ b/tests/describe/include_examples.cpp @@ -30,13 +30,21 @@ #include <sstream> #include <string> #include <string_view> +#include <vector> // Output of the ported examples is captured here; each case compares it with // what the original program printed (built from upstream/boost alone). inline std::ostringstream ex_out; inline std::istringstream ex_in; -inline char ex_arg0[] = "example"; -inline char* ex_argv[] = {ex_arg0, nullptr}; +inline std::vector<std::string> ex_args; // argv of the ported main +inline std::vector<char*> ex_argv_store; +inline char** ex_argv = nullptr; +inline int ex_argc = 0; +struct ex_argv_t { // converts to char** and char const** (both main signatures) + char** p; + operator char**() const { return p; } + operator const char**() const { return const_cast<const char**>(p); } +}; template <class... A> int ex_printf(const char* fmt, A... a) { int n = std::snprintf(nullptr, 0, fmt, a...); diff --git a/tests/hash2/examples.cpp b/tests/hash2/examples.cpp index ae70fe7c..798affed 100644 --- a/tests/hash2/examples.cpp +++ b/tests/hash2/examples.cpp @@ -7,6 +7,7 @@ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include <doctest/doctest.h> #include <cstdio> +#include <boost/config.hpp> #include <cerrno> import std; import mcpplibs.mwrap.boost.hash2; diff --git a/tools/apply_patches.py b/tools/apply_patches.py index a75367d8..a48b381a 100644 --- a/tools/apply_patches.py +++ b/tools/apply_patches.py @@ -29,9 +29,9 @@ # One entry per patches/<name>.patch; see patches/README.md for each reason. PATCHES = [ "accumulators", "algorithm", "asio", "beast", "bimap", "config_platform", - "config_user", "convert", "hof", "io", "lambda", "mqtt5", "multi_array", - "parameter", "range_adaptors", "numeric", "regex", "safe_numerics", - "serialization", "system", "test", "ublas", "units", + "config_user", "container", "convert", "hof", "io", "lambda", "mqtt5", + "multi_array", "parameter", "range_adaptors", "numeric", "regex", + "safe_numerics", "serialization", "system", "test", "ublas", "units", ] # A vendored file that is an addition, not a patch of an upstream file. diff --git a/upstream/boost/boost/container/pmr/devector.hpp b/upstream/boost/boost/container/pmr/devector.hpp index 9ecbb4fa..140f19a5 100644 --- a/upstream/boost/boost/container/pmr/devector.hpp +++ b/upstream/boost/boost/container/pmr/devector.hpp @@ -8,8 +8,12 @@ // ////////////////////////////////////////////////////////////////////////////// -#ifndef BOOST_CONTAINER_PMR_VECTOR_HPP -#define BOOST_CONTAINER_PMR_VECTOR_HPP +// boost-m vendored edit: upstream reuses pmr/vector.hpp's include guard here, +// so a TU that includes devector.hpp first loses boost::container::pmr::vector +// (the container module's GMF did: pmr::vector / vector_of were never +// exported). Replay after re-running import_boost. +#ifndef BOOST_CONTAINER_PMR_DEVECTOR_HPP +#define BOOST_CONTAINER_PMR_DEVECTOR_HPP #if defined (_MSC_VER) # pragma once @@ -50,4 +54,4 @@ struct devector_of } //namespace container { } //namespace boost { -#endif //BOOST_CONTAINER_PMR_VECTOR_HPP +#endif //BOOST_CONTAINER_PMR_DEVECTOR_HPP