Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions .agents/docs/2026-07-16-add-marzer-tomlplusplus-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# 收录 toml++(marzer.tomlplusplus)3.4.0

> PR: https://github.com/mcpplibs/mcpp-index/pull/64
> 日期:2026-07-16
> 背景:PR #64 原以 fork 形态提交,按 review 意见([comment](https://github.com/mcpplibs/mcpp-index/pull/64#issuecomment-4907425691))
> 参照 `nlohmann.json` 重做。本文记录形态判定、镜像、验证结论与注意事项。

## 1. 来源与形态判定

- 来源:**(a) 第三方上游库**。上游 [marzer/tomlplusplus](https://github.com/marzer/tomlplusplus) 不提供 mcpp 支持。
- 版本:`v3.4.0`(截至 2026-07-16 的最新 release tag)。
- License:MIT。
- 布局:tarball 顶层 wrap 目录为 `tomlplusplus-3.4.0/`,头文件位于 `include/toml++/`,**header-only**。
- 形态:**C++23 module(generated wrapper)** —— 与 `nlohmann.json` 同类。

判定依据:release tarball **不含** 任何 `.cppm`:

```console
$ tar tzf tomlplusplus-3.4.0.tar.gz | grep -iE 'cppm|modules'
tomlplusplus-3.4.0/.gitmodules # 仅 git submodule 配置,非模块单元
```

而上游**确实**已编写官方模块单元 `src/modules/tomlplusplus.cppm`(`export module tomlplusplus;`),
但它只存在于 `master` 分支,未进入 v3.4.0。因此采用 `generated_files` 内嵌该 cppm,
基础头文件仍 pin 在可复现的 v3.4.0 release tag —— **信任链上不引入 fork**。

## 2. 关键注意事项:master 的 cppm 不能原样内嵌

⚠️ 这是本次收录最容易踩的坑,与 `nlohmann.json`(可 VERBATIM 内嵌)不同。

上游 master 的 cppm 面向 master 的头文件编写,其中一行:

```cpp
using TOML_NAMESPACE::get_line;
```

`get_line` 是 v3.4.0 **之后**才加入 `include/toml++/impl/source_region.hpp` 的。对 pinned 的 v3.4.0 头文件做
负向验证,确认其确实不存在:

```console
$ grep -rn "get_line" tomlplusplus-3.4.0/ # 整个 tarball 无任何命中

$ cat probe.cpp
#include <toml++/toml.hpp>
namespace probe { using toml::get_line; }
int main(){}

$ g++ -std=c++17 -fsyntax-only -Iinclude probe.cpp
probe.cpp:2:31: error: 'get_line' has not been declared in 'toml'

$ # 对照组:同一探针换成 toml::parse,编译通过 —— 证明头文件本身无恙
```

**结论**:内嵌版本删除该行,其余**逐字节一致**(已用 `diff` 对内嵌内容与上游 master 减去该行的结果做校验,结果 IDENTICAL)。
差异已在描述符注释中写明。

**演进条件**:待上游发布 >3.4.0 且随 release 附带 `src/modules/tomlplusplus.cppm` 时,可切回
`sources = { "*/src/modules/tomlplusplus.cppm" }` 并删除 `generated_files`,`get_line` 会随之回归。

## 3. 描述符

- 路径:`pkgs/m/marzer.tomlplusplus.lua`(目录取**完整包名首字母** `m`)。
- 命名:`marzer.tomlplusplus`,遵循 `nlohmann.json` 的 `<命名空间>.<库>` 约定。
- `include_dirs = { "*/include" }`:cppm 的 global-module fragment 内 `#include <toml++/toml.hpp>` 由此解析;
同时保留用户直接 `#include` 的能力。`*` 吸收 `tomlplusplus-3.4.0/` wrap 层。
- `generated_files` 的 key 为 verdir 相对路径(无 glob),与 `nlohmann.json` 一致。
- 多行字符串采用 `[==[ ]==]`(而非 `[[ ]]`):与 `nlohmann.json` 保持一致,且对 payload 中可能出现的
`[[`/`]]` 更稳妥。已确认 payload 不含 `]==]`。
- 版本号采用裸版本 `"3.4.0"`;URL 中保留上游 `…/v3.4.0.tar.gz` 拼写。

## 4. feature 评估

**不实现 feature**。toml++ 为 header-only,无“额外的可编译源码”可供门控;其可选行为(如 `TOML_EXCEPTIONS`、
`TOML_ENABLE_FORMATTERS`)均为编译期 **define**,而 `features` 表当前仅能门控 `sources`,无法携带 define。
与 Eigen 的 `EIGEN_MPL2_ONLY` 属同类限制,待 mcpp 支持 define/cflags 后再评估。

## 5. CN 镜像

- 新建 gitcode 仓库 `mcpp-res/tomlplusplus`(经 `gtc repo create` + init commit 建立 `main` 分支)。
- release tag `3.4.0`,资产 `tomlplusplus-3.4.0.tar.gz`,上传的是**与 GLOBAL 完全相同**的 tarball。
- 字节一致性验证:

```console
CN sha: 8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155
GLB sha: 8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155
cmp : byte-identical
```

- sha 稳定性:GLOBAL 重复下载两次,sha 一致(无 GitLab 式重打包漂移)。

## 6. 验证结论

测试工程 `tests/examples/marzer.tomlplusplus/`,已登记进根 `mcpp.toml` 的 `[workspace].members`
(本仓测试面已由 `run_example.sh` 迁移至 `mcpp test --workspace`,故不再新增 `src/main.cpp` 式 bin 例子)。
断言覆盖 parse、typed access、array/table 节点、`_toml` 字面量、serialize→reparse 往返。

| 检查 | 结果 |
|---|---|
| `mcpp test`(GLOBAL) | ✅ `parse ... ok` — 1 passed |
| `mcpp test`(`MCPP_INDEX_MIRROR=CN`,清 `target .mcpp mcpp.lock` 后真实重新下载) | ✅ `parse ... ok` — 1 passed |
| CN vs GLOBAL 字节比对 | ✅ byte-identical |
| 全量 lint(46 descriptor,含 `check_mirror_urls.lua`) | ✅ all valid |
| `mcpp xpkg parse`(CI pin 0.0.91 与本地 0.0.93) | ✅ OK |
| 内嵌 cppm vs 上游 master 减 get_line | ✅ `diff` IDENTICAL |

## 7. 其它

- PR #64 原分支落后 `main` 10 个提交,已 rebase 到最新 `main` 后强推(`maintainerCanModify` 为 true)。
- 原 `pkgs/c/compat.tomlplusplus.lua` 与 `tests/examples/tomlplusplus/` 已删除。
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上
| C 源码 compat(含 `features`) | [`compat.cjson`](pkgs/c/compat.cjson.lua) · [`compat.zlib`](pkgs/c/compat.zlib.lua) |
| header-only(含 `features`) | [`compat.eigen`](pkgs/c/compat.eigen.lua) |
| 外部构建系统(`install()` 从源码构建) | [`compat.openblas`](pkgs/c/compat.openblas.lua)(Make) · [`compat.opencv`](pkgs/c/compat.opencv.lua)(CMake) |
| C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) |
| C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](pkgs/m/marzer.tomlplusplus.lua) |

