.avtg 往返:serialize_roundtrip
标准示例文档:源码清单、学习路线、逐段讲解、常见误区、双平台构建、真实输出与退出码。
用途
把两条 passthrough 工具组成的组存成 .avtg 再读回,校验组名、工具数与顺序不变。演示的是组包往返,不是算法执行。
读完应能回答:为什么 Load 必须拿到注册表?ok 与 diagnostics 各管什么?为什么顺序值得单独断言?
源码
examples/serialize_roundtrip.cpp—— 示例本体:建组、保存、读回、比对、清理(片段 1–6) · 查看完整源文件include/vyntek/toolgroup/serialize.hpp——LoadAvtg/SaveAvtg与LoadResult/SaveResult定义 · 查看完整源文件examples/CMakeLists.txt——serialize_roundtrip目标:链接Vyntek::toolgroup· 查看完整源文件
学习路线
- 先想清楚存了什么:.avtg 存的是组结构与
type_id,不存工具代码,所以读回必须靠注册表重建(片段 1、2)。 - 再看待保存对象:
SaveAvtg只读组,返回SaveResult;失败细节在diagnostics(片段 3)。 - 然后看读回:
LoadAvtg产生一个全新的shared_ptr<ToolGroup>(片段 4)。 - 最后看契约断言:工具名序列与组名(片段 5),以及示例产物的清理(片段 6)。
片段 1 · 取工具名序列(serialize_roundtrip.cpp:19-26)
查看完整源文件std::vector<std::string> ToolNames(const ToolGroup & group) {
std::vector<std::string> names;
names.reserve(group.tools().size());
for (const auto & tool : group.tools()) {
names.push_back(tool ? tool->name() : std::string{});
}
return names;
}讲解:
group.tools()返回const std::vector<std::unique_ptr<Tool>>&,按下标即声明顺序;这就是往返要比对的契约。- 用
tool ? tool->name() : std::string{}防御空指针:结构不应有空元素,但比较函数不该因此崩。 - 只取名字:名字是 .avtg 里稳定可读的标识,
ToolId可能因加载重建而变化。
片段 2 · 建组并装载两个工具(serialize_roundtrip.cpp:33-47)
查看完整源文件 auto group = ToolGroup::Create("serialize-demo");
if (!group) {
std::fprintf(stderr, "ToolGroup::Create failed\n");
return 1;
}
ToolRegistry registry;
registry.RegisterBuiltinTools();
for (const char * name : { "t1", "t2" }) {
auto tool = registry.Create("passthrough", name);
if (!tool || group->AddTool(std::move(tool)) == nullptr) {
std::fprintf(stderr, "AddTool(%s) failed\n", name);
return 2;
}
}讲解:
ToolRegistry registry;必须在 Load 时仍然存活:加载要按type_id重新Create工具。for (const char * name : { "t1", "t2" })用初始化列表固定顺序;工具顺序就是后面断言的期望值。AddTool(std::move(tool)) == nullptr判失败:所有权已转移,返回值只是观察指针。- 退出码 2 同时覆盖「创建失败」与「装载失败」——对调用方都是「组没建起来」。
片段 3 · 保存(serialize_roundtrip.cpp:49-55)
查看完整源文件 const std::string path = "serialize_roundtrip.avtg";
const SaveResult saved = SaveAvtg(*group, path);
if (!saved.ok) {
std::fprintf(stderr, "SaveAvtg failed (%zu diagnostics)\n", saved.diagnostics.size());
return 3;
}
std::printf("save: ok -> %s\n", path.c_str());讲解:
SaveAvtg(const ToolGroup&, const std::string& path)返回SaveResult:ok与diagnostics分开。不要用异常或文件大小判断成功。ok == false时诊断数量是有用信息(示例直接打印),细节应从diagnostics逐条读。- 路径由调用方给:示例用当前工作目录的相对路径,建议在临时目录运行。
片段 4 · 读回(serialize_roundtrip.cpp:57-64)
查看完整源文件 const LoadResult loaded = LoadAvtg(path, registry);
if (!loaded.group) {
std::fprintf(stderr, "LoadAvtg failed (%zu diagnostics)\n", loaded.diagnostics.size());
return 4;
}
const std::vector<std::string> before = ToolNames(*group);
const std::vector<std::string> after = ToolNames(*loaded.group);
std::printf("load: ok, tools=%zu\n", after.size());讲解:
LoadAvtg(path, registry)返回LoadResult:group为空即失败,诊断在diagnostics里,不要只看空指针。- 失败原因可能是
IoFailure、SchemaTooNew、未知type_id等;不同原因对应不同修复动作,诊断必须保留。 - 成功时得到一个新的
shared_ptr<ToolGroup>,与保存前的对象无关。
片段 5 · 顺序与组名断言(serialize_roundtrip.cpp:66-73)
查看完整源文件 if (before != after) {
std::fprintf(stderr, "tool list/order changed across round-trip\n");
return 5;
}
if (loaded.group->name() != group->name()) {
std::fprintf(stderr, "group name changed across round-trip\n");
return 5;
}讲解:
- 先比工具名序列(数量 + 顺序),再比组名:两者是 .avtg 往返最容易被悄悄破坏的部分。
- 失败统一用退出码 5,但错误信息区分「列表/顺序」与「组名」——排障时先看 stderr。
- 只比「工具数」会漏掉顺序错乱,而顺序决定执行序(
Run按tools[]自上而下)。
片段 6 · 收尾(serialize_roundtrip.cpp:81-83)
查看完整源文件 std::remove(path.c_str()); // demo artifact only
std::puts("serialize_roundtrip ok");
return 0;讲解:std::remove 删除示例自己写的临时包,避免污染工作目录;真实宿主应把 .avtg 存到自己的配方目录并纳入版本管理。
常见误区
- 「Load 不需要 registry」 —— .avtg 只存
type_id;没有注册表就无法重建工具,未知类型会失败 - 「保存成功 = 内容正确」 ——
ok只表示写入成功;字段保真由 TC-SER-* 与往返断言覆盖 - 「工具数对就行」 —— 顺序是执行契约,必须逐项比对
- 「能往返 = 算法可用」 —— 解析已实现;
passthrough的Run仍是基类桩
构建与运行
VYNTEK_BUILD_EXAMPLES=ON 才会构建示例;ninja-debug-all 预设已同时打开 tests 与 examples。二进制统一落在 build/ninja-debug-all/bin/。
Linux(本机 VPS,AGENTS.md「本机 VPS」小节)
cmake --preset ninja-debug-all
cmake --build --preset ninja-debug-all --target serialize_roundtrip
cd /tmp && /path/to/vyntek/build/ninja-debug-all/bin/serialize_roundtripWindows(PowerShell,AGENTS.md「构建」小节,仓库在 D:\Works\Vyntek)
cmake --preset ninja-debug-all
cmake --build --preset ninja-debug-all --target serialize_roundtrip
cd $env:TEMP; D:\Works\Vyntek\build\ninja-debug-all\bin\serialize_roundtrip.exe示例在当前工作目录写 serialize_roundtrip.avtg,成功读完即删除,所以建议在临时目录运行。
输出
本机实跑(Linux,2026-10-22,在空临时目录):
$ ./build/ninja-debug-all/bin/serialize_roundtrip
vyntek 0.1.0 (commit <build-sha>)
save: ok -> serialize_roundtrip.avtg
load: ok, tools=2
name: serialize-demo, tools: t1 t2
serialize_roundtrip ok
$ echo $?
0失败路径也实跑过:把当前目录设为不可写后运行,SaveAvtg 报 1 条诊断并以 3 退出:
$ cd /tmp/read-only-dir && ./serialize_roundtrip
SaveAvtg failed (1 diagnostics)
vyntek 0.1.0 (commit <build-sha>)
$ echo $?
3(stderr 与 stdout 混排顺序由缓冲决定,这里保留实测顺序。)
退出码
0—— 往返成功且工具列表、组名一致,打印serialize_roundtrip ok1——ToolGroup::Create失败2——registry.Create/AddTool失败3——SaveAvtg失败(SaveResult.ok == false;诊断见diagnostics)4——LoadAvtg失败(返回空组;诊断见diagnostics)5—— 工具数、顺序或组名不一致