### 新增一个包

Expand Down
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"tests/examples/gui-stack",
"tests/examples/imgui",
"tests/examples/imgui-window",
"tests/examples/marzer.tomlplusplus",
"tests/examples/nlohmann.json",
"tests/examples/openblas",
"tests/examples/opencv",
Expand Down
172 changes: 172 additions & 0 deletions pkgs/m/marzer.tomlplusplus.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
-- Form B inline descriptor for toml++ (marzer/tomlplusplus) — a TOML config
-- file parser and serializer for C++17 (and later), exposed as the C++23
-- module `tomlplusplus` so users can write `import tomlplusplus;` out of the
-- box (no opt-in, no `#include` needed).
--
-- Why generated: the released v3.4.0 source tarball is header-only and ships
-- NO module interface unit. Upstream HAS authored an official one at
-- `src/modules/tomlplusplus.cppm` (`export module tomlplusplus;`), but it
-- lives on the `master` branch only and is not in any release tag yet (v3.4.0
-- 404s for that path). So we provide it ourselves via mcpp's `generated_files`,
-- embedding upstream's official tomlplusplus.cppm. The base headers stay
-- pinned to the reproducible v3.4.0 release tag, straight from upstream —
-- no fork in the trust path.
--
-- ONE deviation from upstream master's cppm, deliberate and minimal:
-- `using TOML_NAMESPACE::get_line;` is dropped. `get_line` was added to
-- `impl/source_region.hpp` AFTER v3.4.0 and does not exist in the pinned
-- headers, so re-exporting it would not compile. Every other line is verbatim.
--
-- Evolution: once a toml++ release (>3.4.0) ships src/modules/tomlplusplus.cppm,
-- switch `sources` to "*/src/modules/tomlplusplus.cppm", drop `generated_files`,
-- and the get_line re-export comes back with it.
--
-- include_dirs exposes the tarball's include/ so the module unit's global-module
-- fragment `#include <toml++/toml.hpp>` resolves (and `#include` remains
-- available to users who want it). The upstream path is a GLOB — the leading
-- `*` absorbs the archive's `tomlplusplus-3.4.0/` wrap layer — while the
-- generated cppm path is verdir-relative (no glob), like nlohmann.json.
package = {
spec = "1",
namespace = "marzer",
name = "marzer.tomlplusplus",
description = "TOML config file parser and serializer for C++, exposed as C++23 module tomlplusplus",
licenses = {"MIT"},
repo = "https://github.com/marzer/tomlplusplus",
type = "package",

xpm = {
linux = {
["3.4.0"] = {
url = {
GLOBAL = "https://github.com/marzer/tomlplusplus/archive/refs/tags/v3.4.0.tar.gz",
CN = "https://gitcode.com/mcpp-res/tomlplusplus/releases/download/3.4.0/tomlplusplus-3.4.0.tar.gz",
},
sha256 = "8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155",
},
},
macosx = {
["3.4.0"] = {
url = {
GLOBAL = "https://github.com/marzer/tomlplusplus/archive/refs/tags/v3.4.0.tar.gz",
CN = "https://gitcode.com/mcpp-res/tomlplusplus/releases/download/3.4.0/tomlplusplus-3.4.0.tar.gz",
},
sha256 = "8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155",
},
},
windows = {
["3.4.0"] = {
url = {
GLOBAL = "https://github.com/marzer/tomlplusplus/archive/refs/tags/v3.4.0.tar.gz",
CN = "https://gitcode.com/mcpp-res/tomlplusplus/releases/download/3.4.0/tomlplusplus-3.4.0.tar.gz",
},
sha256 = "8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155",
},
},
},

mcpp = {
schema = "0.1",
language = "c++23",
import_std = false,
modules = { "tomlplusplus" },
include_dirs = { "*/include" },
-- Upstream's official module unit (master @ src/modules/tomlplusplus.cppm),
-- reproduced verbatim apart from the get_line drop documented above.
-- Verdir-relative path, no glob.
generated_files = {
["mcpp_generated/tomlplusplus.cppm"] = [==[
/**
* @file tomlpp.cppm
* @brief File containing the module declaration for toml++.
*/

module;

#define TOML_UNDEF_MACROS 0
#include <toml++/toml.hpp>

export module tomlplusplus;

/**
* @namespace toml
* @brief The toml++ namespace toml::
*/
export namespace toml {
/**
* @namespace literals
* @brief The toml++ namespace toml::literals::
*/
inline namespace literals {
using TOML_NAMESPACE::literals::operator""_toml;
using TOML_NAMESPACE::literals::operator""_tpath;
}

using TOML_NAMESPACE::array;
using TOML_NAMESPACE::date;
using TOML_NAMESPACE::date_time;
using TOML_NAMESPACE::inserter;
using TOML_NAMESPACE::json_formatter;
using TOML_NAMESPACE::key;
using TOML_NAMESPACE::node;
using TOML_NAMESPACE::node_view;
using TOML_NAMESPACE::parse_error;
using TOML_NAMESPACE::parse_result;
using TOML_NAMESPACE::path;
using TOML_NAMESPACE::path_component;
using TOML_NAMESPACE::source_position;
using TOML_NAMESPACE::source_region;
using TOML_NAMESPACE::table;
using TOML_NAMESPACE::time;
using TOML_NAMESPACE::time_offset;
using TOML_NAMESPACE::toml_formatter;
using TOML_NAMESPACE::value;
using TOML_NAMESPACE::yaml_formatter;
using TOML_NAMESPACE::format_flags;
using TOML_NAMESPACE::node_type;
using TOML_NAMESPACE::path_component_type;
using TOML_NAMESPACE::value_flags;
using TOML_NAMESPACE::array_iterator;
using TOML_NAMESPACE::const_array_iterator;
using TOML_NAMESPACE::const_table_iterator;
using TOML_NAMESPACE::default_formatter;
using TOML_NAMESPACE::inserted_type_of;
using TOML_NAMESPACE::optional;
using TOML_NAMESPACE::source_index;
using TOML_NAMESPACE::source_path_ptr;
using TOML_NAMESPACE::table_iterator;

using TOML_NAMESPACE::at_path;
using TOML_NAMESPACE::operator""_toml;
using TOML_NAMESPACE::operator""_tpath;
using TOML_NAMESPACE::operator<<;
using TOML_NAMESPACE::parse;
using TOML_NAMESPACE::parse_file;

using TOML_NAMESPACE::is_array;
using TOML_NAMESPACE::is_boolean;
using TOML_NAMESPACE::is_chronological;
using TOML_NAMESPACE::is_container;
using TOML_NAMESPACE::is_date;
using TOML_NAMESPACE::is_date_time;
using TOML_NAMESPACE::is_floating_point;
using TOML_NAMESPACE::is_integer;
using TOML_NAMESPACE::is_key;
using TOML_NAMESPACE::is_key_or_convertible;
using TOML_NAMESPACE::is_node;
using TOML_NAMESPACE::is_node_view;
using TOML_NAMESPACE::is_number;
using TOML_NAMESPACE::is_string;
using TOML_NAMESPACE::is_table;
using TOML_NAMESPACE::is_time;
using TOML_NAMESPACE::is_value;

using TOML_NAMESPACE::preserve_source_value_flags;
}
]==],
},
sources = { "mcpp_generated/tomlplusplus.cppm" },
targets = { ["tomlplusplus"] = { kind = "lib" } },
deps = { },
},
}
11 changes: 11 additions & 0 deletions tests/examples/marzer.tomlplusplus/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# toml++ test project: consumes the C++23 module `tomlplusplus` and asserts
# parse + typed access + round-trip serialization under `mcpp test`.
[package]
name = "tomlplusplus-tests"
version = "0.1.0"

[indices]
marzer = { path = "../../.." }

[dependencies.marzer]
tomlplusplus = "3.4.0"
33 changes: 33 additions & 0 deletions tests/examples/marzer.tomlplusplus/tests/parse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Behavioral test: parse → typed access → array/table nodes → _toml literal →
// serialize-and-reparse round-trip, all through the C++23 module surface
// (`import tomlplusplus;`, no #include).
import std;
import tomlplusplus;

int main() {
auto cfg = toml::parse(R"(
[server]
host = "localhost"
port = 8080
tags = ["a", "b"]
)");

// typed access — value<std::string> so this compares strings, not pointers
bool ok = cfg["server"]["port"].value<int>() == 8080
&& cfg["server"]["host"].value<std::string>() == "localhost"
&& cfg["server"]["tags"].as_array()->size() == 2;

// the _toml literal re-exported through toml::literals
using namespace toml::literals;
auto lit = "x = 1"_toml;
ok = ok && lit["x"].value<int>() == 1;

// round-trip: serialize via the exported operator<<, then re-parse
std::ostringstream os;
os << cfg;
auto again = toml::parse(os.str());
ok = ok && again["server"]["port"].value<int>() == 8080
&& again["server"]["tags"].as_array()->size() == 2;

return ok ? 0 : 1;
}
Loading