From 7f088aef4907ae14306e84fea3dd42ceabfbd684 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 19 Jul 2026 23:49:26 +0800 Subject: [PATCH 01/25] =?UTF-8?q?feat(buildtools):=20=E6=96=B0=E5=A2=9E=20?= =?UTF-8?q?mcpp=20Provider=EF=BC=88C++26=EF=BC=89=EF=BC=8C=E6=9B=BF?= =?UTF-8?q?=E4=BB=A3=20xmake=20=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit d2x 改为通用练习框架后,构建工具由具体课程实现。本 Provider 用 C++26 + mcpp 编写——教 C++ 的仓库用 C++ 写自己的工具,且 mcpp 本就是 必需品,零新增运行时依赖。 发现方式刻意不引入声明文件:真相只有目录结构和练习文件自己的头部注释 (// d2x:cxxflags:)。rustlings PR #1355 的教训是 edition 同时写在 rustc 参数和 rust-project.json 里、两边漂移酿成 bug——任何独立声明文件都是 第二套真相源。 双 member 布局解决一个硬约束:dslings 的练习默认就编译不过,而 mcpp 的 build/run 都会先全量构建整个包,一个坏兄弟拖垮全部且零产物。于是 .d2x/build// 持全量 target 供 clangd 拿到完整 compile_commands.json, .d2x/build/_current/ 每次只写当前一题供 checker。清单写入前做内容比对, 不推进 mtime,mcpp 的快速路径得以保留。 练习源文件原地不动:main 用 ../ 逃逸出包根。 引导零脚本、Windows 安全:.d2x.json 指向 `mcpp run -q -p d2x/buildtools/mcpp --`,从仓库根执行无需 cd,首次自动 构建 Provider。 runner 在每次 spawn 前清 LD_LIBRARY_PATH:mcpp run 会把它指向自己私有的 glibc 并注入子进程,嵌套的 mcpp 被迫加载错配运行时后会在动态链接器里 段错误(冷启动稳定复现)。d2x 侧对同一问题早有相同处理。 tests/e2e.sh 断言每个参考答案通过、每个练习不通过——rustlings `dev check --require-solutions` 的等价物。实测 51/51 通过。 --- .d2x.json | 6 +- .gitignore | 9 +- d2x/buildtools/mcpp/mcpp.toml | 6 + d2x/buildtools/mcpp/src/discovery.cppm | 173 +++++++++++++++++++++++++ d2x/buildtools/mcpp/src/emit.cppm | 95 ++++++++++++++ d2x/buildtools/mcpp/src/main.cpp | 131 +++++++++++++++++++ d2x/buildtools/mcpp/src/manifest.cppm | 129 ++++++++++++++++++ d2x/buildtools/mcpp/src/runner.cppm | 109 ++++++++++++++++ d2x/buildtools/mcpp/tests/e2e.sh | 66 ++++++++++ mcpp.toml | 7 + 10 files changed, 727 insertions(+), 4 deletions(-) create mode 100644 d2x/buildtools/mcpp/mcpp.toml create mode 100644 d2x/buildtools/mcpp/src/discovery.cppm create mode 100644 d2x/buildtools/mcpp/src/emit.cppm create mode 100644 d2x/buildtools/mcpp/src/main.cpp create mode 100644 d2x/buildtools/mcpp/src/manifest.cppm create mode 100644 d2x/buildtools/mcpp/src/runner.cppm create mode 100755 d2x/buildtools/mcpp/tests/e2e.sh create mode 100644 mcpp.toml diff --git a/.d2x.json b/.d2x.json index 7424e6b..4ece6aa 100644 --- a/.d2x.json +++ b/.d2x.json @@ -1,7 +1,7 @@ { "version": "0.1.1", - "buildtools": "xmake d2x-buildtools", - "lang": "en", + "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", + "lang": "zh", "llm": { "api_key": "", "api_url": "https://api.deepseek.com/v1", @@ -9,4 +9,4 @@ "system_prompt": "" }, "ui_backend": "tui" -} \ No newline at end of file +} diff --git a/.gitignore b/.gitignore index 683495e..19e8da7 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,11 @@ llm.config.xlings media build .cache/ -dslings/compile_commands.json \ No newline at end of file +dslings/compile_commands.json +# d2x Provider 生成物(mcpp workspace 清单)与学习进度 +.d2x/build/ +.d2x/state.json + +# mcpp 构建产物 +target/ +compile_commands.json diff --git a/d2x/buildtools/mcpp/mcpp.toml b/d2x/buildtools/mcpp/mcpp.toml new file mode 100644 index 0000000..5aeb0fb --- /dev/null +++ b/d2x/buildtools/mcpp/mcpp.toml @@ -0,0 +1,6 @@ +[package] +name = "d2x-buildtools-mcpp" +version = "0.1.0" +description = "d2mcpp 的 d2x Provider:枚举练习、生成 mcpp 清单、构建/运行/判定" +license = "Apache-2.0" +standard = "c++26" diff --git a/d2x/buildtools/mcpp/src/discovery.cppm b/d2x/buildtools/mcpp/src/discovery.cppm new file mode 100644 index 0000000..a9e2d87 --- /dev/null +++ b/d2x/buildtools/mcpp/src/discovery.cppm @@ -0,0 +1,173 @@ +// 练习发现:目录约定 + 练习文件内的就近指令。 +// +// 刻意不引入独立的声明文件(exercises.toml 之类)。rustlings 最贵的一课 +// 是 PR #1355:edition 同时写在 rustc 参数和 rust-project.json 里,两边漂移 +// 酿成 bug。任何独立声明文件都是第二套真相源。这里的真相只有两处,且都 +// 无法漂移:目录结构,和练习文件自己的头部注释。 +export module d2x.provider.discovery; + +import std; + +namespace fs = std::filesystem; + +namespace d2x::discovery { + +export struct Exercise { + std::string id; // cpp11-00-auto-and-decltype-0 + int order{}; // 显式顺序,不依赖字典序 + std::string title; // auto and decltype (0) + std::string chapter; // cpp11/00-auto-and-decltype + std::string std_dir; // cpp11 —— 决定它属于哪个 member + fs::path file; // 绝对路径 + std::vector cxxflags; // 来自 // d2x:cxxflags: 指令 +}; + +// .d2x.json 里我们只关心 "lang"。写一个完整 JSON 解析器是杀鸡用牛刀, +// 但也不能假装解析——这里只做一件明确的事:取出 "lang" 的字符串值。 +export std::string read_lang(const fs::path& root) { + std::ifstream in(root / ".d2x.json"); + if (!in) return "en"; + std::string text((std::istreambuf_iterator(in)), {}); + + auto key = text.find("\"lang\""); + if (key == std::string::npos) return "en"; + auto colon = text.find(':', key); + if (colon == std::string::npos) return "en"; + auto open = text.find('"', colon); + if (open == std::string::npos) return "en"; + auto close = text.find('"', open + 1); + if (close == std::string::npos) return "en"; + return text.substr(open + 1, close - open - 1); +} + +// cpp11 -> 11。用于排序,让 cpp11 的练习永远排在 cpp14 之前。 +int std_rank(std::string_view dir) { + if (dir.starts_with("cpp")) { + int n = 0; + auto rest = dir.substr(3); + auto [_, ec] = std::from_chars(rest.data(), rest.data() + rest.size(), n); + if (ec == std::errc{}) return n; + } + return -1; // 根目录下的 hello-mcpp +} + +// 从练习文件头部读 `// d2x:cxxflags: -O0 -fno-elide-constructors`。 +// 只扫前 40 行——指令属于文件头的元信息区,不该藏在代码中间。 +std::vector read_cxxflags(const fs::path& file) { + std::ifstream in(file); + if (!in) return {}; + + constexpr std::string_view kMarker = "d2x:cxxflags:"; + std::string line; + for (int n = 0; n < 40 && std::getline(in, line); ++n) { + auto pos = line.find(kMarker); + if (pos == std::string::npos) continue; + + std::vector flags; + std::istringstream rest(line.substr(pos + kMarker.size())); + for (std::string flag; rest >> flag; ) flags.push_back(flag); + return flags; + } + return {}; +} + +// 00-auto-and-decltype-0 -> { chapter_no=0, topic="auto-and-decltype", index=0 } +// 03-trailing-return-type -> { chapter_no=3, topic="trailing-return-type", index=0 } +// +// 尾部的 -<数字> 是练习序号;没有就当 0。注意 d2mcpp 现有命名里 +// 03/04 两章确实缺了 -0 后缀,这个规则把它们归一化掉。 +struct NameParts { int chapter_no; std::string topic; int index; }; + +std::optional parse_stem(std::string_view stem) { + auto dash = stem.find('-'); + if (dash == std::string_view::npos) return std::nullopt; + + int chapter_no = 0; + auto head = stem.substr(0, dash); + if (std::from_chars(head.data(), head.data() + head.size(), chapter_no).ec != std::errc{}) + return std::nullopt; + + auto rest = stem.substr(dash + 1); + int index = 0; + auto last = rest.rfind('-'); + if (last != std::string_view::npos) { + auto tail = rest.substr(last + 1); + int parsed = 0; + auto [_, ec] = std::from_chars(tail.data(), tail.data() + tail.size(), parsed); + if (ec == std::errc{} && !tail.empty()) { + index = parsed; + rest = rest.substr(0, last); + } + } + return NameParts{chapter_no, std::string(rest), index}; +} + +std::string humanize(std::string_view topic) { + std::string out(topic); + std::ranges::replace(out, '-', ' '); + return out; +} + +// 扫描练习根目录。lang=zh 用 dslings/,lang=en 用 dslings/en/。 +export std::vector scan(const fs::path& repo_root, std::string_view lang) { + fs::path base = repo_root / "dslings"; + if (lang == "en") base /= "en"; + if (!fs::is_directory(base)) return {}; + + std::vector found; + + auto add = [&](const fs::path& file, const std::string& std_dir) { + auto stem = file.stem().string(); + auto parts = parse_stem(stem); + + Exercise ex; + ex.file = fs::absolute(file).lexically_normal(); + ex.std_dir = std_dir; + ex.cxxflags = read_cxxflags(file); + + if (parts) { + ex.id = std_dir.empty() ? stem : std::format("{}-{}", std_dir, stem); + ex.chapter = std_dir.empty() + ? std::format("{:02}-{}", parts->chapter_no, parts->topic) + : std::format("{}/{:02}-{}", std_dir, parts->chapter_no, parts->topic); + ex.title = std::format("{} ({})", humanize(parts->topic), parts->index); + ex.order = std_rank(std_dir) * 100'000 + parts->chapter_no * 100 + parts->index; + } else { + // 不符合 NN-topic 约定的(如根目录的 hello-mcpp.cpp) + ex.id = std_dir.empty() ? stem : std::format("{}-{}", std_dir, stem); + ex.chapter = std_dir.empty() ? "intro" : std_dir; + ex.title = humanize(stem); + ex.order = std_rank(std_dir) * 100'000; + } + found.push_back(std::move(ex)); + }; + + // 根目录下的入门练习(hello-mcpp.cpp) + for (const auto& entry : fs::directory_iterator(base)) { + if (entry.is_regular_file() && entry.path().extension() == ".cpp") add(entry.path(), ""); + } + + // 按标准分的子目录 + std::vector std_dirs; + for (const auto& entry : fs::directory_iterator(base)) { + if (entry.is_directory() && entry.path().filename().string().starts_with("cpp")) + std_dirs.push_back(entry.path()); + } + std::ranges::sort(std_dirs); + + for (const auto& dir : std_dirs) { + auto std_dir = dir.filename().string(); + std::vector files; + for (const auto& entry : fs::directory_iterator(dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".cpp") + files.push_back(entry.path()); + } + std::ranges::sort(files); + for (const auto& f : files) add(f, std_dir); + } + + std::ranges::sort(found, {}, &Exercise::order); + return found; +} + +} // namespace d2x::discovery diff --git a/d2x/buildtools/mcpp/src/emit.cppm b/d2x/buildtools/mcpp/src/emit.cppm new file mode 100644 index 0000000..63a6bbb --- /dev/null +++ b/d2x/buildtools/mcpp/src/emit.cppm @@ -0,0 +1,95 @@ +// NDJSON 事件输出。 +// +// 协议要求每一行都是一个完整的 JSON 对象,解析不了的行由 d2x 忽略—— +// 这正好吞掉 `mcpp run` 的前导空行和可能混入的编译输出,所以这里不需要 +// 任何哨兵前缀。唯一的硬要求是:一个事件绝不能跨行。 +module; + +// stdout / fputs / fflush 是宏与 C 运行时符号,import std 不提供, +// 必须走全局模块片段。 +#include + +export module d2x.provider.emit; + +import std; + +namespace d2x::emit { + +// JSON 字符串转义。练习输出里有 ANSI 转义序列、换行、引号,必须全部处理, +// 否则一个彩色编译错误就能把整行 JSON 打碎。 +export std::string escape(std::string_view s) { + std::string out; + out.reserve(s.size() + s.size() / 8); + for (unsigned char c : s) { + switch (c) { + case '"': out += "\\\""; break; + case '\\': out += "\\\\"; break; + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + case '\b': out += "\\b"; break; + case '\f': out += "\\f"; break; + default: + // ANSI 的 ESC(0x1b) 落在这里,按  编码 + if (c < 0x20) out += std::format("\\u{:04x}", static_cast(c)); + else out += static_cast(c); + } + } + return out; +} + +export std::string str(std::string_view v) { + return std::format("\"{}\"", escape(v)); +} + +export std::string array(const std::vector& items) { + std::string out = "["; + for (std::size_t i = 0; i < items.size(); ++i) { + if (i) out += ","; + out += items[i]; + } + out += "]"; + return out; +} + +// 一行一个事件,立刻 flush——d2x 是逐行读的,缓冲会让「实时看到编译输出」失效。 +export void line(std::string_view json_object) { + std::fputs(std::string(json_object).c_str(), stdout); + std::fputc('\n', stdout); + std::fflush(stdout); +} + +export void describe(std::string_view name, int protocol) { + line(std::format(R"({{"event":"describe","protocol":{},"name":{}}})", + protocol, str(name))); +} + +export void exercise(std::string_view id, int order, std::string_view title, + std::string_view chapter, const std::vector& files) { + std::vector quoted; + quoted.reserve(files.size()); + for (const auto& f : files) quoted.push_back(str(f)); + + line(std::format(R"({{"event":"exercise","id":{},"order":{},"title":{},"chapter":{},"files":{}}})", + str(id), order, str(title), str(chapter), array(quoted))); +} + +export void stage(std::string_view name) { + line(std::format(R"({{"event":"stage","name":{}}})", str(name))); +} + +export void output(std::string_view chunk) { + if (chunk.empty()) return; + line(std::format(R"({{"event":"output","chunk":{}}})", str(chunk))); +} + +export void verdict(std::string_view outcome, std::string_view stage_name, int exit_code) { + line(std::format(R"({{"event":"verdict","outcome":{},"stage":{},"exit_code":{},"diagnostics":[]}})", + str(outcome), str(stage_name), exit_code)); +} + +export void error(std::string_view message) { + line(std::format(R"({{"event":"error","message":{}}})", str(message))); +} + +} // namespace d2x::emit diff --git a/d2x/buildtools/mcpp/src/main.cpp b/d2x/buildtools/mcpp/src/main.cpp new file mode 100644 index 0000000..80be0f8 --- /dev/null +++ b/d2x/buildtools/mcpp/src/main.cpp @@ -0,0 +1,131 @@ +// d2mcpp 的 d2x Provider。 +// +// describe 自我描述 +// exercises 枚举练习(有序) +// check 验证一道练习 +// +// 全部输出为 NDJSON 事件流,一行一个事件。d2x 忽略解析不了的行, +// 所以 `mcpp run` 的前导空行之类的噪声不会破坏协议。 +// +// 用法(由 .d2x.json 的 buildtools 字段驱动,从仓库根执行,无需 cd): +// mcpp run -q -p d2x/buildtools/mcpp -- check cpp11-00-auto-and-decltype-0 + +import std; + +import d2x.provider.emit; +import d2x.provider.discovery; +import d2x.provider.manifest; +import d2x.provider.runner; + +namespace fs = std::filesystem; + +namespace { + +constexpr int kProtocolVersion = 1; + +// 向上找到仓库根:认 .d2x.json + dslings/ 同时存在。 +// 不能依赖可执行文件位置——它在 target///bin/ 里,层数不固定。 +std::optional find_repo_root() { + auto dir = fs::current_path(); + for (int depth = 0; depth < 12; ++depth) { + if (fs::exists(dir / ".d2x.json") && fs::is_directory(dir / "dslings")) + return dir; + if (!dir.has_parent_path() || dir.parent_path() == dir) break; + dir = dir.parent_path(); + } + return std::nullopt; +} + +int cmd_describe() { + d2x::emit::describe("mcpp", kProtocolVersion); + return 0; +} + +int cmd_exercises(const fs::path& root) { + auto lang = d2x::discovery::read_lang(root); + auto all = d2x::discovery::scan(root, lang); + if (all.empty()) { + d2x::emit::error(std::format("no exercises found under {}/dslings (lang={})", + root.string(), lang)); + return 1; + } + + // 顺手刷新全量清单:clangd 依赖它拿到每个练习的编译参数。 + // 这里只写文件不构建——构建是 check 的事,枚举必须快。 + d2x::manifest::write_full(root, all); + + for (const auto& ex : all) { + std::vector files{ex.file.string()}; + d2x::emit::exercise(ex.id, ex.order, ex.title, ex.chapter, files); + } + return 0; +} + +int cmd_check(const fs::path& root, std::string_view id) { + auto lang = d2x::discovery::read_lang(root); + auto all = d2x::discovery::scan(root, lang); + + auto it = std::ranges::find(all, id, &d2x::discovery::Exercise::id); + if (it == all.end()) { + d2x::emit::error(std::format("unknown exercise: {}", id)); + return 1; + } + + // 先保证整个 workspace 存在。check 可能是全新仓库上的第一条命令 + // (学员直接跑 d2x checker),此时根清单还没生成,mcpp 会以退出码 2 + // 报 "workspace member not found"。write_* 都是内容比对后才落盘, + // 重复调用不会推进 mtime、不会让 mcpp 的快速路径失效。 + d2x::manifest::write_full(root, all); + + // 再把这一题单独写进 _current,其余练习(多半还编译不过)不参与构建 + d2x::manifest::write_current(root, *it); + d2x::runner::enter(d2x::manifest::build_dir(root)); + + d2x::emit::stage("compile"); + auto compiled = d2x::runner::build_current(); + d2x::emit::output(compiled.output); + if (compiled.exit_code != 0) { + d2x::emit::verdict("fail", "compile", compiled.exit_code); + return 0; // 练习没通过是正常业务路径,不是 Provider 出错 + } + + d2x::emit::stage("run"); + auto ran = d2x::runner::run_current(); + d2x::emit::output(ran.output); + + auto outcome = d2x::runner::judge_run(ran.exit_code, ran.output); + d2x::emit::verdict(d2x::runner::to_string(outcome), "run", ran.exit_code); + return 0; +} + +} // namespace + +int main(int argc, char** argv) { + std::vector args(argv + 1, argv + argc); + + if (args.empty()) { + d2x::emit::error("usage: describe | exercises | check "); + return 2; + } + + if (args[0] == "describe") return cmd_describe(); + + auto root = find_repo_root(); + if (!root) { + d2x::emit::error("repo root not found (looking for .d2x.json + dslings/)"); + return 2; + } + + if (args[0] == "exercises") return cmd_exercises(*root); + + if (args[0] == "check") { + if (args.size() < 2) { + d2x::emit::error("check requires an exercise id"); + return 2; + } + return cmd_check(*root, args[1]); + } + + d2x::emit::error(std::format("unknown command: {}", args[0])); + return 2; +} diff --git a/d2x/buildtools/mcpp/src/manifest.cppm b/d2x/buildtools/mcpp/src/manifest.cppm new file mode 100644 index 0000000..bcf5843 --- /dev/null +++ b/d2x/buildtools/mcpp/src/manifest.cppm @@ -0,0 +1,129 @@ +// 生成 mcpp workspace 清单。 +// +// 双 member 布局,解决一个硬约束:dslings 的练习默认就编译不过,而 mcpp 的 +// build/run 都会先全量构建整个包——一个坏兄弟拖垮全部且零产物。所以: +// +// .d2x/build// 全量 target,供 clangd 拿到完整 compile_commands.json +// .d2x/build/_current/ 每次只写当前一题,checker 只构建它 +// +// 生成物全部在 .d2x/build/ 下,练习源文件原地不动(main 用 ../ 逃逸出包根)。 +export module d2x.provider.manifest; + +import std; +import d2x.provider.discovery; + +namespace fs = std::filesystem; + +namespace d2x::manifest { + +using discovery::Exercise; + +// 所有练习统一按 c++23 编译。mcpp 目前硬拒 c++11/14/17/20 +// (src/manifest/types.cppm 的白名单),本方案选择不改 mcpp 上游。 +constexpr std::string_view kStandard = "c++23"; + +// 生成的 member 位于 .d2x/build//,回到仓库根要退三层。 +constexpr std::string_view kToRoot = "../../.."; + +export fs::path build_dir(const fs::path& repo_root) { + return repo_root / ".d2x" / "build"; +} + +std::string member_name(const Exercise& ex) { + return ex.std_dir.empty() ? "intro" : ex.std_dir; +} + +// 把绝对路径转成相对 member 目录的形式。mcpp 的 main 接受 ../ 逃逸。 +std::string relative_to_member(const fs::path& repo_root, const fs::path& file) { + auto rel = fs::relative(file, repo_root).generic_string(); + return std::format("{}/{}", kToRoot, rel); +} + +void write_package_header(std::ostream& out, std::string_view name) { + std::println(out, "# 由 d2x-buildtools-mcpp 生成,请勿手工编辑。"); + std::println(out, "[package]"); + std::println(out, "name = \"{}\"", name); + std::println(out, "version = \"0.1.0\""); + std::println(out, "standard = \"{}\"", kStandard); + std::println(out, ""); + std::println(out, "[build]"); + // 显式清空源码 glob:练习不是这个包的「源码」,它们只是各自 target 的入口。 + std::println(out, "sources = []"); + // 仓库根进搜索路径——155 个练习全部 #include + std::println(out, "include_dirs = [\"{}\"]", kToRoot); +} + +void write_target(std::ostream& out, const fs::path& repo_root, const Exercise& ex) { + std::println(out, ""); + std::println(out, "[targets.{}]", ex.id); + std::println(out, "kind = \"bin\""); + std::println(out, "main = \"{}\"", relative_to_member(repo_root, ex.file)); + if (!ex.cxxflags.empty()) { + std::string list; + for (std::size_t i = 0; i < ex.cxxflags.size(); ++i) { + if (i) list += ", "; + list += std::format("\"{}\"", ex.cxxflags[i]); + } + std::println(out, "cxxflags = [{}]", list); + } +} + +// 只有内容真的变了才落盘。避免无谓地推进 mtime,让 mcpp 的快速路径失效。 +bool write_if_changed(const fs::path& path, std::string_view content) { + if (fs::exists(path)) { + std::ifstream in(path); + std::string existing((std::istreambuf_iterator(in)), {}); + if (existing == content) return false; + } + fs::create_directories(path.parent_path()); + std::ofstream out(path); + out << content; + return true; +} + +// 全量清单:每个 C++ 标准一个 member,持有该标准下的全部练习。 +// 供 clangd —— 即使大多数练习编译不过,mcpp 仍会生成完整的 +// compile_commands.json(已实测),所以 IDE 对每个练习都有正确的编译参数。 +export void write_full(const fs::path& repo_root, const std::vector& all) { + auto root = build_dir(repo_root); + + std::map> by_member; + for (const auto& ex : all) by_member[member_name(ex)].push_back(&ex); + + std::vector members; + for (const auto& [name, list] : by_member) { + std::ostringstream buf; + write_package_header(buf, name); + for (const auto* ex : list) write_target(buf, repo_root, *ex); + write_if_changed(root / name / "mcpp.toml", buf.str()); + members.push_back(name); + } + + // _current 也是这个 workspace 的成员,占位清单先写空壳 + members.push_back("_current"); + write_if_changed(root / "_current" / "mcpp.toml", + [&] { std::ostringstream b; write_package_header(b, "_current"); return b.str(); }()); + + std::ostringstream ws; + std::println(ws, "# 由 d2x-buildtools-mcpp 生成,请勿手工编辑。"); + std::println(ws, "[workspace]"); + std::print(ws, "members = ["); + for (std::size_t i = 0; i < members.size(); ++i) { + if (i) std::print(ws, ", "); + std::print(ws, "\"{}\"", members[i]); + } + std::println(ws, "]"); + write_if_changed(root / "mcpp.toml", ws.str()); +} + +// 单题清单:checker 每次 check 前重写它,只含要验证的那一题。 +// 实测改写 target 集合不会让 fingerprint 目录爆炸(始终只有 1 个), +// 切题 0.118s、切回 0.018s。 +export void write_current(const fs::path& repo_root, const Exercise& ex) { + std::ostringstream buf; + write_package_header(buf, "_current"); + write_target(buf, repo_root, ex); + write_if_changed(build_dir(repo_root) / "_current" / "mcpp.toml", buf.str()); +} + +} // namespace d2x::manifest diff --git a/d2x/buildtools/mcpp/src/runner.cppm b/d2x/buildtools/mcpp/src/runner.cppm new file mode 100644 index 0000000..7bcb3ef --- /dev/null +++ b/d2x/buildtools/mcpp/src/runner.cppm @@ -0,0 +1,109 @@ +// 调用 mcpp 构建/运行单个练习,并判定结果。 +module; + +// popen/pclose 是 POSIX,WIFEXITED 等是宏——都不在 import std 的范围内。 +#include +#ifndef _WIN32 +# include +# include // unsetenv +#endif + +export module d2x.provider.runner; + +import std; + +namespace fs = std::filesystem; + +namespace d2x::runner { + +export struct Captured { + int exit_code{}; + std::string output; +}; + +// 合并 stderr —— 编译错误和练习的运行输出都要原样呈现给学员。 +// 这些内容最终进 JSON 的 output 字段,不会污染协议。 +// +// 必须先清掉 LD_LIBRARY_PATH:本 Provider 由 `mcpp run` 启动时,mcpp 会把 +// LD_LIBRARY_PATH 指向它私有的 glibc(~/.mcpp/registry/.../xim-x-glibc/*/lib64) +// 并注入子进程。我们接着去 spawn 嵌套的 mcpp —— 那是另一个二进制,被迫加载 +// 错配的 glibc 后会在动态链接器里段错误。清空后 mcpp 会为它自己的子进程重新 +// 设置正确的值,所以练习程序照常能跑。 +// +// d2x 侧对同一问题有相同的处理(platform.cppm 的 run_command_capture)。 +export Captured capture(const std::string& cmd) { + Captured result; +#ifndef _WIN32 + ::unsetenv("LD_LIBRARY_PATH"); +#endif + std::string full = cmd + " 2>&1"; + +#ifdef _WIN32 + FILE* pipe = ::_popen(full.c_str(), "r"); +#else + FILE* pipe = ::popen(full.c_str(), "r"); +#endif + if (!pipe) return {127, std::format("failed to spawn: {}", cmd)}; + + char buf[4096]; + while (std::fgets(buf, sizeof(buf), pipe)) result.output += buf; + +#ifdef _WIN32 + int status = ::_pclose(pipe); + result.exit_code = status; +#else + int status = ::pclose(pipe); + // pclose 回的是 wait status,转成真实退出码,否则 exit 1 会变成 256 + result.exit_code = (status == -1) ? 127 + : WIFEXITED(status) ? WEXITSTATUS(status) + : WIFSIGNALED(status) ? 128 + WTERMSIG(status) + : status; +#endif + return result; +} + +// d2mcpp 的练习断言协议,定义在 d2x/cpp/common.hpp: +// d2x_assert / d2x_assert_eq 失败时打印 ❌ +// D2X_WAIT 宏打印一句提示,学员删掉它才算真正完成 +// +// 这套判定是「课程约定」,不是「构建工具行为」——所以它属于 Provider, +// 而不是 d2x 框架。换一门 Rust 课程,这里会完全不同。 +export constexpr std::string_view kFailMark = "\xE2\x9D\x8C"; // U+274C ❌ +export constexpr std::string_view kWaitMark = "D2X_WAIT"; + +export enum class Outcome { Pass, Fail, Blocked }; + +export std::string_view to_string(Outcome o) { + switch (o) { + case Outcome::Pass: return "pass"; + case Outcome::Fail: return "fail"; + case Outcome::Blocked: return "blocked"; + } + return "fail"; +} + +// 运行阶段的输出判定。退出码为 0 只是必要条件: +// 出现 ❌ → 断言没过,Fail +// 出现 D2X_WAIT → 答案已对但路障还在,Blocked(既非失败也不该前进) +export Outcome judge_run(int exit_code, std::string_view output) { + if (exit_code != 0) return Outcome::Fail; + if (output.find(kFailMark) != std::string_view::npos) return Outcome::Fail; + if (output.find(kWaitMark) != std::string_view::npos) return Outcome::Blocked; + return Outcome::Pass; +} + +// mcpp 需要在 workspace 根(.d2x/build/)下执行。Provider 是独立进程, +// 直接 chdir 比拼 `cd X && ...` 更干净,也避开 Windows 的 shell 差异。 +export void enter(const fs::path& workspace_root) { + fs::current_path(workspace_root); +} + +export Captured build_current() { + return capture("mcpp build -q -p _current"); +} + +export Captured run_current() { + return capture("mcpp run -q -p _current"); +} + +} // namespace d2x::runner diff --git a/d2x/buildtools/mcpp/tests/e2e.sh b/d2x/buildtools/mcpp/tests/e2e.sh new file mode 100755 index 0000000..f0ef46d --- /dev/null +++ b/d2x/buildtools/mcpp/tests/e2e.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Provider 端到端测试。 +# +# 断言两件事,缺一不可: +# 1. 每个练习「未完成时」不通过 —— 否则学员会被直接跳过,练习形同虚设 +# 2. 每个参考答案「放进去后」通过 —— 否则参考答案本身是错的 +# +# 这是 rustlings `cargo dev check --require-solutions` 的等价物。d2mcpp 现有的 +# dslings-ref-ci.yml 因为 solutions/ 在 xmake.lua 里被注释掉,实际校验零个目标。 +set -uo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)" +cd "$REPO_ROOT" + +PROVIDER=(mcpp run -q -p d2x/buildtools/mcpp --) + +# 任何退出路径都还原被改动的练习文件,绝不把仓库留在脏状态 +restore() { git checkout -- dslings/ 2>/dev/null || true; } +trap restore EXIT + +outcome_of() { # $1 = exercise id + "${PROVIDER[@]}" check "$1" 2>&1 \ + | grep -o '"outcome":"[a-z]*"' | head -1 | cut -d'"' -f4 +} + +echo "==> 枚举练习" +mapfile -t LINES < <("${PROVIDER[@]}" exercises 2>&1 | grep '"event":"exercise"') +if [ "${#LINES[@]}" -eq 0 ]; then + echo "FAIL: Provider 没有枚举出任何练习"; exit 1 +fi +echo " 共 ${#LINES[@]} 个" + +pass=0; fail=0; skipped=0 + +for line in "${LINES[@]}"; do + id=$(printf '%s' "$line" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) + file=$(printf '%s' "$line" | grep -o '"files":\["[^"]*"' | head -1 | cut -d'"' -f4) + + rel="${file#"$REPO_ROOT"/}" # dslings/cpp11/xx.cpp + sol="solutions/${rel#dslings/}" # solutions/cpp11/xx.cpp + sol="${sol#en/}" # en 目录共用同一份参考答案 + + # 1) 未完成态必须不通过 + got=$(outcome_of "$id") + if [ "$got" = "pass" ]; then + echo "FAIL [$id] 练习未完成却判定通过"; fail=$((fail+1)); continue + fi + + # 2) 参考答案必须通过 + if [ ! -f "$sol" ]; then + echo "SKIP [$id] 无参考答案 ($sol)"; skipped=$((skipped+1)); continue + fi + cp "$sol" "$file" + got=$(outcome_of "$id") + git checkout -- "$file" + + if [ "$got" = "pass" ]; then + pass=$((pass+1)) + else + echo "FAIL [$id] 参考答案未通过 (outcome=$got)"; fail=$((fail+1)) + fi +done + +echo +echo "==> 参考答案通过 $pass · 失败 $fail · 跳过 $skipped" +[ "$fail" -eq 0 ] diff --git a/mcpp.toml b/mcpp.toml new file mode 100644 index 0000000..4edacd4 --- /dev/null +++ b/mcpp.toml @@ -0,0 +1,7 @@ +# d2mcpp 根 workspace。 +# +# 这里只登记「提交进仓库」的包。生成物(.d2x/build/ 下按 C++ 标准分的 +# member、以及只含当前一题的 _current)自成一个独立 workspace,由 +# Provider 生成——不放进这里,避免「根清单引用尚未生成的成员」的循环。 +[workspace] +members = ["d2x/buildtools/mcpp"] From fc2f3a645c4e1fe0a55f90c8e1ef005900877693 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Mon, 20 Jul 2026 00:01:20 +0800 Subject: [PATCH 02/25] =?UTF-8?q?chore:=20=E5=BD=BB=E5=BA=95=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=20xmake=EF=BC=8C=E6=9E=84=E5=BB=BA=E9=93=BE=E8=B7=AF?= =?UTF-8?q?=E5=85=A8=E9=83=A8=E8=B5=B0=20mcpp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit d2x 改为通用练习框架、构建由 mcpp Provider 承担后,xmake 已无调用方。 删除 11 个 xmake.lua(根 / dslings / dslings/en / solutions 各级)、 d2x/buildtools/xmake/ 插件、d2x/cpp/common.lua(xmake 侧的语言探测辅助, 早已被 main.lua 读 .d2x.json 取代)、以及只写着 TODO 的 cmake 适配器占位。 d2x/buildtools/README.md 一并删除——它描述的 list/build/run 契约已被 Provider 协议(describe/exercises/check)取代,留着会误导。 .xlings.json 去掉 xmake 3.0.7 的 pin(该版本已从 registry 移除,是 d2x CI 里那条 "用真 xmake 而非 xlings shim" workaround 的根因),同时去掉 gcc 与 mingw-w64 —— mcpp 自带工具链沙箱,Windows 侧由它的 MinGW/MSVC 覆盖。 CI 重写:旧流水线只挑 `-ref` 结尾的 xmake target,而 solutions/ 在 xmake.lua 里被注释掉,grep 返回空、循环全跳过、job 退出 0 —— 实际校验零个 目标,且第 60 行的 wc -l 对空串仍打印 "Found 1 reference targets" 把空转 掩盖了。新流水线跑 e2e.sh,断言每个参考答案通过、每个练习不通过,走的是 `d2x checker` 内部同一条 Provider 路径。 撰稿 skill 同步更新:练习不再需要注册,放进 dslings// 即可被目录约定 发现;per-exercise 编译选项改为练习文件头部的 // d2x:cxxflags: 指令。 复验:Provider 枚举 52 题、端到端 51/51 参考答案通过 0 失败、d2x checker 全链路正常。 --- .../2026-07-19-mcpp-replace-xmake-research.md | 416 ++++++++++++++++++ .agents/skills/d2mcpp-authoring/SKILL.md | 14 +- .../d2mcpp-authoring/assets/chapter.en.md | 2 +- .../d2mcpp-authoring/assets/chapter.zh.md | 2 +- .../d2mcpp-authoring/references/anatomy.md | 77 ++-- .github/workflows/dslings-ref-ci.yml | 101 ++--- .gitignore | 2 - .vscode/settings.json | 3 +- .xlings.json | 10 +- book/en/src/base/chapter_1.md | 2 +- book/src/base/chapter_1.md | 4 +- d2x/buildtools/README.md | 14 - d2x/buildtools/cmake/README.md | 1 - d2x/buildtools/xmake/main.lua | 67 --- d2x/buildtools/xmake/xmake.lua | 14 - d2x/cpp/common.lua | 29 -- dslings/cpp11/xmake.lua | 195 -------- dslings/cpp14/xmake.lua | 11 - dslings/en/cpp11/xmake.lua | 192 -------- dslings/en/cpp14/xmake.lua | 11 - dslings/en/xmake.lua | 6 - dslings/xmake.lua | 23 - solutions/cpp11/xmake.lua | 198 --------- solutions/cpp14/xmake.lua | 11 - solutions/xmake.lua | 11 - xmake.lua | 6 - 26 files changed, 505 insertions(+), 917 deletions(-) create mode 100644 .agents/docs/2026-07-19-mcpp-replace-xmake-research.md delete mode 100644 d2x/buildtools/README.md delete mode 100644 d2x/buildtools/cmake/README.md delete mode 100644 d2x/buildtools/xmake/main.lua delete mode 100644 d2x/buildtools/xmake/xmake.lua delete mode 100644 d2x/cpp/common.lua delete mode 100644 dslings/cpp11/xmake.lua delete mode 100644 dslings/cpp14/xmake.lua delete mode 100644 dslings/en/cpp11/xmake.lua delete mode 100644 dslings/en/cpp14/xmake.lua delete mode 100644 dslings/en/xmake.lua delete mode 100644 dslings/xmake.lua delete mode 100644 solutions/cpp11/xmake.lua delete mode 100644 solutions/cpp14/xmake.lua delete mode 100644 solutions/xmake.lua delete mode 100644 xmake.lua diff --git a/.agents/docs/2026-07-19-mcpp-replace-xmake-research.md b/.agents/docs/2026-07-19-mcpp-replace-xmake-research.md new file mode 100644 index 0000000..3438368 --- /dev/null +++ b/.agents/docs/2026-07-19-mcpp-replace-xmake-research.md @@ -0,0 +1,416 @@ +# 调研报告:用 mcpp 替代 xmake 支撑 d2mcpp 全项目 + +- 日期:2026-07-19 +- 范围:d2mcpp(课程仓库)、d2x(课程驱动 CLI)、mcpp 0.0.99(构建工具) +- 结论一句话:**当前不可整体替换;但经 §4 的 rustlings/cargo 对照修订后,真正的阻塞只剩 1 个——mcpp 的 `[package].standard` 不接受 c++11/14/17/20。推荐"按标准分 workspace member(5~10 个)+ 每 member 内多 target + ninja 逐 target 隔离"。** +- ⚠️ 阅读顺序:§1-§3 是初版结论(单包 155 target、3 个上游需求);**§4 基于 rustlings/cargo 的横向对照做了架构修订,以 §4.4 为准。** + +--- + +## 1. 现状:xmake 在 d2mcpp 里承担什么 + +### 1.1 集成缝在哪 + +d2mcpp 不直接暴露 xmake 给学习者。三个 README 里 `xmake` 出现 **0 次**,学习者的唯一入口是 `d2x checker`。xmake 通过一层薄适配器接入: + +``` +.d2x.json: "buildtools": "xmake d2x-buildtools" + ↓ +xmake.lua:1 add_plugindirs("d2x/buildtools") + ↓ +d2x/buildtools/xmake/{xmake.lua, main.lua} ← 实现 init / list / build / run +``` + +契约写在 `d2x/buildtools/README.md`,只有 4 个命令。d2x 侧的实现(`d2x/src/buildtools.cppm:28-42`)是裸字符串拼接 + `popen`: + +```cpp +auto init() const { return platform::run_command_capture(bin + " init"); } +auto list() const { return platform::run_command_capture(bin + " list"); } +auto build(const std::string& t) const { return platform::run_command_capture(bin + " build " + t); } +auto run (const std::string& t) const { return platform::run_command_capture(bin + " run " + t); } +``` + +`bin` 完全来自配置,**不限定是 xmake**——写成 `./d2x/buildtools/mcpp/adapter` 也能跑。这是好消息:适配器不需要 mcpp 提供插件机制。 + +### 1.2 契约的精确要求(迁移必须逐条满足) + +| 项 | 要求 | 出处 | +|---|---|---| +| `list` 输出 | 每行 `@ <绝对路径>, <绝对路径>...`,`@` 必须**恰好出现一次**,否则该行被**静默丢弃** | `d2x/src/buildtools.cppm:76-100` | +| 路径 | 事实上必须绝对(`utils::read_file_to_string` 会抛异常,编辑器打开、mtime 轮询都按 CWD 解析) | `d2x/src/utils.cppm:66` 的 TODO | +| 排序 | 存进 `std::map`,即**按 target 名字典序**决定练习顺序 | `buildtools.cppm:21` | +| 退出码 | `build`/`run` 必须 `exit == 0` 才算通过;不解析编译器错误文本 | `d2x/src/checker.cppm:18-26` | +| 通过判定 | build==0 **且** run==0 **且** 输出无 `❌` **且** 无 `D2X_WAIT` | `checker.cppm:80-127` | +| 环境 | d2x 调用前会**清空 `LD_LIBRARY_PATH`**,且从不 `chdir` | `d2x/src/platform.cppm:51-54` | +| 噪声容忍 | 不匹配的行被忽略;ANSI 转义会被 trim 掉 | `d2x/src/utils.cppm:32-56` | + +`init` 会在 `load_buildtools()` 里被**无条件调用一次且忽略返回值**(`buildtools.cppm:109`),mcpp 适配器可以让它做真正的初始化工作而不怕失败传播。 + +### 1.3 target 全景(迁移工作量的真实分母) + +11 个 `xmake.lua`,153 处 target 声明。实际参与构建的只有两套之一(zh / en 互斥,同名): + +| lang | target 数 | 组成 | +|---|---|---| +| `en`(`option` 默认,且是提交进仓库的 `.d2x.json` 值) | 51 | 1 hello + 48 en/cpp11 + 2 en/cpp14 | +| `zh` | 52 | 1 hello + 49 cpp11 + 2 cpp14 | + +- **`dslings/cpp17`、`cpp20`、`cpp23` 全是 6 字节的 README 占位符**,0 个 `.cpp`,没有任何 `includes()` 引用。课程实际只写到 cpp11 + 2 个 cpp14 练习。 +- **`solutions/` 51 个 `-ref` target 完全没被构建**——`xmake.lua:6` 的 `includes("solutions/xmake.lua")` 被注释掉了(commit `307dc8a`)。 +- **每个 target 都是"一个 `.cpp`、无依赖"**:`add_files` 带多文件/通配符 = 0 处,`add_deps`/`add_packages`/`add_defines`/`add_rules`/自定义 hook = 0 处。这是迁移最有利的事实。 + +需要特殊处理的只有 **32 个 target**(占 152 的 21%),且模式极少: + +| 特性 | 出现次数 | 涉及 target | +|---|---|---| +| `set_kind("binary")`(显式,语义上是默认值,可直接丢弃) | 23 | 各 dir 前几个 | +| `add_cxxflags("-fno-elide-constructors")` + `set_optimize("none")` | 3 | `04-rvalue-references` ×{zh,en,sol} | +| `add_cxxflags("-Wpedantic -Werror")` | 3 | `07-constexpr-0` ×{zh,en,sol} | +| 单 target `set_languages("c++17")` | 3 | `08-literal-type-0` ×{zh,en,sol} | +| 单 target `set_languages("cxx11")` | 2 | `00-0-hello-mcpp` ×{zh,en} | +| 文件级 `set_languages` | 7 个文件 | — | +| Windows 分支(`set_encodings` / `set_toolchains("gcc")` / `add_ldflags("-static")`) | 1 处 | `dslings/xmake.lua:1-5` | +| `add_includedirs(".")` | 1 处(根) | **155/155 个 `.cpp` 都靠它** `#include ` | + +--- + +## 2. mcpp 能力核对(含实测) + +以下带 ✅/❌ 的结论**均为本机 mcpp 0.0.99 实测**,非仅读文档。 + +### 2.1 能做到的 + +**✅ 单包多可执行 target。** `[targets.] kind="bin" main="..."`,每个 target 的 `main` 对其他 target 是排除的(`src/build/plan.cppm:882-895`),155 个各带 `main()` 的文件不会 `multiple definition`。实测 3 target 一次构建出 3 个二进制。 + +**✅ 扁平布局,零文件搬迁。** 不需要 `src/` 目录,`main` 可指向包根下任意相对路径: + +```toml +[build] +sources = ["*.cpp"] + +[targets.ex0] +kind = "bin" +main = "00-auto-and-decltype-0.cpp" +``` +实测通过。dslings 现有目录结构可以原地不动。 + +**✅ 每 target 编译选项。** `cxxflags` / `cflags` / `defines` 可用。文档警告"只作用于该 target 的入口 TU"——但对"一个练习 = 一个 `.cpp`"的场景,入口 TU **就是**整个程序,所以 `-fno-elide-constructors`、`-Wpedantic -Werror`、`-O0` 全部覆盖到位。这三个特殊 target 无损迁移。 + +**✅ 非模块的普通 C++ 能构建。** `import std` 的 BMI 预构建是条件触发的(`src/build/prepare.cppm:3176-3217`),155 个无 `import` 的普通 `.cpp` 走快速路径。 + +**✅ 退出码符合 d2x 契约。** 实测:正常构建 0 / 编译失败 1 / 成员不存在 2 / `run` 0。 + +**✅ 速度显著优于 xmake。** 同机实测: + +| 操作 | xmake | mcpp | +|---|---|---| +| 列出 target | **2.06 s** | — (无此命令) | +| 单练习构建(冷) | 0.63 s | **0.12 s** | +| 单练习构建(暖,无改动) | 0.61 s | **0.014 s** | + +xmake 每次都要付 Lua VM 启动成本,暖构建几乎不省。mcpp 暖路径 14 ms —— 对 `d2x checker` 这种"改一行、立刻重编"的循环,这是量级差异。 + +**✅ 自带工具链沙箱。** 这正是 d2x 自己迁到 mcpp 时解决 macOS SDK 问题的关键。同时能一并解掉 CI 里那条已知的坑:d2x 的 `checker-smoke` job 注释写着 *"d2mcpp 的 .xlings.json 钉了 xmake 3.0.7,它已从 registry 移除,所以 xlings 的 xmake shim 会解析失败"*(`d2x/.github/workflows/ci.yml:124-134`)。迁到 mcpp 后这个 workaround 可以整个删掉。 + +**✅ Windows 覆盖更好。** MinGW(`x86_64-windows-gnu`)与 MSVC(`msvc@system`,0.0.90 起)都支持,且默认 `static_stdlib = true`——正好对上 d2x 会清空 `LD_LIBRARY_PATH` 的行为,比现在手写 `add_ldflags("-static")` 更稳。 + +### 2.2 阻塞点 + +**🚨 阻塞 1:C++ 标准下限是 c++23。** + +``` +$ mcpp build # mcpp.toml 里 standard = "c++11" +error: unsupported C++ standard 'c++11'; expected c++23, c++26, c++2c, + gnu++23, gnu++26, c++latest, or c++fly +``` + +白名单硬编码在 `mcpp/src/manifest/types.cppm:539-586`。所有绕行通道都被显式堵死: + +``` +$ # [targets.X].cxxflags = ["-std=c++11"] +error: targets.ex-c.cxxflags contains '-std=c++11'; use [package].standard + to configure the C++ language standard +``` +(`[build].cxxflags` 同样有此守卫,见 `toml.cppm:830-836`。) + +对一个**以 C++ 标准分级为组织主轴**的课程仓库(`cpp11/` `cpp14/` `cpp17/` `cpp20/` `cpp23/`),这是根本性冲突。 + +**这有多严重?我做了量化实测**:把 51 个 `solutions/` 全部在"原标准"与 c++23 下编译并 diff 运行输出(地址已归一化): + +``` +identical output : 50 +DRIFTED : 1 ← 04-rvalue-references +``` + +好消息:**50/51 无差异**,短期风险比预想的低。坏消息:**漂移的那一个恰好是移动语义的核心课**。C++11 下: + +``` +----> 临时对像 - 右值2 +Object():0x...60c +Object(Object&&):0x...608 ← 移动构造,整节课的教学点 +~Object():0x...60c +``` + +C++23 下(同样带 `-fno-elide-constructors`): + +``` +----> 临时对像 - 右值2 +Object():0x...de8 ← 只剩这一行 +``` + +因为 C++17 的**保证复制省略**让 `-fno-elide-constructors` 无法再强制 prvalue 实质化。教学内容被标准升级**直接抹掉**,而不是"编译失败"这种能被 CI 发现的显式故障。随着课程往 cpp17/20 扩写,这类静默漂移只会变多。 + +顺带确认:`07-constexpr-0` 的 `-Wpedantic -Werror` 守卫(VLA 报错)在两个标准下**都成立**(各 3 个 error),这个练习无损。 + +**❌ 阻塞 2:`mcpp build ` 不能按 target 过滤。** + +`build` 子命令**没有位置参数**(`src/cli.cppm:217-241`),传进去的名字被静默忽略,永远全量构建。实测:`rm -rf target && mcpp build ex-a` → 三个二进制全出来了。(`-p/--package` 选的是 workspace 成员,不是 target;`--target` 是**平台三元组**,不是 target 名。`mcpp run ` 倒是支持。) + +这与 dslings 的语义直接冲突:**练习本来就该是编译不过的**(`D2X_YOUR_ANSWER` 宏展开为空,49 个练习带该标记)。实测单包场景: + +``` +$ mcpp build # 3 个 target,其中 1 个坏 +error: build failed +... +$ ls target/.../bin/ # 空 +``` + +一个坏兄弟拖垮整包、且**一个产物都不出**。 + +**❌ 阻塞 3:没有列 target 的命令,更没有机器可读输出。** + +全 CLI 22 个命令里没有 `list`/`targets`/`metadata`。唯一的 `--json` 是 `mcpp xpkg parse --json`。最接近的是 `target/.build_cache` 里的纯文本 `runTargets=` 块。d2x 的 `list` 契约无法直接满足。 + +### 2.3 阻塞 2 有一个可用的逃生舱 + +mcpp 后端是 ninja,且 **`build.ninja` 在构建失败时依然会生成**。实测(`ex-b` 故意写坏,先 `mcpp build` 失败退出 1,再清空产物只留 `build.ninja`): + +``` +1) mcpp build (all, b broken) exit=1 +2) 清空 bin/ 和 obj/,保留 build.ninja +3) ninja bin/ex-a exit=0 → bin/ 里出现 ex-a +4) ninja bin/ex-c exit=0 → bin/ 里出现 ex-c +5) ninja bin/ex-b exit=1 (坏的那个,正确失败) +6) 最终 bin/: ex-a ex-c +``` + +**逐 target 隔离 + 正确退出码,完全满足 d2x 契约。** `ninja -t targets` 还能直接枚举出 `bin/ex-a: cxx_link` 这样的列表,可以作为 `list` 命令的数据源之一。 + +代价:适配器要依赖 mcpp 的内部产物布局(`target///build.ninja`,路径含哈希,需 `find` 定位)和一个 ninja 可执行文件。**这是耦合内部实现,属于技术债,应该同时向上游提需求。** + +### 2.4 workspace 方案为什么不推荐 + +workspace 能天然隔离失败(实测 `mcpp build -p a` 在兄弟成员坏掉时正常返回 0),但代价过高: + +- **members 不支持 glob**——`docs/06-workspace.md:34` 写了 `members = ["libs/*"]`,**实测报错** `workspace member 'a' not found in [workspace].members`。解析器逐字符串比较(`src/project.cppm:40`)。155 个练习要在根清单里手写 155 条字面路径。**这是文档 bug,值得单独给 mcpp 提 issue。**(另:`workspace.exclude` 被解析后**从未被读取**,是死配置。) +- 每个成员要独立 `mcpp.toml` + 独立 `target/` 目录 → 155 个清单、155 个构建目录。 +- **workspace 根的 `[build]` 不被成员继承**——实测根上写 `include_dirs` 成员读不到,155 个成员每个都得重复写 `include_dirs = ["../../.."]`。 +- **成员构建是串行的**(`src/cli/cmd_build.cppm:65-74` 是普通 `for` 循环,每个成员完整走一遍 `prepare_build` + spawn ninja)。 + +--- + +## 3. 结论与建议 + +### 3.1 直接回答 + +**"除去 xmake、用 mcpp 支撑整个项目"——今天做不到。** 卡在三点:C++ 标准下限、无按 target 构建、无 target 枚举。其中第 2、3 点有 workaround(ninja 直连),**第 1 点没有 workaround**,必须改 mcpp。 + +但三点的修复成本都不高,而且第 1 点的修复位置非常明确、局部。 + +### 3.2 推荐路线:单包多 target + ninja 直连,分三阶段 + +> ⚠️ **本节已被 §4(rustlings/cargo 对照)修订。** 布局从"单包 155 target"改为"**按标准分 workspace member**",上游需求从 3 个降为 1 个必需 + 2 个可选。下面的阶段划分仍然成立,但请以 §4.4 的架构为准。 + +**阶段 0 — 先给 mcpp 提上游需求**(这是关键路径,其余都在等它) + +| # | 需求 | 位置 | 估计改动 | +|---|---|---|---| +| 1 | **【必需】** `[package].standard` 放宽到 `c++11/14/17/20`(含 `gnu++` 变体) | `mcpp/src/manifest/types.cppm:520-586` 的 `normalize_cpp_standard` | ~30 行,纯新增分支。已确认 `[language].modules must be true` 的守卫(`toml.cppm:146-149`)**只在出现废弃的 `[language]` 段时才触发**(`had_language_section`),对源码无硬约束,不构成架构冲突 | +| 2 | 【可选】`mcpp build ` 按 target 名过滤(对齐已有的 `mcpp run `) | `mcpp/src/cli.cppm:217-241` 加位置参数 + `src/build/execute.cppm` 过滤 link unit | 小。有 ninja 兜底 | +| 3 | 【可选】`mcpp list --json`(或 `mcpp targets`)输出 target 名 + 源文件列表 | 新增子命令,数据已在 plan 里 | 小。有 `ninja -t targets` 兜底 | + +附带值得报的两个 bug:workspace `members` glob 文档与实现不符(cargo 同款语法可用,mcpp 报错);`workspace.exclude` 是死代码。 + +> ❌ **不要提"per-target `standard`"这个需求。** 我一度打算这么提(对齐 cargo 的 per-`[[bin]]` `edition`),但实测发现 **cargo 正在废弃该字段**:`warning: 'edition' is set on binary 'ex-c' which is deprecated`。cargo 官方文档标注其"将在未来 Edition 移除"。语言版本的正确粒度是**包**,不是 target。详见 §4.3。 + +**阶段 1 — 在 d2mcpp 侧写 mcpp 适配器**(可与阶段 0 并行) + +- 新建 `d2x/buildtools/mcpp/`,与现有 `xmake/` 平级(`cmake/README.md` 早就占了位,内容只有 `TODO`——说明多适配器一直在规划里)。 +- 用生成器从 `dslings/**/*.cpp` 生成 `mcpp.toml`(155 个 `[targets.*]`,扁平布局,零文件搬迁)。生成器要处理的特殊情况只有 32 个 target、5 种模式,全部可枚举。 +- 适配器实现四个命令: + - `init` → `mcpp build`(忽略失败,目的是刷新 `build.ninja`) + - `list` → `ninja -t targets` 或直接读生成器的映射表,输出 `name@ /abs/path` + - `build ` → 上游需求 2 落地前用 `ninja -C bin/`,落地后换成 `mcpp build ` + - `run ` → `mcpp run ` +- 注意三个坑:**target 名要 shell 安全**(d2x 不做转义);**路径必须绝对**;**名字里不能有 `@`**(会让整行被静默丢弃)。 +- 命名要保持字典序 = 教学顺序(d2x 用 `std::map`)。 + +**阶段 2 — 切换与收尾** + +- `.d2x.json` 的 `buildtools` 改指向 mcpp 适配器;`.xlings.json` 去掉 `xmake` 与 `gcc`/`mingw-w64` 的 pin,换成 `mcpp`(mcpp 自带工具链)。 +- 删掉 `dslings/xmake.lua:1-5` 的 Windows 分支(mcpp 默认静态链接 + 自带 MinGW/MSVC 覆盖)。 +- d2x 的 `checker-smoke` CI job 可以删掉"用真 xmake 而非 xlings shim"的 workaround。 +- **`add_includedirs(".")` 对应 `[build] include_dirs = ["."]`** ——155/155 个文件全靠它,别漏。 + +### 3.3 阶段 0 完成前的过渡建议 + +- **不要现在就把 dslings 迁到 mcpp。** 强行迁移意味着把 cpp11 课程按 c++23 编译,`04-rvalue-references` 的教学内容会**静默失效**(不是编译报错,CI 抓不到)。 +- **可以现在就用 mcpp 的地方**:未来的 `dslings/cpp23/`。那个目录现在是空占位,而 C++23 模块教学本来就该用 mcpp(`d2x/mcpp/common.cppm` 已经放了一个 `export module d2x.project.common;` 的桩)。以它为试点验证适配器,零迁移风险。 + - ⚠️ 但那个桩现在是坏的:`#include` 在全局模块片段里且什么都没 `export`,而**宏本来就不能跨模块导出**——`import d2x.project.common;` 拿不到 `d2x_assert` 也拿不到 `D2X_WAIT`,即拿不到 d2x 的整个判定协议。cpp23 章节要么继续 `#include` 那个头,要么把断言重新设计成非宏形式。这需要先决策。 +- 注意 d2x 目前**只支持单一 `buildtools` 配置**。若要 cpp11-20 留 xmake、cpp23 用 mcpp,需要适配器内部按目录路由,或给 d2x 加多 buildtools 支持。**前者更简单,建议走前者。** + +### 3.4 顺带发现的既有问题(与迁移无关,但建议一并修) + +1. **`dslings-ref-ci.yml` 是个静默空转的 CI。** 它只挑 `-ref` 结尾的 target,而 `solutions/` 在 `xmake.lua:6` 被注释掉了 → grep 返回空 → 循环全跳过 → job 退出 0。第 60 行的 `wc -l` 对空串仍打印 "Found 1 reference targets",把空转掩盖了。**参考答案实际上没有任何 CI 校验。** +2. **`dslings/en/cpp11/xmake.lua` 少一个 target**:`00-auto-and-decltype-5.cpp` 文件在但没有 target(49 文件 / 48 target)。 +3. **命名不一致**:dslings 用 `cpp11-00-auto-and-decltype`(无 `-0`),solutions 用 `cpp11-00-auto-and-decltype-0-ref`。迁移生成器必须处理。 +4. **`solutions/xmake.lua` 没有 Windows 分支**,若重新启用会在 Windows 上缺 UTF-8/gcc/静态链接处理。 +5. **`d2x/src/checker.cppm:74-76` 无 `files.empty()` 保护**就索引 `files[0]`——某个 target 若列出零文件会崩。`d2x/docs/crash-analysis-d2x-in-d2mcpp.md:52` 声称加过保护,实际没有。 + +--- + +## 附:能力对照速查 + +| 需求 | xmake(现状) | mcpp 0.0.99 | 迁移后 | +|---|---|---|---| +| 155 个单文件可执行 | ✅ | ✅ `[targets.*]` | ✅ | +| 零文件搬迁(扁平布局) | ✅ | ✅ 实测 | ✅ | +| 每 target 编译选项 | ✅ | ✅ `cxxflags`/`defines` | ✅ | +| **每 target / 每目录 C++ 标准** | ✅ | 🚨 **下限 c++23,`-std=` 被硬拦** | 需上游 #1 | +| **只构建一个 target** | ✅ | ❌ 位置参数被忽略 | ninja 兜底 / 上游 #2 | +| **坏兄弟不拖垮好 target** | ✅ | ❌ 单包全挂且零产物 | ninja 兜底 | +| **枚举 target** | ✅ 插件 | ❌ 无命令、无 JSON | 生成器兜底 / 上游 #3 | +| 插件机制(`add_plugindirs`) | ✅ | ❌ 无 | 不需要,适配器可独立 | +| 单练习构建耗时(暖) | 0.61 s | **0.014 s** | 大幅改善 | +| 工具链自管理 | ❌ 依赖 xlings pin(3.0.7 已从 registry 移除) | ✅ 沙箱自带 | 大幅改善 | +| Windows | MinGW,需手写 `-static` | MinGW + MSVC,默认静态 | 改善 | + +--- + +## 4. 横向对照:rustlings 和 cargo 是怎么做的 + +dslings 与 rustlings 是同构问题——~155 个单文件练习、**默认就编译不过**、由 checker CLI 驱动。rustlings 恰好在 2024 年做过一次同方向的架构迁移,其取舍和踩到的坑对我们有直接参考价值。 + +### 4.1 rustlings 的两代架构 + +**v5 及以前:直调 `rustc`,不用 cargo。**(`src/exercise.rs` @ tag `5.6.1`) + +```rust +const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; +const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/clippy/Cargo.toml"; + +Mode::Compile => Command::new("rustc") + .args([self.path.to_str().unwrap(), "-o", &temp_file()]) + .args(RUSTC_COLOR_ARGS).args(RUSTC_EDITION_ARGS).output(), +``` + +产物是 CWD 下 pid+线程 id 命名的临时文件,靠 `impl Drop for FileHandle` 回收。IDE 支持靠**另一套**手写生成的 `rust-project.json`(`src/project.rs`,需要手动跑 `rustlings lsp`)。clippy 则再套一层 hack:运行时往固定路径写一个临时 `Cargo.toml`、`cargo clean`、`cargo clippy`——每次全量重建,且练习被编译两遍。 + +**两套真相源直接酿成 bug。** PR #1355 记录得很清楚: + +> The edition args of `rustc` is set to 2018 by default. While rustling generate `rust-project.json` with edition setting to 2021. It may cause some problems. + +**v6(2024-03-31,commit `82b563f1`,`src/exercise.rs +56/−204`):生成 `Cargo.toml`,改用 cargo。** 动机在 issue #1935 里说得很直白,**三条理由里没有一条是速度**: + +> We can generate a `Cargo.toml` file containing a `[[bin]]` entry for every exercise. This should make things much smoother and less hacky, especially because we have many issues related to the **language server**. This should also enable us to use Cargo instead of rustc to run the exercises. That would especially make **Clippy** exercises less of a trouble. This approach also allows us to easily add **dependencies** to the exercises in case we want to do so in the future. + +即:**LSP、Clippy、未来的依赖管理**——外加消灭第二套真相源。 + +### 4.2 v6 的具体机制 + +| 项 | rustlings 做法 | +|---|---| +| 清单 | `dev/Cargo.toml` 是模板,**生成且提交进仓库**(不 gitignore)。只有 `bin = [...]` 那一段被机器重写(`src/cargo_toml.rs::bins_start_end_ind` 按字节偏移做字符串手术),其余手写内容原样保留,学习者可自行加 `[dependencies]` | +| 何时重新生成 | 只在显式 `rustlings dev update`,**从不在运行时隐式生成**。CI 用 `dev check` 重新生成并逐字节比对,不一致就报 "Cargo.toml is outdated" | +| 条目形式 | 不是 `[[bin]]` 段头,是等价的内联表数组,**只有 `name` 和 `path` 两个键**:`{ name = "intro1", path = "../exercises/00_intro/intro1.rs" }`。必须放在任何 table 头之前 | +| 规模 | 94 个练习 → **188 个 bin**(每个练习额外生成一个 `_sol` 指向参考答案) | +| 构建命令 | `cargo build -q --bin ` → 失败即止;`cargo test -q --bin `;`cargo clippy -q --bin --profile test`;最后**直接 exec `target/debug/`,不用 `cargo run`** | +| 诊断格式 | **纯文本**,不用 `--message-format=json`。`cargo metadata` 只用来取 `target_directory` | +| 练习清单来源 | `rustlings-macros/info.toml`,proc macro 在编译期 `include_bytes!` 进二进制。字段:`name` / `dir` / `test`(默认真) / `strict_clippy` / `hint` / `skip_check_unsolved` | +| 通过判定 | **纯退出码**,无输出匹配。特别地,为了避免练习里裸 `exit(1)` 让人一头雾水,会补一句 "The exercise didn't run successfully (nonzero exit code)" | +| 完成状态 | `.rustlings-state.txt`(gitignore)。格式极简:第 3 行是当前练习名,第 5 行起是已完成练习名。**只缓存"在哪一题"和"哪些做完了"**,不缓存编译输出。用名字不用索引,所以重排 `info.toml` 不会毁进度 | +| 文件监听 | `notify` v8 + **200ms 去抖**(脏位图,N 次快速保存塌缩成 1 次重建)+ `EXERCISE_RUNNING: AtomicBool` 自触发保护 + 激进事件过滤(只放行 `Modify::*` 和 `Access::Close(Write)`) | + +`// I AM NOT DONE` 标记**在 v6 已被删除**,改为按 `n` 显式进入下一题 + 持久化状态。 + +### 4.3 cargo 的能力(我在本机 cargo 1.89 实测) + +做了和 mcpp 完全对等的实验(3 个 bin,`ex-b` 故意写坏): + +| 能力 | 结果 | +|---|---| +| `cargo build --bin ex-a` | ✅ exit 0,只产出 `ex-a`,坏兄弟完全无关 | +| `cargo build --bin ex-b` | ✅ exit 101 | +| `cargo run --bin ex-a` | ✅ 且**透传程序退出码**(实测 `exit(3)` → 101 之外的 3) | +| `cargo metadata --format-version 1 --no-deps` | ✅ JSON 含 name + **绝对 `src_path`** + edition;**练习编译不过时照样 exit 0**(清单层查询,不编译) | +| workspace `members = ["ex/*"]` glob | ✅ 可用(mcpp 文档写了但实测报错) | +| `--message-format=json` | ✅ 给出 `file:line:col` 结构化诊断 | +| 逐 bin 构建耗时 | 暖 0.055s / 改动后 0.136s / `metadata` 0.038s | + +**🚨 但 per-`[[bin]]` `edition` 是个陷阱。** 它确实能工作(实测包级 2021 + 单 bin 覆盖 2015,2015-only 代码正确通过/拒绝),**但 cargo 会警告并正在移除**: + +``` +warning: `edition` is set on binary `ex-c` which is deprecated +``` + +cargo book 标注该字段 *"deprecated and will be removed in a future Edition"*。而 rustlings **从未使用过它**——全仓库 grep `edition` 只有一个值 `2024`,且从不出现在 bin 条目里;`append_bins()` 在代码层面就只能写 `name` 和 `path`;`ExerciseInfo` 没有任何 edition/flags 字段。188 个 target 全部继承唯一的包级 `edition = "2024"`。 + +> 而且即便只有一个包级 edition,rustlings 也已经踩到漂移风险——`Cargo.toml:17` 挂着注释 `# On Update: Update the edition of rustfmt in dev check and CARGO_TOML in dev new`,同一个值手工同步三处。 + +**结论:语言版本的正确粒度是「包」,不是「target」。** 这直接否定了我原先"给 mcpp 加 `[targets.X].standard`"的想法。 + +### 4.4 修订后的推荐架构:按标准分 workspace member + +既然粒度是包,而 **d2mcpp 的目录结构本来就是按标准组织的**,两者天然对齐: + +``` +mcpp.toml [workspace] members = ["dslings/cpp11", "dslings/cpp14", ...] +dslings/cpp11/mcpp.toml standard = "c++11" + 49 个 [targets.*] +dslings/cpp14/mcpp.toml standard = "c++14" + 2 个 +dslings/cpp17/mcpp.toml standard = "c++17" +dslings/cpp20/mcpp.toml standard = "c++20" +dslings/cpp23/mcpp.toml standard = "c++23" +``` + +**5~10 个 member,不是 155 个。** §2.4 里反对 workspace 的四条理由全部失效:members 不用 glob(10 条字面路径无所谓)、10 个 `mcpp.toml` 不是负担、`include_dirs` 重复 10 次不是负担、串行构建 10 个 member 也不是负担。 + +已实测验证(用 c++23/c++26 两个 member,因为 c++11 目前仍被拒): + +``` +cpp23 member -> ['-std=c++23'] ← 各自 compile_commands.json +cpp26 member -> ['-std=c++26'] +``` +**每 member 的 `standard` 正确落到编译行。** 且 `mcpp build -p cpp26` 在 `cpp23` member 有坏练习时正常返回 0。 + +剩余问题只有两个,都已有解: +- **member 内部**仍需逐 target 隔离(一个坏练习会拖垮同 member 的 49 个)→ ninja 兜底(§2.3 已实测)或上游需求 2。 +- `08-literal-type-0` 需要 c++17 但住在 cpp11 目录 → 单独开一个小 member,或挪到 cpp17。 + +**上游需求从 3 个降为 1 个必需:只剩 `[package].standard` 放宽。** 而且这个方向与 cargo 的包粒度哲学一致,不是在跟工具较劲。 + +### 4.5 rustlings 踩过的坑:哪些我们会中、哪些不会 + +**⚠️ 会中的:性能是这个模型引入的**回归**,不是收益。** issue #2071「Checking an exercise takes too long」(2024-08 提出,**至今未关**): + +> every time I update the code of an exercise, the `rust-analyzer` builds the whole `exercises` folder (it has like 156 exercises...). During this process, I cannot use any command. + +维护者承认 checker 和 IDE 争抢同一个 cargo lock 与 target 目录,且保存一次就全量失效重算。连带 #2113「Rustling is slow」、#2091「Too many open files (os error 24)」、#2072「LSP issues because of too many errors」。至今只有缓解措施(`rust-analyzer.cargo.targetDir` 隔离目录)。 + +**✅ 但我们大概率不会中最严重的那条**——因为这本质是 **rust-analyzer 特有的全 crate 检查模型**。C++ 侧是 clangd + `compile_commands.json`,**逐 TU 按需**,第 90 个练习编译不过不影响第 3 个的诊断。我实测确认 mcpp 的行为正好支持这点: + +- **构建失败时 `compile_commands.json` 照样生成**(104-target spike:`mcpp build` 退出 1,但 104 条 entry 齐全) +- 每条 entry 带自己的 `-std=`,配合 §4.4 的分 member 架构,clangd 能对每个练习显示**正确标准**下的诊断 + +**这反而是迁移的一个净收益。** d2mcpp 现在**没有** `compile_commands.json`(`.gitignore:49` 预留了位置但文件不存在),IDE 靠 `.vscode/settings.json` 里手写的 `C_Cpp.default.includePath`——它不编码任何 `-std=`,等于**全仓库一个标准**。这正是 rustlings PR #1355 那类"两套真相源"bug 的同款形态。 + +**✅ 独立于构建系统的、更便宜的一个win:done-state 缓存。** rustlings 的"每次重编所有练习"抱怨(#121/#132/#1843)**早于 v6,且是靠 `.rustlings-state.txt` 解决的,不是靠换 cargo**。d2x 目前没有任何完成状态持久化。这件事现在就能做,与 xmake/mcpp 之争无关。 + +**✅ 值得直接抄的 CI 门禁。** rustlings 的 `cargo dev check --require-solutions` 断言:**每个练习都必须失败,每个参考答案都必须通过**。对照 §3.4 第 1 条——d2mcpp 的 `dslings-ref-ci.yml` 目前是静默空转,参考答案零校验。这个门禁应该优先补上。 + +**✅ 值得抄的两个防御细节:** +- rustlings 全量检查时,**"报错"(而非"失败")的练习会退回串行重试**,注释写明 *"it could be because we exceeded the limit of open file descriptors"*。155 个 target 并发时同样会遇到。 +- 文件监听必须有**自触发保护**(构建自身的写入不能反过来触发重建)+ **去抖**。d2x 现在是 `wait_files_changed` 轮询 mtime,没有这两层。 + +### 4.6 一句话总结 + +rustlings 的证据**支持**"生成单一构建描述、一个练习一个 target"这条路线,但**理由是 IDE/lint 集成和消灭第二套真相源,不是速度**。语言版本用**包**粒度(cargo 正在废弃 target 粒度),这恰好让 d2mcpp 的"按标准分目录"结构变成天然的 workspace member 划分,把 mcpp 的上游需求压缩到只剩一条。 diff --git a/.agents/skills/d2mcpp-authoring/SKILL.md b/.agents/skills/d2mcpp-authoring/SKILL.md index 67abbfb..78162d3 100644 --- a/.agents/skills/d2mcpp-authoring/SKILL.md +++ b/.agents/skills/d2mcpp-authoring/SKILL.md @@ -4,7 +4,7 @@ description: >- Authoring conventions, design principles, and file formats for the d2mcpp (D2X) Modern C++ tutorial project. Use this whenever you add or edit a C++ language-feature lesson — a book chapter, a dslings exercise, a reference - solution, or when registering them in SUMMARY/xmake/changelog. Trigger it for + solution, or when registering them in SUMMARY/changelog. Trigger it for any request like "add a chapter for X", "write a dslings exercise for fold expressions", "add the constexpr lesson", "fill the cpp14 section", or anything touching book/src, book/en/src, dslings/, or solutions/. Even small @@ -19,7 +19,7 @@ quartet: **Book (mdBook) + Code (dslings exercises) + Solution + Auto-checker (`d2x checker`)**, all of it **bilingual (zh + en)**. A "lesson" is never one file — it is a coordinated set across several directories. The cardinal sin here is producing a half-set (a chapter with no exercise, a zh file with no en -counterpart, an exercise not registered in xmake). This skill exists so every +counterpart, an exercise missing from solutions/). This skill exists so every lesson lands complete and consistent. Read `references/anatomy.md` for the full directory map and the exact list of @@ -48,7 +48,8 @@ Honor that axis: remark, P0136 inheriting-constructor semantics fix. - **Compile each chapter at its introduction standard.** Do not globally pin a newer standard. If one exercise genuinely needs a later standard, make it an - **explicit, commented exception** in xmake — never a bare `-- TODO` hack. + **explicit, commented exception** via a `// d2x:cxxflags:` header directive + in the exercise file — never a bare `TODO` hack. Background and the full per-feature evolution analysis live in `.agents/docs/2026-06-08-cpp11-feature-evolution-and-cxx20-baseline.md`. Consult @@ -70,8 +71,7 @@ When adding a feature numbered `NN` with slug `topic` (e.g. `06-scoped-enums`): 3. `dslings//NN-topic-K.cpp` — one or more exercises (`K` = 0,1,2…), zh comments 4. `dslings/en//NN-topic-K.cpp` — en exercises (translate comments only) 5. `solutions//NN-topic-K.cpp` — reference solution per exercise -6. Register each exercise target in `dslings//xmake.lua` -7. Register each solution target in `solutions/xmake.lua` +6. (nothing to register — exercises are discovered by directory convention) 8. Add the chapter line to **both** `book/src/SUMMARY.md` and `book/en/src/SUMMARY.md` 9. Add a changelog entry to **both** `book/src/changelog.md` and `book/en/src/changelog.md` @@ -106,7 +106,7 @@ Use `assets/chapter.zh.md` / `assets/chapter.en.md`. Required structure, in orde 6. `## 二、真实案例` corroborates the feature with **real, verbatim STL code** quoted from the in-repo `msvc-stl/` basis — see "The 真实案例 section" below. 7. `## 四、练习代码` lists **every** exercise topic of the chapter (`-0`, `-1`, …, - matching the dslings/xmake count) with dslings links as an index, but the checker + matching the dslings file count) with dslings links as an index, but the checker block shows only the **single entry** command `d2x checker ` — `d2x` auto- advances through the remaining exercises, so do NOT list one command per exercise. Then a `### 练习交流讨论` link to the chapter's forum thread. Each exercise's own @@ -179,4 +179,4 @@ Use `assets/solution.cpp`. It is the corrected exercise with: Before claiming a lesson complete, verify the whole set exists and is wired up, and that the exercise/solution actually build & check. See `references/anatomy.md` for the verification commands (`d2x checker ` and -the xmake build) — run them; do not assert success without the output. +the mcpp build) — run them; do not assert success without the output. diff --git a/.agents/skills/d2mcpp-authoring/assets/chapter.en.md b/.agents/skills/d2mcpp-authoring/assets/chapter.en.md index 35ec161..f12c11a 100644 --- a/.agents/skills/d2mcpp-authoring/assets/chapter.en.md +++ b/.agents/skills/d2mcpp-authoring/assets/chapter.en.md @@ -60,7 +60,7 @@ - diff --git a/.agents/skills/d2mcpp-authoring/references/anatomy.md b/.agents/skills/d2mcpp-authoring/references/anatomy.md index cdd4dee..da7eabf 100644 --- a/.agents/skills/d2mcpp-authoring/references/anatomy.md +++ b/.agents/skills/d2mcpp-authoring/references/anatomy.md @@ -16,12 +16,10 @@ book/en/src/SUMMARY.md # en TOC (register here) book/src/changelog.md # zh changelog (add entry) book/en/src/changelog.md # en changelog (add entry) -dslings//NN-topic-K.cpp # zh exercise(s) -dslings/en//NN-topic-K.cpp # en exercise(s) -dslings//xmake.lua # register exercise target(s) +dslings//NN-topic-K.cpp # zh exercise(s) — no registration needed +dslings/en//NN-topic-K.cpp # en exercise(s) — no registration needed solutions//NN-topic-K.cpp # reference solution(s) -solutions/xmake.lua # register solution target(s) (includes ) ``` Repo-level shared basis (NOT per-lesson, created once, never edited by hand): @@ -37,54 +35,50 @@ The `## 二、真实案例` section of every chapter links into `msvc-stl/` for verbatim STL excerpt; you do not add files here per lesson — only refresh the snapshot via `msvc-stl/SOURCE.md` when needed. -A new `` section that does not exist yet also needs: an `includes("")` -line wired from `dslings/xmake.lua` and `solutions/xmake.lua`, a `dslings//` +A new `` section that does not exist yet needs only: a `dslings//` and `dslings/en//` directory, a `solutions//` directory, and the section heading in both `SUMMARY.md` files (e.g. `# C++14核心语言特性` / -`# C++14 Core Language Features`). +`# C++14 Core Language Features`). No build-file wiring — the Provider +discovers `dslings//` by directory convention. -## xmake registration — exercises (`dslings//xmake.lua`) +## Exercise registration — there is none -Append one target per exercise file. Binary kind is the default; only set it -where existing siblings do. Example for a two-exercise chapter: +Exercises are discovered by **directory convention**, not registered anywhere. +Dropping `dslings//NN-topic-K.cpp` into place is the whole job: the d2x +Provider (`d2x/buildtools/mcpp/`) derives the exercise id, order and chapter +from the path, and generates the mcpp manifests under `.d2x/build/`. -```lua --- target: -NN-topic +This is deliberate. rustlings' most expensive lesson (PR #1355) was the Rust +edition living in *two* places — the `rustc` args and `rust-project.json` — +which drifted and caused bugs. Any standalone registration file is a second +source of truth. Here the only truths are the directory layout and the exercise +file's own header comment. -target("-NN-topic-0") - add_files("NN-topic-0.cpp") +### Per-exercise compile flags -target("-NN-topic-1") - add_files("NN-topic-1.cpp") -``` - -Per-target options seen in the repo, use only when the lesson needs them: +When a lesson needs non-default flags, declare them in the exercise file's own +header — near the code they affect, impossible to drift: -```lua -target("cpp11-04-rvalue-references") - set_optimize("none") - add_cxxflags("-fno-elide-constructors") -- observe moves; note C++17 guaranteed elision can't be disabled - add_files("04-rvalue-references.cpp") +```cpp +// d2x:cxxflags: -O0 -fno-elide-constructors ``` -If an exercise needs a non-default standard, make it an **explicit, commented -exception** — never a bare TODO: +Real examples in the repo: `04-rvalue-references` (observe moves — but note +C++17 guaranteed elision means `-fno-elide-constructors` can no longer force +prvalue materialisation, so this lesson needs rewriting) and `07-constexpr-0` +(`-Wpedantic -Werror` to make the VLA an error). -```lua -target("cppNN-NN-topic-0") - set_languages("c++17") -- exception: - add_files("NN-topic-0.cpp") -``` +### C++ standard -The default standard is set at the top of `dslings//xmake.lua`. Per the -design principle, that should be the **introduction standard** of the section -(e.g. `set_languages("c++11")` for `cpp11`). Avoid per-host standard forks; if -one is unavoidable, comment why. +All exercises compile as **c++23**. mcpp currently rejects `c++11/14/17/20` +(`src/manifest/types.cppm` whitelist) and the project chose not to patch mcpp +upstream. The `cppNN/` directories therefore denote *when a feature was +introduced* — they no longer change compile flags. -## xmake registration — solutions (`solutions/xmake.lua` + `solutions//xmake.lua`) +## Solution registration — there is none either -Mirror the exercise targets so CI builds the reference solutions too. Follow the -existing pattern in `solutions//xmake.lua`. +Drop the file at `solutions//NN-topic-K.cpp`. CI swaps it over the exercise +and asserts it passes; see `d2x/buildtools/mcpp/tests/e2e.sh`. ## SUMMARY registration @@ -113,8 +107,11 @@ to an absolute `YYYY/MM/DD`. Run from the project root; report real output, do not assert success blind: ```bash -# build the exercise + solution targets (lang defaults come from xmake) -xmake f -c >/dev/null && xmake build cppNN-NN-topic-0 +# check a single exercise through the same path `d2x checker` uses +mcpp run -q -p d2x/buildtools/mcpp -- check cppNN-NN-topic-0 + +# or validate every exercise + solution at once +bash d2x/buildtools/mcpp/tests/e2e.sh # drive the auto-checker against the exercise (expects the unsolved exercise to fail, # the solution to pass) — name omits the NN- prefix diff --git a/.github/workflows/dslings-ref-ci.yml b/.github/workflows/dslings-ref-ci.yml index ab21712..20fcf35 100644 --- a/.github/workflows/dslings-ref-ci.yml +++ b/.github/workflows/dslings-ref-ci.yml @@ -1,4 +1,14 @@ -name: Validate dslings reference solutions +name: Validate dslings exercises and reference solutions + +# 断言两件事,缺一不可: +# 1. 每个练习「未完成时」不通过 —— 否则学员会被直接跳过,练习形同虚设 +# 2. 每个参考答案「放进去后」通过 —— 否则参考答案本身是错的 +# +# 走的是 `d2x checker` 内部使用的同一条 Provider 路径(d2x/buildtools/mcpp), +# 所以 CI 绿灯等价于学员本地能跑通,而不是另一条平行的构建路径。 +# +# 旧版本只挑 `-ref` 结尾的 xmake target,而 solutions/ 在 xmake.lua 里被注释掉, +# grep 返回空、循环全跳过、job 退出 0 —— 实际校验零个目标。 on: push: @@ -7,109 +17,64 @@ on: - "dslings/**" - "solutions/**" - "d2x/**" - - "xmake.lua" + - "mcpp.toml" + - ".d2x.json" - ".github/workflows/dslings-ref-ci.yml" pull_request: paths: - "dslings/**" - "solutions/**" - "d2x/**" - - "xmake.lua" + - "mcpp.toml" + - ".d2x.json" - ".github/workflows/dslings-ref-ci.yml" workflow_dispatch: jobs: - build-and-run-reference: + validate-exercises: runs-on: ubuntu-latest env: - XLINGS_VERSION: "0.4.14" + XLINGS_VERSION: "0.4.51" + XLINGS_NON_INTERACTIVE: "1" steps: - uses: actions/checkout@v4 - - name: Install Xlings ${{ env.XLINGS_VERSION }} + # 官方一键安装脚本自带镜像回退。手动 curl GitHub release 包会被 + # release CDN 的间歇性 504 打断。 + - name: Install xlings ${{ env.XLINGS_VERSION }} run: | set -eu - curl -fsSL "https://github.com/openxlings/xlings/releases/download/v${XLINGS_VERSION}/xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" \ - | tar -xzf - -C /tmp - /tmp/xlings-${XLINGS_VERSION}-linux-x86_64/bin/xlings self install + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \ + | bash -s "v${XLINGS_VERSION}" echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" - - name: Install xmake (via xlings) - run: xlings install xmake -y - - - name: xmake config - run: xmake f -y - - # Following steps go through the SAME `xmake d2x-buildtools` plugin - # path that `d2x checker` uses internally (see d2x/buildtools/xmake/main.lua). - # This way the CI exercises the real d2x integration code path, not a - # parallel xmake-only path. A passing CI here is what a learner would - # see if they ran `d2x checker cpp11-XX-feat-K-ref` locally. - - - name: List all *-ref targets via d2x-buildtools - id: list_ref + # 按 .xlings.json 安装 mcpp。mcpp 自带工具链沙箱,无需另装 gcc。 + - name: Install mcpp run: | set -eu - REF_TARGETS=$(xmake d2x-buildtools --command=list 2>/dev/null \ - | tr ' ' '\n' \ - | sed 's/@.*$//' \ - | grep -E '\-ref$' \ - | sort -u) - echo "Found $(echo "$REF_TARGETS" | wc -l) reference targets:" - echo "$REF_TARGETS" - echo "targets<> "$GITHUB_OUTPUT" - echo "$REF_TARGETS" >> "$GITHUB_OUTPUT" - echo "EOF" >> "$GITHUB_OUTPUT" + xlings update + xlings install -y - - name: Build all *-ref targets via d2x-buildtools - run: | - set -eu - while IFS= read -r tgt; do - [ -z "$tgt" ] && continue - echo "::group::build $tgt" - xmake d2x-buildtools --command=build --target="$tgt" - echo "::endgroup::" - done <<< "${{ steps.list_ref.outputs.targets }}" + - name: Build d2x Provider + run: mcpp build -p d2x/buildtools/mcpp - - name: Run all *-ref targets via d2x-buildtools and assert no ❌ - run: | - set -eu - fail=0 - while IFS= read -r tgt; do - [ -z "$tgt" ] && continue - echo "::group::run $tgt" - output=$(xmake d2x-buildtools --command=run --target="$tgt" 2>&1) - echo "$output" - if echo "$output" | grep -qE '❌|Compilation/Running failed'; then - echo "::error::reference target $tgt produced a ❌ — exercise solution does not pass its own asserts" - fail=1 - fi - if echo "$output" | grep -q 'Delete the D2X_WAIT to continue'; then - echo "::error::reference target $tgt still contains D2X_WAIT — remove it from the solution" - fail=1 - fi - echo "::endgroup::" - done <<< "${{ steps.list_ref.outputs.targets }}" - exit $fail + - name: Validate exercises and solutions + run: bash d2x/buildtools/mcpp/tests/e2e.sh - name: Verify dslings ↔ solutions filename parity run: | set -eu - # Every dslings/{cpp,hpp} file should have a matching solutions/ counterpart + # en/ 镜像跳过 —— 参考答案与语言无关,两边共用同一份 missing=0 while IFS= read -r f; do rel="${f#dslings/}" - # Skip the en/ mirror — solutions are language-neutral - case "$rel" in - en/*) continue ;; - esac if [ ! -f "solutions/$rel" ]; then echo "::warning::missing solutions/$rel (paired with dslings/$rel)" missing=$((missing+1)) fi done < <(find dslings -name '*.cpp' -not -path 'dslings/en/*') if [ "$missing" -gt 0 ]; then - echo "::warning::$missing dslings exercises do not yet have a reference solution. This is informational during the bring-up phase; tighten to error once cpp11 is fully covered." + echo "::warning::$missing 个练习尚无参考答案。铺开阶段仅作提示;cpp11 覆盖完整后收紧为错误。" fi diff --git a/.gitignore b/.gitignore index 19e8da7..3ec6cc6 100644 --- a/.gitignore +++ b/.gitignore @@ -40,11 +40,9 @@ __pycache__ # d2x project files .xlings .vscode -.xmake .zed llm.config.xlings media -build .cache/ dslings/compile_commands.json # d2x Provider 生成物(mcpp workspace 清单)与学习进度 diff --git a/.vscode/settings.json b/.vscode/settings.json index a80607e..9638d4c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,8 +8,7 @@ "C_Cpp.files.exclude": { "**/.vscode": true, - "**/build": true, - "**/.xmake": true, + "**/target": true, "**/.xlings": true, "**/.DS_Store": true }, diff --git a/.xlings.json b/.xlings.json index 66d4e23..c66cdbb 100644 --- a/.xlings.json +++ b/.xlings.json @@ -1,10 +1,12 @@ { "workspace": { "d2x": "0.1.5", - "xmake": "3.0.7", "mdbook": "0.4.43", "code": "", - "gcc": { "linux": "15.1.0" }, - "mingw-w64": { "windows": "13.0.0" } + "mcpp": { + "linux": "0.0.99", + "macosx": "0.0.99", + "windows": "0.0.99" + } } -} \ No newline at end of file +} diff --git a/book/en/src/base/chapter_1.md b/book/en/src/base/chapter_1.md index 9baf009..802c819 100644 --- a/book/en/src/base/chapter_1.md +++ b/book/en/src/base/chapter_1.md @@ -158,7 +158,7 @@ Edit the `lang` attribute in the project configuration file `.d2x.json`. `zh` co ```bash { "version": "0.1.1", - "buildtools": "xmake d2x-buildtools", + "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", "lang": "en", ... } diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index fabfeb8..81920aa 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -156,7 +156,7 @@ Homepage: https://github.com/openxlings/xlings ```bash { "version": "0.1.1", - "buildtools": "xmake d2x-buildtools", + "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", "lang": "en", < -- 修改这里 ... } @@ -171,7 +171,7 @@ Homepage: https://github.com/openxlings/xlings ```bash { "version": "0.1.1", - "buildtools": "xmake d2x-buildtools", + "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", "lang": "en", < -- 修改这里 ... } diff --git a/d2x/buildtools/README.md b/d2x/buildtools/README.md deleted file mode 100644 index 9106a16..0000000 --- a/d2x/buildtools/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# d2x 支持的构建工具 - -通过对构建工具做最小化的接口规范来适配不同编程语言及其工具链 - -## 构建工具接口规范 - -``` -xxx d2x-buildtools [command] [target] - -Commands: - list List all targets - build Build specified target - run Run specified target -``` \ No newline at end of file diff --git a/d2x/buildtools/cmake/README.md b/d2x/buildtools/cmake/README.md deleted file mode 100644 index 30404ce..0000000 --- a/d2x/buildtools/cmake/README.md +++ /dev/null @@ -1 +0,0 @@ -TODO \ No newline at end of file diff --git a/d2x/buildtools/xmake/main.lua b/d2x/buildtools/xmake/main.lua deleted file mode 100644 index b757d22..0000000 --- a/d2x/buildtools/xmake/main.lua +++ /dev/null @@ -1,67 +0,0 @@ -import("core.base.json") -import("core.base.option") -import("core.project.project") -import("core.project.config") - -local function get_d2x_lang() - local d2x_json_file = path.join(os.projectdir(), ".d2x.json") - if os.isfile(d2x_json_file) then - local d2x_config = json.loadfile(d2x_json_file) or {} - return d2x_config["lang"] - end - return nil -end - -function list() - - config.load() - local lang = get_d2x_lang() - if lang and #lang > 0 then - config.set("lang", lang, {force = true}) - end - - local targets = project.targets() - for name, _ in pairs(targets) do - local files = targets[name]:sourcefiles() - local flag = true - - -- cprintf / print xxx - printf(name) -- target name - for _, file in ipairs(files) do - file = path.absolute(file) - if flag then - --printf(": " .. file) -- avoid C:\ issue on Windows - printf("@ " .. file) - flag = false - else - printf(", " .. file) - end - end - printf("\n") - end -end - -function main() - - local command = option.get("command") - local target = option.get("target") - - os.cd(os.projectdir()) - --print("project file: " .. project.rootfile()) - - if command == "init" then - local lang = get_d2x_lang() - if lang and #lang > 0 then - os.exec("xmake f --lang=" .. lang) - end - elseif command == "list" then - list() - elseif command == "build" then - os.exec("xmake build " .. (target or "")) - elseif command == "run" then - os.exec("xmake run " .. (target or "")) - else - print("Unknown command: " .. tostring(command)) - end - -end diff --git a/d2x/buildtools/xmake/xmake.lua b/d2x/buildtools/xmake/xmake.lua deleted file mode 100644 index ad4a113..0000000 --- a/d2x/buildtools/xmake/xmake.lua +++ /dev/null @@ -1,14 +0,0 @@ -task("d2x-buildtools") - - set_category("plugin") - - on_run("main") - - set_menu { - usage = "xmake d2x-buildtools [options]", - description = "d2x buildtools plugin", - options = { - {nil, "command", "v", nil, "d2x-buildtools's command"}, - {nil, "target", "v", nil, "the target name"} - } - } \ No newline at end of file diff --git a/d2x/cpp/common.lua b/d2x/cpp/common.lua deleted file mode 100644 index 4a14d17..0000000 --- a/d2x/cpp/common.lua +++ /dev/null @@ -1,29 +0,0 @@ -function get_local_lang() ---local config = platform.get_config_info() - local local_lang = "en" -- Default language - -- TODO: config language by xlings.json - if is_host("linux") then - local tmp_local_lang = os.getenv("LANG") or "en" - if tmp_local_lang:find("zh") then - local_lang = "zh" - end - elseif is_host("windows") then - local tmp_local_lang = nil - if find_tool("wmic") then -- Windows 10 and earlier - -- wmic is deprecated in Windows 11, but still available - tmp_local_lang = os.iorun([[wmic os get locale]]) - else -- win11+ - tmp_local_lang = os.iorun([[powershell -NoProfile -ExecutionPolicy Bypass -Command "'{0:X4}' -f (Get-Culture).LCID"]]) - end - if tmp_local_lang and tmp_local_lang:find("0804") then - local_lang = "zh" - end - elseif is_host("macosx") then - local tmp_local_lang = os.iorun([[defaults read -g AppleLocale]]) - if tmp_local_lang and tmp_local_lang:find("zh") then - local_lang = "zh" - end - end - - return local_lang -end diff --git a/dslings/cpp11/xmake.lua b/dslings/cpp11/xmake.lua deleted file mode 100644 index e7dbdae..0000000 --- a/dslings/cpp11/xmake.lua +++ /dev/null @@ -1,195 +0,0 @@ -if is_host("windows") then - set_languages("cxx14") -else - set_languages("cxx11") -end - --- target: cpp11-00-auto-and-decltype - -target("cpp11-00-auto-and-decltype") - set_kind("binary") - add_files("00-auto-and-decltype-0.cpp") -target("cpp11-00-auto-and-decltype-1") - set_kind("binary") - add_files("00-auto-and-decltype-1.cpp") -target("cpp11-00-auto-and-decltype-2") - set_kind("binary") - add_files("00-auto-and-decltype-2.cpp") -target("cpp11-00-auto-and-decltype-3") - set_kind("binary") - add_files("00-auto-and-decltype-3.cpp") -target("cpp11-00-auto-and-decltype-4") - set_kind("binary") - add_files("00-auto-and-decltype-4.cpp") -target("cpp11-00-auto-and-decltype-5") - set_kind("binary") - add_files("00-auto-and-decltype-5.cpp") - --- target: cpp11-01-default-and-delete - -target("cpp11-01-default-and-delete-0") - set_kind("binary") - add_files("01-default-and-delete-0.cpp") - -target("cpp11-01-default-and-delete-1") - set_kind("binary") - add_files("01-default-and-delete-1.cpp") - -target("cpp11-01-default-and-delete-2") - set_kind("binary") - add_files("01-default-and-delete-2.cpp") - --- target: cpp11-02-final-and-override - -target("cpp11-02-final-and-override-0") - add_files("02-final-and-override-0.cpp") - -target("cpp11-02-final-and-override-1") - add_files("02-final-and-override-1.cpp") - -target("cpp11-02-final-and-override-2") - add_files("02-final-and-override-2.cpp") - --- target: cpp11-03-trailing-return-type - -target("cpp11-03-trailing-return-type") - add_files("03-trailing-return-type.cpp") - --- target: cpp11-04-rvalue-references - -target("cpp11-04-rvalue-references") - set_optimize("none") - add_cxxflags("-fno-elide-constructors") - add_files("04-rvalue-references.cpp") - --- target: cpp11-05-move-semantics - -target("cpp11-05-move-semantics-0") - add_files("05-move-semantics-0.cpp") - -target("cpp11-05-move-semantics-1") - add_files("05-move-semantics-1.cpp") - -target("cpp11-05-move-semantics-2") - add_files("05-move-semantics-2.cpp") - --- target: cpp11-06-scoped-enums - -target("cpp11-06-scoped-enums-0") - add_files("06-scoped-enums-0.cpp") - -target("cpp11-06-scoped-enums-1") - add_files("06-scoped-enums-1.cpp") - --- target: cpp11-07-constexpr - -target("cpp11-07-constexpr-0") - add_cxxflags("-Wpedantic -Werror") - add_files("07-constexpr-0.cpp") - -target("cpp11-07-constexpr-1") - add_files("07-constexpr-1.cpp") - --- target: cpp11-08-literal-type - -target("cpp11-08-literal-type-0") - set_languages("c++17") -- TODO: optimize it - add_files("08-literal-type-0.cpp") - -target("cpp11-08-literal-type-1") - add_files("08-literal-type-1.cpp") - --- target: cpp11-09-list-initialization - -target("cpp11-09-list-initialization-0") - add_files("09-list-initialization-0.cpp") - -target("cpp11-09-list-initialization-1") - add_files("09-list-initialization-1.cpp") - -target("cpp11-09-list-initialization-2") - add_files("09-list-initialization-2.cpp") - -target("cpp11-09-list-initialization-3") - add_files("09-list-initialization-3.cpp") - --- target: cpp11-10-delegating-constructors - -target("cpp11-10-delegating-constructors-0") - add_files("10-delegating-constructors-0.cpp") - -target("cpp11-10-delegating-constructors-1") - add_files("10-delegating-constructors-1.cpp") - --- target: cpp11-11-inherited-constructors - -target("cpp11-11-inherited-constructors-0") - add_files("11-inherited-constructors-0.cpp") - -target("cpp11-11-inherited-constructors-1") - add_files("11-inherited-constructors-1.cpp") - -target("cpp11-11-inherited-constructors-2") - add_files("11-inherited-constructors-2.cpp") - --- target: cpp11-12-nullptr - -target("cpp11-12-nullptr-0") - add_files("12-nullptr-0.cpp") - -target("cpp11-12-nullptr-1") - add_files("12-nullptr-1.cpp") - -target("cpp11-12-nullptr-2") - add_files("12-nullptr-2.cpp") - --- target: cpp11-13-long-long -target("cpp11-13-long-long-0") - add_files("13-long-long-0.cpp") - -target("cpp11-13-long-long-1") - add_files("13-long-long-1.cpp") - --- target: cpp11-14-type-alias - -target("cpp11-14-type-alias-0") - add_files("14-type-alias-0.cpp") - -target("cpp11-14-type-alias-1") - add_files("14-type-alias-1.cpp") - -target("cpp11-14-type-alias-2") - add_files("14-type-alias-2.cpp") - -target("cpp11-14-type-alias-3") - add_files("14-type-alias-3.cpp") - --- target: cpp11-15-variadic-templates - -target("cpp11-15-variadic-templates-0") - add_files("15-variadic-templates-0.cpp") - -target("cpp11-15-variadic-templates-1") - add_files("15-variadic-templates-1.cpp") - --- target: cpp11-16-generalized-unions - -target("cpp11-16-generalized-unions-0") - add_files("16-generalized-unions-0.cpp") - -target("cpp11-16-generalized-unions-1") - add_files("16-generalized-unions-1.cpp") - -target("cpp11-16-generalized-unions-2") - add_files("16-generalized-unions-2.cpp") - --- target: cpp11-17-pod-type - -target("cpp11-17-pod-type-0") - add_files("17-pod-type-0.cpp") - -target("cpp11-17-pod-type-1") - add_files("17-pod-type-1.cpp") - -target("cpp11-17-pod-type-2") - add_files("17-pod-type-2.cpp") diff --git a/dslings/cpp14/xmake.lua b/dslings/cpp14/xmake.lua deleted file mode 100644 index 8a21cbf..0000000 --- a/dslings/cpp14/xmake.lua +++ /dev/null @@ -1,11 +0,0 @@ -set_languages("cxx14") - --- target: cpp14-00-generic-lambdas - -target("cpp14-00-generic-lambdas-0") - set_kind("binary") - add_files("00-generic-lambdas-0.cpp") - -target("cpp14-00-generic-lambdas-1") - set_kind("binary") - add_files("00-generic-lambdas-1.cpp") diff --git a/dslings/en/cpp11/xmake.lua b/dslings/en/cpp11/xmake.lua deleted file mode 100644 index 6daeb9e..0000000 --- a/dslings/en/cpp11/xmake.lua +++ /dev/null @@ -1,192 +0,0 @@ -if is_host("windows") then - set_languages("cxx14") -else - set_languages("cxx11") -end - --- target: cpp11-00-auto-and-decltype - -target("cpp11-00-auto-and-decltype") - set_kind("binary") - add_files("00-auto-and-decltype-0.cpp") -target("cpp11-00-auto-and-decltype-1") - set_kind("binary") - add_files("00-auto-and-decltype-1.cpp") -target("cpp11-00-auto-and-decltype-2") - set_kind("binary") - add_files("00-auto-and-decltype-2.cpp") -target("cpp11-00-auto-and-decltype-3") - set_kind("binary") - add_files("00-auto-and-decltype-3.cpp") -target("cpp11-00-auto-and-decltype-4") - set_kind("binary") - add_files("00-auto-and-decltype-4.cpp") - --- target: cpp11-01-default-and-delete - -target("cpp11-01-default-and-delete-0") - set_kind("binary") - add_files("01-default-and-delete-0.cpp") - -target("cpp11-01-default-and-delete-1") - set_kind("binary") - add_files("01-default-and-delete-1.cpp") - -target("cpp11-01-default-and-delete-2") - set_kind("binary") - add_files("01-default-and-delete-2.cpp") - --- target: cpp11-02-final-and-override - -target("cpp11-02-final-and-override-0") - add_files("02-final-and-override-0.cpp") - -target("cpp11-02-final-and-override-1") - add_files("02-final-and-override-1.cpp") - -target("cpp11-02-final-and-override-2") - add_files("02-final-and-override-2.cpp") - --- target: cpp11-03-trailing-return-type - -target("cpp11-03-trailing-return-type") - add_files("03-trailing-return-type.cpp") - --- target: cpp11-04-rvalue-references - -target("cpp11-04-rvalue-references") - set_optimize("none") - add_cxxflags("-fno-elide-constructors") - add_files("04-rvalue-references.cpp") - --- target: cpp11-05-move-semantics - -target("cpp11-05-move-semantics-0") - add_files("05-move-semantics-0.cpp") - -target("cpp11-05-move-semantics-1") - add_files("05-move-semantics-1.cpp") - -target("cpp11-05-move-semantics-2") - add_files("05-move-semantics-2.cpp") - --- target: cpp11-06-scoped-enums - -target("cpp11-06-scoped-enums-0") - add_files("06-scoped-enums-0.cpp") - -target("cpp11-06-scoped-enums-1") - add_files("06-scoped-enums-1.cpp") - --- target: cpp11-07-constexpr - -target("cpp11-07-constexpr-0") - add_cxxflags("-Wpedantic -Werror") - add_files("07-constexpr-0.cpp") - -target("cpp11-07-constexpr-1") - add_files("07-constexpr-1.cpp") - --- target: cpp11-08-literal-type - -target("cpp11-08-literal-type-0") - set_languages("c++17") -- TODO: optimize it - add_files("08-literal-type-0.cpp") - -target("cpp11-08-literal-type-1") - add_files("08-literal-type-1.cpp") - --- target: cpp11-09-list-initialization - -target("cpp11-09-list-initialization-0") - add_files("09-list-initialization-0.cpp") - -target("cpp11-09-list-initialization-1") - add_files("09-list-initialization-1.cpp") - -target("cpp11-09-list-initialization-2") - add_files("09-list-initialization-2.cpp") - -target("cpp11-09-list-initialization-3") - add_files("09-list-initialization-3.cpp") - --- target: cpp11-10-delegating-constructors - -target("cpp11-10-delegating-constructors-0") - add_files("10-delegating-constructors-0.cpp") - -target("cpp11-10-delegating-constructors-1") - add_files("10-delegating-constructors-1.cpp") - --- target: cpp11-11-inherited-constructors - -target("cpp11-11-inherited-constructors-0") - add_files("11-inherited-constructors-0.cpp") - -target("cpp11-11-inherited-constructors-1") - add_files("11-inherited-constructors-1.cpp") - -target("cpp11-11-inherited-constructors-2") - add_files("11-inherited-constructors-2.cpp") - --- target: cpp11-12-nullptr - -target("cpp11-12-nullptr-0") - add_files("12-nullptr-0.cpp") - -target("cpp11-12-nullptr-1") - add_files("12-nullptr-1.cpp") - -target("cpp11-12-nullptr-2") - add_files("12-nullptr-2.cpp") - --- target: cpp11-13-long-long -target("cpp11-13-long-long-0") - add_files("13-long-long-0.cpp") - -target("cpp11-13-long-long-1") - add_files("13-long-long-1.cpp") - --- target: cpp11-14-type-alias - -target("cpp11-14-type-alias-0") - add_files("14-type-alias-0.cpp") - -target("cpp11-14-type-alias-1") - add_files("14-type-alias-1.cpp") - -target("cpp11-14-type-alias-2") - add_files("14-type-alias-2.cpp") - -target("cpp11-14-type-alias-3") - add_files("14-type-alias-3.cpp") - --- target: cpp11-15-variadic-templates - -target("cpp11-15-variadic-templates-0") - add_files("15-variadic-templates-0.cpp") - -target("cpp11-15-variadic-templates-1") - add_files("15-variadic-templates-1.cpp") - --- target: cpp11-16-generalized-unions - -target("cpp11-16-generalized-unions-0") - add_files("16-generalized-unions-0.cpp") - -target("cpp11-16-generalized-unions-1") - add_files("16-generalized-unions-1.cpp") - -target("cpp11-16-generalized-unions-2") - add_files("16-generalized-unions-2.cpp") - --- target: cpp11-17-pod-type - -target("cpp11-17-pod-type-0") - add_files("17-pod-type-0.cpp") - -target("cpp11-17-pod-type-1") - add_files("17-pod-type-1.cpp") - -target("cpp11-17-pod-type-2") - add_files("17-pod-type-2.cpp") diff --git a/dslings/en/cpp14/xmake.lua b/dslings/en/cpp14/xmake.lua deleted file mode 100644 index 8a21cbf..0000000 --- a/dslings/en/cpp14/xmake.lua +++ /dev/null @@ -1,11 +0,0 @@ -set_languages("cxx14") - --- target: cpp14-00-generic-lambdas - -target("cpp14-00-generic-lambdas-0") - set_kind("binary") - add_files("00-generic-lambdas-0.cpp") - -target("cpp14-00-generic-lambdas-1") - set_kind("binary") - add_files("00-generic-lambdas-1.cpp") diff --git a/dslings/en/xmake.lua b/dslings/en/xmake.lua deleted file mode 100644 index 7678d9a..0000000 --- a/dslings/en/xmake.lua +++ /dev/null @@ -1,6 +0,0 @@ -target("00-0-hello-mcpp") - set_languages("cxx11") - add_files("hello-mcpp.cpp") - -includes("cpp11") -includes("cpp14") diff --git a/dslings/xmake.lua b/dslings/xmake.lua deleted file mode 100644 index 32c5a95..0000000 --- a/dslings/xmake.lua +++ /dev/null @@ -1,23 +0,0 @@ -if is_host("windows") then - set_encodings("source:utf-8", "target:utf-8") - set_toolchains("gcc") - add_ldflags("-static") -end - -option("lang") - set_default("en") - set_description("Language: en or zh") - -local lang = get_config("lang") - -if lang == "zh" then - - target("00-0-hello-mcpp") - set_languages("cxx11") - add_files("hello-mcpp.cpp") - - includes("cpp11") - includes("cpp14") -else - includes("en") -end diff --git a/solutions/cpp11/xmake.lua b/solutions/cpp11/xmake.lua deleted file mode 100644 index 31cdd51..0000000 --- a/solutions/cpp11/xmake.lua +++ /dev/null @@ -1,198 +0,0 @@ --- Reference solutions for cpp11 chapter exercises. --- Each target name mirrors dslings/cpp11/ with a "-ref" suffix. --- Used by CI to verify exercise infrastructure (xmake target / include paths / --- language standard) and that the canonical answer actually passes all --- d2x_assert / d2x_assert_eq checks. - -if is_host("windows") then - set_languages("cxx14") -else - set_languages("cxx11") -end - --- target: cpp11-00-auto-and-decltype - -target("cpp11-00-auto-and-decltype-0-ref") - add_files("00-auto-and-decltype-0.cpp") - -target("cpp11-00-auto-and-decltype-1-ref") - add_files("00-auto-and-decltype-1.cpp") - -target("cpp11-00-auto-and-decltype-2-ref") - add_files("00-auto-and-decltype-2.cpp") - -target("cpp11-00-auto-and-decltype-3-ref") - add_files("00-auto-and-decltype-3.cpp") - -target("cpp11-00-auto-and-decltype-4-ref") - add_files("00-auto-and-decltype-4.cpp") - -target("cpp11-00-auto-and-decltype-5-ref") - add_files("00-auto-and-decltype-5.cpp") - --- target: cpp11-01-default-and-delete - -target("cpp11-01-default-and-delete-0-ref") - add_files("01-default-and-delete-0.cpp") - -target("cpp11-01-default-and-delete-1-ref") - add_files("01-default-and-delete-1.cpp") - -target("cpp11-01-default-and-delete-2-ref") - add_files("01-default-and-delete-2.cpp") - --- target: cpp11-02-final-and-override - -target("cpp11-02-final-and-override-0-ref") - add_files("02-final-and-override-0.cpp") - -target("cpp11-02-final-and-override-1-ref") - add_files("02-final-and-override-1.cpp") - -target("cpp11-02-final-and-override-2-ref") - add_files("02-final-and-override-2.cpp") - --- target: cpp11-03-trailing-return-type - -target("cpp11-03-trailing-return-type-ref") - add_files("03-trailing-return-type.cpp") - --- target: cpp11-04-rvalue-references - -target("cpp11-04-rvalue-references-ref") - set_optimize("none") - add_cxxflags("-fno-elide-constructors") - add_files("04-rvalue-references.cpp") - --- target: cpp11-05-move-semantics - -target("cpp11-05-move-semantics-0-ref") - add_files("05-move-semantics-0.cpp") - -target("cpp11-05-move-semantics-1-ref") - add_files("05-move-semantics-1.cpp") - -target("cpp11-05-move-semantics-2-ref") - add_files("05-move-semantics-2.cpp") - --- target: cpp11-06-scoped-enums - -target("cpp11-06-scoped-enums-0-ref") - add_files("06-scoped-enums-0.cpp") - -target("cpp11-06-scoped-enums-1-ref") - add_files("06-scoped-enums-1.cpp") - --- target: cpp11-07-constexpr - -target("cpp11-07-constexpr-0-ref") - add_cxxflags("-Wpedantic -Werror") - add_files("07-constexpr-0.cpp") - -target("cpp11-07-constexpr-1-ref") - add_files("07-constexpr-1.cpp") - --- target: cpp11-08-literal-type - -target("cpp11-08-literal-type-0-ref") - set_languages("c++17") -- TODO: optimize it - add_files("08-literal-type-0.cpp") - -target("cpp11-08-literal-type-1-ref") - add_files("08-literal-type-1.cpp") - --- target: cpp11-09-list-initialization - -target("cpp11-09-list-initialization-0-ref") - add_files("09-list-initialization-0.cpp") - -target("cpp11-09-list-initialization-1-ref") - add_files("09-list-initialization-1.cpp") - -target("cpp11-09-list-initialization-2-ref") - add_files("09-list-initialization-2.cpp") - -target("cpp11-09-list-initialization-3-ref") - add_files("09-list-initialization-3.cpp") - --- target: cpp11-10-delegating-constructors - -target("cpp11-10-delegating-constructors-0-ref") - add_files("10-delegating-constructors-0.cpp") - -target("cpp11-10-delegating-constructors-1-ref") - add_files("10-delegating-constructors-1.cpp") - --- target: cpp11-11-inherited-constructors - -target("cpp11-11-inherited-constructors-0-ref") - add_files("11-inherited-constructors-0.cpp") - -target("cpp11-11-inherited-constructors-1-ref") - add_files("11-inherited-constructors-1.cpp") - -target("cpp11-11-inherited-constructors-2-ref") - add_files("11-inherited-constructors-2.cpp") - --- target: cpp11-12-nullptr - -target("cpp11-12-nullptr-0-ref") - add_files("12-nullptr-0.cpp") - -target("cpp11-12-nullptr-1-ref") - add_files("12-nullptr-1.cpp") - -target("cpp11-12-nullptr-2-ref") - add_files("12-nullptr-2.cpp") - --- target: cpp11-13-long-long - -target("cpp11-13-long-long-0-ref") - add_files("13-long-long-0.cpp") - -target("cpp11-13-long-long-1-ref") - add_files("13-long-long-1.cpp") - --- target: cpp11-14-type-alias - -target("cpp11-14-type-alias-0-ref") - add_files("14-type-alias-0.cpp") - -target("cpp11-14-type-alias-1-ref") - add_files("14-type-alias-1.cpp") - -target("cpp11-14-type-alias-2-ref") - add_files("14-type-alias-2.cpp") - -target("cpp11-14-type-alias-3-ref") - add_files("14-type-alias-3.cpp") - --- target: cpp11-15-variadic-templates - -target("cpp11-15-variadic-templates-0-ref") - add_files("15-variadic-templates-0.cpp") - -target("cpp11-15-variadic-templates-1-ref") - add_files("15-variadic-templates-1.cpp") - --- target: cpp11-16-generalized-unions - -target("cpp11-16-generalized-unions-0-ref") - add_files("16-generalized-unions-0.cpp") - -target("cpp11-16-generalized-unions-1-ref") - add_files("16-generalized-unions-1.cpp") - -target("cpp11-16-generalized-unions-2-ref") - add_files("16-generalized-unions-2.cpp") - --- target: cpp11-17-pod-type - -target("cpp11-17-pod-type-0-ref") - add_files("17-pod-type-0.cpp") - -target("cpp11-17-pod-type-1-ref") - add_files("17-pod-type-1.cpp") - -target("cpp11-17-pod-type-2-ref") - add_files("17-pod-type-2.cpp") diff --git a/solutions/cpp14/xmake.lua b/solutions/cpp14/xmake.lua deleted file mode 100644 index 8bbbb90..0000000 --- a/solutions/cpp14/xmake.lua +++ /dev/null @@ -1,11 +0,0 @@ -set_languages("cxx14") - --- target: cpp14-00-generic-lambdas - -target("cpp14-00-generic-lambdas-0-ref") - set_kind("binary") - add_files("00-generic-lambdas-0.cpp") - -target("cpp14-00-generic-lambdas-1-ref") - set_kind("binary") - add_files("00-generic-lambdas-1.cpp") diff --git a/solutions/xmake.lua b/solutions/xmake.lua deleted file mode 100644 index c1b845e..0000000 --- a/solutions/xmake.lua +++ /dev/null @@ -1,11 +0,0 @@ --- Reference solutions root. --- Mirrors dslings/ structure but only contains the canonical "filled-in" --- answers, used purely for build / run CI. Each target carries the "-ref" --- suffix to coexist with the dslings/ targets that share the same logical --- name (e.g. cpp11-13-long-long-0 vs cpp11-13-long-long-0-ref). --- --- Solutions are language-neutral by design: the comments matter only to --- contributors, not the compiler, so we keep a single zh-style copy. - -includes("cpp11/xmake.lua") -includes("cpp14/xmake.lua") diff --git a/xmake.lua b/xmake.lua deleted file mode 100644 index e37aca0..0000000 --- a/xmake.lua +++ /dev/null @@ -1,6 +0,0 @@ -add_plugindirs("d2x/buildtools") - -add_includedirs(".") - -includes("dslings/xmake.lua") ---includes("solutions/xmake.lua") \ No newline at end of file From 8b01ee8df1fa7f7700a99ae0d5d7fdb092b1245b Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Mon, 20 Jul 2026 00:26:07 +0800 Subject: [PATCH 03/25] =?UTF-8?q?feat(harness):=20=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E4=BF=A1=E5=8F=B7=E6=94=B9=E8=B5=B0=E4=BE=A7=E4=BF=A1=E9=81=93?= =?UTF-8?q?=EF=BC=8C=E7=BB=83=E4=B9=A0=E8=84=9A=E6=89=8B=E6=9E=B6=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E6=88=90=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 判定原先 100% 是带内信号:断言失败并不改变退出码(实测退出码仍为 0), 所以只能扫 stdout 找 ❌。这会误判——一个断言全过的正确解答,只要在说明 文字里打了个 ❌ 就被判失败(已复现)。反过来,输出截断或 Windows 控制台 代码页不对,判定同样失效。 改为:stdout 回归「给人看」,判定走侧信道 NDJSON。 harness 独立成 mcpp 库包 dslings/harness/,同时提供两条路径: - #include 宏路径。可见输出与旧版逐字节一致, ✅/❌ 的逐条对照是教学的一部分,不动。 - import d2x.harness; 模块路径。用 std::source_location 自动带 file/line,比 __LINE__ 更准,正好是 Verdict.diagnostics 需要的东西。 两条路径不等价,这是 C++ 的硬约束而非疏漏:宏无法跨模块导出,所以 D2X_YOUR_ANSWER 没有模块等价物——它必须展开为空才能制造编译错误。 模块化章节需要另一套填空约定。 include/ 下保持 d2x/cpp/common.hpp 这个既有路径,104 个练习和书本一行 不用改;路径改名可以以后单独做,不必和机制改造捆在一起。 生成的 member 清单改用 [dependencies] path 依赖,干掉了 include_dirs = ["../../.."] 那个把整个仓库根塞进搜索路径的 hack—— 原先练习能 #include 仓库里任何文件。 侧信道逐条追加而非退出时统一写:练习段错误时崩溃前的断言结果照样保留。 D2X_RESULT_FILE 未设置时 harness 只打印不写文件,学员直接跑二进制零摩擦。 Provider 判定顺序:有 ok:false → Fail 且每条失败转成一个 Diagnostic; 无失败但有 wait → Blocked;侧信道文件不存在 → 退回「编译通过 + 退出 0」。 最后一条让 harness 自动变成可选的——纯观察型练习(现有 18 个只用 D2X_WAIT)可以写成零依赖的纯 C++ 文件,学员能原样拷进 Compiler Explorer。 顺带删掉死代码 d2x_is_invocable(104 个练习中 0 处使用)和坏掉的模块桩 d2x/mcpp/common.cppm(宏不能跨模块导出,import 进去什么也拿不到)。 e2e.sh 的清理钩子收窄为逐个练习目录:harness 现在也住在 dslings/ 下, 原先的 `git checkout -- dslings/` 会把开发中的改动一起抹掉(已踩过)。 实测:51/51 参考答案通过;四态判定正确(未完成→fail、参考答案→pass、 答案对且输出含❌→pass、答案对留路障→blocked);真实断言失败产出带 行号与期望/实际值的 diagnostics;模块路径与「无 harness 退回退出码」均通过。 --- d2x/buildtools/mcpp/src/emit.cppm | 21 +++- d2x/buildtools/mcpp/src/main.cpp | 15 ++- d2x/buildtools/mcpp/src/manifest.cppm | 7 +- d2x/buildtools/mcpp/src/runner.cppm | 119 +++++++++++++++--- d2x/buildtools/mcpp/tests/e2e.sh | 12 +- d2x/cpp/common.hpp | 46 ------- d2x/mcpp/common.cppm | 5 - dslings/harness/include/d2x/cpp/common.hpp | 72 +++++++++++ .../harness/include/d2x}/cpp/honly_logger.hpp | 0 dslings/harness/include/d2x/cpp/report.hpp | 81 ++++++++++++ dslings/harness/mcpp.toml | 15 +++ dslings/harness/src/harness.cppm | 62 +++++++++ mcpp.toml | 2 +- 13 files changed, 379 insertions(+), 78 deletions(-) delete mode 100644 d2x/cpp/common.hpp delete mode 100644 d2x/mcpp/common.cppm create mode 100644 dslings/harness/include/d2x/cpp/common.hpp rename {d2x => dslings/harness/include/d2x}/cpp/honly_logger.hpp (100%) create mode 100644 dslings/harness/include/d2x/cpp/report.hpp create mode 100644 dslings/harness/mcpp.toml create mode 100644 dslings/harness/src/harness.cppm diff --git a/d2x/buildtools/mcpp/src/emit.cppm b/d2x/buildtools/mcpp/src/emit.cppm index 63a6bbb..4b516b1 100644 --- a/d2x/buildtools/mcpp/src/emit.cppm +++ b/d2x/buildtools/mcpp/src/emit.cppm @@ -83,9 +83,24 @@ export void output(std::string_view chunk) { line(std::format(R"({{"event":"output","chunk":{}}})", str(chunk))); } -export void verdict(std::string_view outcome, std::string_view stage_name, int exit_code) { - line(std::format(R"({{"event":"verdict","outcome":{},"stage":{},"exit_code":{},"diagnostics":[]}})", - str(outcome), str(stage_name), exit_code)); +export void verdict(std::string_view outcome, std::string_view stage_name, int exit_code, + const std::vector& diagnostics = {}) { + line(std::format(R"({{"event":"verdict","outcome":{},"stage":{},"exit_code":{},"diagnostics":{}}})", + str(outcome), str(stage_name), exit_code, array(diagnostics))); +} + +// 一条失败的断言 → 一个 Diagnostic。d2x 的前端据此做行内高亮和跳转, +// 学员不用在几十行输出里找是哪一条没过。 +export std::string diagnostic(std::string_view file, int line_no, + std::string_view expr, + std::string_view expected, std::string_view actual) { + auto message = expected.empty() && actual.empty() + ? std::format("断言未通过: {}", expr) + : std::format("断言未通过: {} —— 期望 {},实际 {}", expr, expected, actual); + + return std::format( + R"({{"file":{},"line":{},"col":0,"severity":"error","message":{}}})", + str(file), line_no, str(message)); } export void error(std::string_view message) { diff --git a/d2x/buildtools/mcpp/src/main.cpp b/d2x/buildtools/mcpp/src/main.cpp index 80be0f8..d2e710e 100644 --- a/d2x/buildtools/mcpp/src/main.cpp +++ b/d2x/buildtools/mcpp/src/main.cpp @@ -90,11 +90,20 @@ int cmd_check(const fs::path& root, std::string_view id) { } d2x::emit::stage("run"); - auto ran = d2x::runner::run_current(); + + // 侧信道放在构建目录里:与生成物同生共死,不污染仓库 + auto result_file = d2x::manifest::build_dir(root) / "_current" / "result.ndjson"; + auto ran = d2x::runner::run_current(result_file); d2x::emit::output(ran.output); - auto outcome = d2x::runner::judge_run(ran.exit_code, ran.output); - d2x::emit::verdict(d2x::runner::to_string(outcome), "run", ran.exit_code); + auto report = d2x::runner::judge_run(ran.exit_code, result_file); + + std::vector diags; + for (const auto& f : report.failures) { + diags.push_back(d2x::emit::diagnostic(f.file, f.line, f.expr, f.expected, f.actual)); + } + + d2x::emit::verdict(d2x::runner::to_string(report.outcome), "run", ran.exit_code, diags); return 0; } diff --git a/d2x/buildtools/mcpp/src/manifest.cppm b/d2x/buildtools/mcpp/src/manifest.cppm index bcf5843..9f4ef73 100644 --- a/d2x/buildtools/mcpp/src/manifest.cppm +++ b/d2x/buildtools/mcpp/src/manifest.cppm @@ -49,8 +49,11 @@ void write_package_header(std::ostream& out, std::string_view name) { std::println(out, "[build]"); // 显式清空源码 glob:练习不是这个包的「源码」,它们只是各自 target 的入口。 std::println(out, "sources = []"); - // 仓库根进搜索路径——155 个练习全部 #include - std::println(out, "include_dirs = [\"{}\"]", kToRoot); + std::println(out, ""); + // 脚手架走正经的库依赖,而不是把仓库根塞进 include 搜索路径。 + // 后者会让练习能 #include 仓库里任何文件,是个隐患。 + std::println(out, "[dependencies]"); + std::println(out, "harness = {{ path = \"{}/dslings/harness\" }}", kToRoot); } void write_target(std::ostream& out, const fs::path& repo_root, const Exercise& ex) { diff --git a/d2x/buildtools/mcpp/src/runner.cppm b/d2x/buildtools/mcpp/src/runner.cppm index 7bcb3ef..8f9b71a 100644 --- a/d2x/buildtools/mcpp/src/runner.cppm +++ b/d2x/buildtools/mcpp/src/runner.cppm @@ -62,15 +62,8 @@ export Captured capture(const std::string& cmd) { return result; } -// d2mcpp 的练习断言协议,定义在 d2x/cpp/common.hpp: -// d2x_assert / d2x_assert_eq 失败时打印 ❌ -// D2X_WAIT 宏打印一句提示,学员删掉它才算真正完成 -// // 这套判定是「课程约定」,不是「构建工具行为」——所以它属于 Provider, // 而不是 d2x 框架。换一门 Rust 课程,这里会完全不同。 -export constexpr std::string_view kFailMark = "\xE2\x9D\x8C"; // U+274C ❌ -export constexpr std::string_view kWaitMark = "D2X_WAIT"; - export enum class Outcome { Pass, Fail, Blocked }; export std::string_view to_string(Outcome o) { @@ -82,14 +75,99 @@ export std::string_view to_string(Outcome o) { return "fail"; } -// 运行阶段的输出判定。退出码为 0 只是必要条件: -// 出现 ❌ → 断言没过,Fail -// 出现 D2X_WAIT → 答案已对但路障还在,Blocked(既非失败也不该前进) -export Outcome judge_run(int exit_code, std::string_view output) { - if (exit_code != 0) return Outcome::Fail; - if (output.find(kFailMark) != std::string_view::npos) return Outcome::Fail; - if (output.find(kWaitMark) != std::string_view::npos) return Outcome::Blocked; - return Outcome::Pass; +export struct Failure { + std::string expr; + std::string expected; + std::string actual; + std::string file; + int line{}; +}; + +export struct RunReport { + Outcome outcome{Outcome::Pass}; + std::vector failures; +}; + +// 从侧信道 NDJSON 里取一个字段。这里不引入 JSON 库: +// 格式由我们自己的 harness 产出,字段固定、无嵌套、无数组。 +std::string field(std::string_view line, std::string_view key) { + auto pat = std::format("\"{}\":", key); + auto at = line.find(pat); + if (at == std::string_view::npos) return {}; + at += pat.size(); + if (at >= line.size()) return {}; + + if (line[at] == '"') { // 字符串值 + ++at; + std::string out; + while (at < line.size() && line[at] != '"') { + if (line[at] == '\\' && at + 1 < line.size()) { + ++at; + switch (line[at]) { + case 'n': out += '\n'; break; + case 't': out += '\t'; break; + case 'r': out += '\r'; break; + default: out += line[at]; + } + } else { + out += line[at]; + } + ++at; + } + return out; + } + auto end = line.find_first_of(",}", at); // 裸值(数字/布尔) + return std::string(line.substr(at, end == std::string_view::npos ? end : end - at)); +} + +// 判定顺序: +// 有 ok:false → Fail,每条失败都能转成一个 Diagnostic +// 无失败但有 wait → Blocked(答案已对,只差拆路障) +// 侧信道文件不存在 → 退回「退出码为 0 即通过」 +// +// 最后一条让 harness 自动变成可选的:纯观察型练习可以是零依赖的 +// 纯 C++ 文件,学员能原样拷进 Compiler Explorer。 +export RunReport judge_run(int exit_code, const fs::path& result_file) { + RunReport report; + + if (exit_code != 0) { + report.outcome = Outcome::Fail; + // 即使退出码非 0,已写入的断言仍然有价值(崩溃前过了几条) + } + + std::ifstream in(result_file); + if (!in) { + // 没有侧信道:练习没用 harness,退出码就是全部信息 + report.outcome = (exit_code == 0) ? Outcome::Pass : Outcome::Fail; + return report; + } + + bool saw_wait = false; + for (std::string line; std::getline(in, line); ) { + auto kind = field(line, "kind"); + if (kind == "wait") { saw_wait = true; continue; } + if (kind != "assert") continue; + if (field(line, "ok") == "true") continue; + + int line_no = 0; + auto raw = field(line, "line"); + std::from_chars(raw.data(), raw.data() + raw.size(), line_no); + + report.failures.push_back(Failure{ + .expr = field(line, "expr"), + .expected = field(line, "expected"), + .actual = field(line, "actual"), + .file = field(line, "file"), + .line = line_no, + }); + } + + if (!report.failures.empty()) report.outcome = Outcome::Fail; + else if (exit_code != 0) report.outcome = Outcome::Fail; + else if (saw_wait) report.outcome = Outcome::Blocked; + else report.outcome = Outcome::Pass; + + return report; } // mcpp 需要在 workspace 根(.d2x/build/)下执行。Provider 是独立进程, @@ -102,7 +180,16 @@ export Captured build_current() { return capture("mcpp build -q -p _current"); } -export Captured run_current() { +// 运行前把侧信道路径告诉 harness,并清掉上一轮的残留 —— +// harness 是追加写的,不清会把上次的失败算进这次。 +export Captured run_current(const fs::path& result_file) { + std::error_code ec; + fs::remove(result_file, ec); +#ifndef _WIN32 + ::setenv("D2X_RESULT_FILE", result_file.string().c_str(), 1); +#else + ::_putenv_s("D2X_RESULT_FILE", result_file.string().c_str()); +#endif return capture("mcpp run -q -p _current"); } diff --git a/d2x/buildtools/mcpp/tests/e2e.sh b/d2x/buildtools/mcpp/tests/e2e.sh index f0ef46d..c541817 100755 --- a/d2x/buildtools/mcpp/tests/e2e.sh +++ b/d2x/buildtools/mcpp/tests/e2e.sh @@ -14,8 +14,16 @@ cd "$REPO_ROOT" PROVIDER=(mcpp run -q -p d2x/buildtools/mcpp --) -# 任何退出路径都还原被改动的练习文件,绝不把仓库留在脏状态 -restore() { git checkout -- dslings/ 2>/dev/null || true; } +# 任何退出路径都还原被改动的练习文件,绝不把仓库留在脏状态。 +# +# 只还原练习源文件,绝不整目录还原 dslings/ —— 脚手架库 dslings/harness/ +# 也在这个目录下,整目录还原会把开发中的改动一起抹掉(踩过一次)。 +EXERCISE_DIRS=(dslings/cpp* dslings/en dslings/hello-mcpp.cpp) +restore() { + for d in "${EXERCISE_DIRS[@]}"; do + [ -e "$d" ] && git checkout -- "$d" 2>/dev/null || true + done +} trap restore EXIT outcome_of() { # $1 = exercise id diff --git a/d2x/cpp/common.hpp b/d2x/cpp/common.hpp deleted file mode 100644 index c67d9df..0000000 --- a/d2x/cpp/common.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef COMMON_HPP_D2X -#define COMMON_HPP_D2X - -#include -#include - -#include - -#define d2x_assert(expr) \ -{ \ - if (!(expr)) { \ - HONLY_LOGW("❌(error) | %s", #expr); \ - } else { \ - HONLY_LOGI_P("✅ | %s", #expr); \ - } \ -} - -#define d2x_assert_eq(a, b) \ -{ \ - if (a != b) {\ - HONLY_LOGW("❌ | %s == %s (%s == %s)", \ - #a, #b, std::to_string(a).c_str(), std::to_string(b).c_str()); \ - } else {\ - HONLY_LOGI_P("✅ | %s == %s (%s == %s)", \ - #a, #b, std::to_string(a).c_str(), std::to_string(b).c_str()); \ - } \ -} - -#define D2X_WAIT HONLY_LOGW("🥳 Delete the D2X_WAIT to continue..."); -#define D2X_YOUR_ANSWER -#define D2X_DONT_DELETE_THIS(x) x - -template -class d2x_is_invocable { -private: - template - static auto test(U* f) -> decltype((*f)(std::declval()...), std::true_type()); - - template - static std::false_type test(...); - -public: - static constexpr bool value = decltype(test(nullptr))::value; -}; - -#endif \ No newline at end of file diff --git a/d2x/mcpp/common.cppm b/d2x/mcpp/common.cppm deleted file mode 100644 index c88709c..0000000 --- a/d2x/mcpp/common.cppm +++ /dev/null @@ -1,5 +0,0 @@ -module; - -#include - -export module d2x.project.common; \ No newline at end of file diff --git a/dslings/harness/include/d2x/cpp/common.hpp b/dslings/harness/include/d2x/cpp/common.hpp new file mode 100644 index 0000000..3b2a4bb --- /dev/null +++ b/dslings/harness/include/d2x/cpp/common.hpp @@ -0,0 +1,72 @@ +#ifndef COMMON_HPP_D2X +#define COMMON_HPP_D2X + +// d2mcpp 练习脚手架 —— 经典 #include 路径。 +// +// 这条路径提供宏,因为 D2X_YOUR_ANSWER 必须展开为空才能制造编译错误, +// 而宏无法跨模块导出。模块化的练习请改用 `import d2x.harness;`,它提供 +// d2x::check / check_eq / wait(靠 std::source_location 自动带 file/line), +// 但没有填空占位符的等价物。 +// +// 可见输出与旧版逐字节一致 —— ✅/❌ 的逐条对照是教学的一部分。新增的只是 +// 把同样的结果写进侧信道,让判定不再依赖扫 stdout 找 emoji(那会误判: +// 一个断言全过的正确解答,只要在说明文字里打了个 ❌ 就被判失败)。 + +#include +#include + +#include +#include + +namespace d2x::detail { + +// std::to_string 只接受算术类型,但断言里允许任意可比较类型。 +// 转得了的转,转不了的留空:侧信道少一点细节,好过整个 harness 编不过。 +template +inline std::string show(const T& v) { + if constexpr (requires { std::to_string(v); }) return std::to_string(v); + else if constexpr (requires { std::string(v); }) return std::string(v); + else return {}; +} + +} // namespace d2x::detail + +#define d2x_assert(expr) \ +{ \ + bool d2x_ok_ = static_cast(expr); \ + d2x::report::assertion(d2x_ok_, #expr, "true", d2x_ok_ ? "true" : "false", \ + __FILE__, __LINE__); \ + if (!d2x_ok_) { \ + HONLY_LOGW("❌(error) | %s", #expr); \ + } else { \ + HONLY_LOGI_P("✅ | %s", #expr); \ + } \ +} + +#define d2x_assert_eq(a, b) \ +{ \ + bool d2x_ok_ = ((a) == (b)); \ + d2x::report::assertion(d2x_ok_, #a " == " #b, \ + d2x::detail::show(b), d2x::detail::show(a), \ + __FILE__, __LINE__); \ + if (!d2x_ok_) {\ + HONLY_LOGW("❌ | %s == %s (%s == %s)", \ + #a, #b, std::to_string(a).c_str(), std::to_string(b).c_str()); \ + } else {\ + HONLY_LOGI_P("✅ | %s == %s (%s == %s)", \ + #a, #b, std::to_string(a).c_str(), std::to_string(b).c_str()); \ + } \ +} + +#define D2X_WAIT { \ + d2x::report::wait(__FILE__, __LINE__); \ + HONLY_LOGW("🥳 Delete the D2X_WAIT to continue..."); \ +} + +// 展开为空 —— 学员在这里填类型。留空会得到「未声明」错误, +// 比留一个占位标识符导致的语法错误更贴近意图。 +#define D2X_YOUR_ANSWER + +#define D2X_DONT_DELETE_THIS(x) x + +#endif // COMMON_HPP_D2X diff --git a/d2x/cpp/honly_logger.hpp b/dslings/harness/include/d2x/cpp/honly_logger.hpp similarity index 100% rename from d2x/cpp/honly_logger.hpp rename to dslings/harness/include/d2x/cpp/honly_logger.hpp diff --git a/dslings/harness/include/d2x/cpp/report.hpp b/dslings/harness/include/d2x/cpp/report.hpp new file mode 100644 index 0000000..81f6f37 --- /dev/null +++ b/dslings/harness/include/d2x/cpp/report.hpp @@ -0,0 +1,81 @@ +#ifndef REPORT_HPP_D2X +#define REPORT_HPP_D2X + +// 侧信道上报:把判定信号从 stdout 移到带外。 +// +// 为什么需要:断言失败并不改变退出码,所以判定只能靠扫 stdout 找 ❌ —— +// 这会误判。一个断言全过的正确解答,只要在说明文字里打了个 ❌ 就被判失败 +// (已复现)。反过来,输出被截断或 Windows 控制台代码页不对,判定同样失效。 +// +// 于是:stdout 回归「给人看」,判定走这个文件。 +// +// 逐条追加而非退出时统一写 —— 练习段错误时,崩溃之前的断言结果照样保留, +// 学员仍能看到前几条过了。 +// +// D2X_RESULT_FILE 未设置时这里什么都不做:学员直接跑二进制零摩擦。 + +#include +#include +#include + +namespace d2x::report { + +inline std::string escape(const std::string& s) { + std::string out; + out.reserve(s.size() + 8); + for (unsigned char c : s) { + switch (c) { + case '"': out += "\\\""; break; + case '\\': out += "\\\\"; break; + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: + if (c < 0x20) { + char buf[8]; + std::snprintf(buf, sizeof buf, "\\u%04x", static_cast(c)); + out += buf; + } else { + out += static_cast(c); + } + } + } + return out; +} + +// 每次都重新打开并追加。比持有 FILE* 慢,但换来两个性质: +// 进程异常终止不丢已写入的行;无需依赖静态析构顺序。 +inline void emit(const std::string& json_line) { + const char* path = std::getenv("D2X_RESULT_FILE"); + if (!path || !*path) return; // 学员直接运行:什么都不做 + + if (std::FILE* f = std::fopen(path, "a")) { + std::fputs(json_line.c_str(), f); + std::fputc('\n', f); + std::fclose(f); + } +} + +inline void assertion(bool ok, const std::string& expr, + const std::string& expected, const std::string& actual, + const char* file, int line) { + std::string out = "{\"kind\":\"assert\",\"ok\":"; + out += ok ? "true" : "false"; + out += ",\"expr\":\"" + escape(expr) + "\""; + out += ",\"expected\":\"" + escape(expected) + "\""; + out += ",\"actual\":\"" + escape(actual) + "\""; + out += ",\"file\":\"" + escape(file ? file : "") + "\""; + out += ",\"line\":" + std::to_string(line) + "}"; + emit(out); +} + +inline void wait(const char* file, int line) { + std::string out = "{\"kind\":\"wait\",\"file\":\""; + out += escape(file ? file : ""); + out += "\",\"line\":" + std::to_string(line) + "}"; + emit(out); +} + +} // namespace d2x::report + +#endif // REPORT_HPP_D2X diff --git a/dslings/harness/mcpp.toml b/dslings/harness/mcpp.toml new file mode 100644 index 0000000..d1cea20 --- /dev/null +++ b/dslings/harness/mcpp.toml @@ -0,0 +1,15 @@ +[package] +name = "harness" +version = "0.1.0" +description = "d2mcpp 练习脚手架:断言、路障、填空占位符,以及判定用的侧信道上报" +license = "Apache-2.0" +standard = "c++23" + +# include/ 会传播给消费者,所以练习里 `#include ` +# 这个既有路径原样可用 —— 104 个练习和书本一行都不用改。 +# 路径命名以后可以单独重整,不必和这次机制改造捆在一起。 +[build] +include_dirs = ["include"] + +[targets.harness] +kind = "lib" diff --git a/dslings/harness/src/harness.cppm b/dslings/harness/src/harness.cppm new file mode 100644 index 0000000..9f3c882 --- /dev/null +++ b/dslings/harness/src/harness.cppm @@ -0,0 +1,62 @@ +module; + +// 上报核心以头文件形式共享给两条路径。放在全局模块片段里引入, +// 这样模块实现能用它,而它的宏(无)不会泄漏给消费者。 +#include +#include + +export module d2x.harness; + +import std; + +// 模块化练习的脚手架 —— import 路径。 +// +// 与 #include 路径的差异,写在这里免得踩坑: +// +// 宏无法跨模块导出。所以这里提供的是函数,不是 d2x_assert 那套宏。 +// 好处是 std::source_location 自动带上 file/line,比 __LINE__ 更准, +// 而且正好是 Provider 填 Verdict.diagnostics 需要的东西;代价是丢了 +// 表达式原文(宏版能打印 "a == b",函数版只能打印值)。 +// +// D2X_YOUR_ANSWER 没有模块等价物 —— 它必须展开为空才能制造编译错误, +// 本质就是宏。模块化章节需要另一套填空约定。 +export namespace d2x { + +// 与 #include 路径共用同一套可见输出格式,学员在两种练习里看到的是一致的。 +inline void check(bool ok, std::source_location loc = std::source_location::current()) { + d2x::report::assertion(ok, "check", "true", ok ? "true" : "false", + loc.file_name(), static_cast(loc.line())); + if (ok) std::print("\033[32m[HONLY LOGI]: - ✅ | check\033[0m\n"); + else std::print("\033[33m[HONLY LOGW]: {}:{} - ❌(error) | check\033[0m\n", + loc.file_name(), loc.line()); + std::fflush(stdout); +} + +template +inline void check_eq(const A& a, const B& b, + std::source_location loc = std::source_location::current()) { + const bool ok = (a == b); + + auto show = [](const auto& v) -> std::string { + if constexpr (requires { std::format("{}", v); }) return std::format("{}", v); + else return {}; + }; + + d2x::report::assertion(ok, "check_eq", show(b), show(a), + loc.file_name(), static_cast(loc.line())); + + if (ok) std::print("\033[32m[HONLY LOGI]: - ✅ | {} == {}\033[0m\n", show(a), show(b)); + else std::print("\033[33m[HONLY LOGW]: {}:{} - ❌ | {} == {}\033[0m\n", + loc.file_name(), loc.line(), show(a), show(b)); + std::fflush(stdout); +} + +// D2X_WAIT 的模块等价物:显式路障,学员删掉它才算真正完成这一题。 +inline void wait(std::source_location loc = std::source_location::current()) { + d2x::report::wait(loc.file_name(), static_cast(loc.line())); + std::print("\033[33m[HONLY LOGW]: {}:{} - 🥳 Delete the d2x::wait() to continue...\033[0m\n", + loc.file_name(), loc.line()); + std::fflush(stdout); +} + +} // namespace d2x diff --git a/mcpp.toml b/mcpp.toml index 4edacd4..963815c 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -4,4 +4,4 @@ # member、以及只含当前一题的 _current)自成一个独立 workspace,由 # Provider 生成——不放进这里,避免「根清单引用尚未生成的成员」的循环。 [workspace] -members = ["d2x/buildtools/mcpp"] +members = ["d2x/buildtools/mcpp", "dslings/harness"] From c9bb1eeed140c1c804c0185455647cce0949312d Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Mon, 20 Jul 2026 00:52:58 +0800 Subject: [PATCH 04/25] =?UTF-8?q?fix:=20=E5=A0=B5=E4=BD=8F=20id=20?= =?UTF-8?q?=E6=B3=A8=E5=85=A5=E3=80=81=E4=BF=AE=E5=A4=8D=E8=8B=B1=E6=96=87?= =?UTF-8?q?=E7=AD=94=E6=A1=88=E7=A9=BA=E8=BD=AC=E3=80=81=E8=AE=A9=E6=95=99?= =?UTF-8?q?=E5=AD=A6=E6=BC=82=E7=A7=BB=E5=8F=AF=E8=A2=AB=20CI=20=E6=A3=80?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 三个真缺陷,都不是理论风险: 1. 练习 id 注入。id 直接取自文件名,有两个危险去向:d2x 把它拼进 shell 命令,我们把它写进生成的 TOML([targets.])。带反引号、`]` 或引号 的文件名在任一处都能越界 —— 对社区课程仓库来说,一个恶意 PR 文件名就 足以在任何跑 checker 的人机器上执行命令。 在 discovery 源头做白名单校验并拒绝,而不是在两个下游各自转义:这类 文件名本就是笔误或恶意,与其想办法安全地传递,不如让作者改名。 实测:`99-evil`touch pwned_marker`.cpp` 被拒绝,命令未执行。 2. e2e.sh 把所有英文参考答案静默 SKIP。前缀剥离顺序错了 —— `${sol#en/}` 执行时 sol 已经以 "solutions/" 开头,匹配不到任何东西,是个静默 no-op, 于是每道英文练习都因找不到 solutions/en/... 而跳过。 这正是本脚本头部注释里说要防的那种空转,和旧 CI 一模一样的毛病。 除了修顺序,另加一道防线:pass==0 时直接判失败,杜绝「0 失败」蒙混。 实测修复后 en 也是 51/51 真验证(此前是 0 通过 / 52 跳过)。 3. d2x_assert_eq 的日志分支仍用裸 std::to_string,而上报分支已改用 SFINAE 安全的 show()。std::to_string 没有 std::string / const char* / scoped enum 的重载,下一个比较字符串或强类型枚举的练习会直接编译失败 —— show() 存在的意义就是避免这个,却只用了一半。 教学漂移(04-rvalue-references): C++17 起 prvalue 直接初始化目标(保证复制省略),-fno-elide-constructors 再也无法让 `Object obj = Object();` 产生移动构造。全仓库改按 c++23 编译后, 这节课的核心观测点被静默抹掉。 改成从具名对象 std::move —— 这在任何标准下都必然调用移动构造。更重要的是 补了一条 d2x_assert(move_ctor_calls >= 1):漂移之所以能静默发生,正是因为 从前没有任何断言检查它,输出少一行没人发现。现在它是 CI 可检测的事实。 --- d2x/buildtools/mcpp/src/discovery.cppm | 28 ++++++++++++++++++++++ d2x/buildtools/mcpp/tests/e2e.sh | 20 +++++++++++++--- dslings/harness/include/d2x/cpp/common.hpp | 4 ++-- solutions/cpp11/04-rvalue-references.cpp | 15 +++++++++--- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/d2x/buildtools/mcpp/src/discovery.cppm b/d2x/buildtools/mcpp/src/discovery.cppm index a9e2d87..7a35184 100644 --- a/d2x/buildtools/mcpp/src/discovery.cppm +++ b/d2x/buildtools/mcpp/src/discovery.cppm @@ -4,6 +4,10 @@ // 是 PR #1355:edition 同时写在 rustc 参数和 rust-project.json 里,两边漂移 // 酿成 bug。任何独立声明文件都是第二套真相源。这里的真相只有两处,且都 // 无法漂移:目录结构,和练习文件自己的头部注释。 +module; + +#include // stderr + export module d2x.provider.discovery; import std; @@ -108,6 +112,21 @@ std::string humanize(std::string_view topic) { return out; } +// 练习 id 直接来自文件名,而它有两个危险去向: +// 1. d2x 把它拼进一条 shell 命令(` check `) +// 2. 我们把它写进生成的 TOML(`[targets.]`) +// 带反引号、`]`、引号或换行的文件名能在任一处越界。 +// +// 在源头挡住比在两个下游各自转义更可靠 —— 课程里本就不该出现这种文件名, +// 与其想办法安全地传递它,不如明确拒绝并让作者改名。 +export bool valid_id(std::string_view id) { + if (id.empty()) return false; + return std::ranges::all_of(id, [](unsigned char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.'; + }); +} + // 扫描练习根目录。lang=zh 用 dslings/,lang=en 用 dslings/en/。 export std::vector scan(const fs::path& repo_root, std::string_view lang) { fs::path base = repo_root / "dslings"; @@ -139,6 +158,15 @@ export std::vector scan(const fs::path& repo_root, std::string_view la ex.title = humanize(stem); ex.order = std_rank(std_dir) * 100'000; } + + // 拒绝而不是转义:这类文件名是课程作者的笔误或恶意 PR, + // 静默接受只会把问题推到下游。 + if (!valid_id(ex.id)) { + std::println(stderr, + "d2x-buildtools-mcpp: 跳过 '{}' —— 练习 id 只允许 [A-Za-z0-9._-]", + ex.file.string()); + return; + } found.push_back(std::move(ex)); }; diff --git a/d2x/buildtools/mcpp/tests/e2e.sh b/d2x/buildtools/mcpp/tests/e2e.sh index c541817..4fa65a7 100755 --- a/d2x/buildtools/mcpp/tests/e2e.sh +++ b/d2x/buildtools/mcpp/tests/e2e.sh @@ -44,9 +44,14 @@ for line in "${LINES[@]}"; do id=$(printf '%s' "$line" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) file=$(printf '%s' "$line" | grep -o '"files":\["[^"]*"' | head -1 | cut -d'"' -f4) - rel="${file#"$REPO_ROOT"/}" # dslings/cpp11/xx.cpp - sol="solutions/${rel#dslings/}" # solutions/cpp11/xx.cpp - sol="${sol#en/}" # en 目录共用同一份参考答案 + # en/ 必须先剥,再拼 solutions/ —— 顺序反了的话 sol 已经以 "solutions/" + # 开头,`${sol#en/}` 匹配不到任何东西,是个静默 no-op,结果所有英文练习 + # 都因为找不到 solutions/en/... 而被 SKIP,测试全绿却一个都没验。 + # 这正是本脚本头部注释里说要防的那种「空转」。 + rel="${file#"$REPO_ROOT"/}" # dslings[/en]/cpp11/xx.cpp + rel="${rel#dslings/en/}" # en 镜像共用同一份参考答案 + rel="${rel#dslings/}" # cpp11/xx.cpp + sol="solutions/${rel}" # solutions/cpp11/xx.cpp # 1) 未完成态必须不通过 got=$(outcome_of "$id") @@ -71,4 +76,13 @@ done echo echo "==> 参考答案通过 $pass · 失败 $fail · 跳过 $skipped" + +# 防空转:一个参考答案都没验到时必须红,而不是「0 失败」蒙混过关。 +# 旧 CI 就是这么绿了很久的 —— 它只挑 -ref 目标,而 solutions/ 早被注释掉, +# 循环一次都没进,job 照样退出 0。 +if [ "$pass" -eq 0 ]; then + echo "FAIL: 没有验证到任何参考答案 —— 测试本身失效了,不是「全部通过」" + exit 1 +fi + [ "$fail" -eq 0 ] diff --git a/dslings/harness/include/d2x/cpp/common.hpp b/dslings/harness/include/d2x/cpp/common.hpp index 3b2a4bb..b420cbe 100644 --- a/dslings/harness/include/d2x/cpp/common.hpp +++ b/dslings/harness/include/d2x/cpp/common.hpp @@ -51,10 +51,10 @@ inline std::string show(const T& v) { __FILE__, __LINE__); \ if (!d2x_ok_) {\ HONLY_LOGW("❌ | %s == %s (%s == %s)", \ - #a, #b, std::to_string(a).c_str(), std::to_string(b).c_str()); \ + #a, #b, d2x::detail::show(a).c_str(), d2x::detail::show(b).c_str()); \ } else {\ HONLY_LOGI_P("✅ | %s == %s (%s == %s)", \ - #a, #b, std::to_string(a).c_str(), std::to_string(b).c_str()); \ + #a, #b, d2x::detail::show(a).c_str(), d2x::detail::show(b).c_str()); \ } \ } diff --git a/solutions/cpp11/04-rvalue-references.cpp b/solutions/cpp11/04-rvalue-references.cpp index 7686923..8becf04 100644 --- a/solutions/cpp11/04-rvalue-references.cpp +++ b/solutions/cpp11/04-rvalue-references.cpp @@ -13,6 +13,7 @@ struct Object; static Object * object_address = nullptr; +static int move_ctor_calls = 0; // 移动构造被调用的次数, 供断言检查 struct Object { int data = 0; @@ -21,7 +22,7 @@ struct Object { object_address = this; } Object(const Object&) { std::cout << "Object(const Object&):" << this << std::endl; } - Object(Object&&) { std::cout << "Object(Object&&):" << this << std::endl; } + Object(Object&&) { ++move_ctor_calls; std::cout << "Object(Object&&):" << this << std::endl; } ~Object() { std::cout << "~Object():" << this << std::endl; } }; @@ -29,8 +30,13 @@ int main() { // 关闭编译器优化 { std::cout << "----> 临时对像 - 右值1" << std::endl; Object(); - std::cout << "----> 临时对像 - 右值2" << std::endl; - Object obj = Object(); + std::cout << "----> 临时对像 - 右值2(具名对象 + std::move)" << std::endl; + // 注意: 不能写 `Object obj = Object();` 来观察移动构造 —— + // C++17 起 prvalue 直接初始化目标, 保证复制省略, 连 + // -fno-elide-constructors 也无法让那次移动发生。 + // 从具名对象 std::move 才是标准无关的观察方式。 + Object named; + Object obj = std::move(named); (void)obj; std::cout << "--------代码可修改区域-开始--------" << std::endl; @@ -43,6 +49,9 @@ int main() { // 关闭编译器优化 objRef.data = 1; // 修改被延长生命周期的临时对象的值(不要直接改动这行代码) std::cout << "objRef.data = " << objRef.data << " - " << &objRef << std::endl; d2x_assert((&objRef == object_address)); + // 钉住移动构造确实发生过。教学漂移之所以能静默发生, 正是因为 + // 从前没有任何断言检查它 —— 输出少了一行, 没人发现。 + d2x_assert((move_ctor_calls >= 1)); } return 0; From becc8e4f7c27470307ec78c5bf3483d9ef95ec23 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Mon, 20 Jul 2026 00:54:09 +0800 Subject: [PATCH 05/25] =?UTF-8?q?fix(dslings):=2004-rvalue-references=20?= =?UTF-8?q?=E7=BB=83=E4=B9=A0=E4=BE=A7=E5=90=8C=E6=AD=A5=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E7=BB=99=20e2e=20=E5=8A=A0=E8=84=8F=E6=A0=91?= =?UTF-8?q?=E9=98=B2=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一次提交只带上了 solutions/ 侧 —— 练习侧的补丁被 e2e.sh 的清理钩子 还原掉了,我没复查就提交了。 清理钩子会把参考答案覆盖到练习上再还原,所以它天然会吃掉练习目录里未提交 的改动。这个陷阱已经咬过两次(一次丢了脚手架,一次丢了刚修好的练习), 所以加一道前置检查:练习目录不干净就拒绝运行,并列出是哪些文件。 宁可拒绝,也不能悄悄丢掉别人的工作。 --- d2x/buildtools/mcpp/tests/e2e.sh | 11 +++++++++++ dslings/cpp11/04-rvalue-references.cpp | 15 ++++++++++++--- dslings/en/cpp11/04-rvalue-references.cpp | 15 ++++++++++++--- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/d2x/buildtools/mcpp/tests/e2e.sh b/d2x/buildtools/mcpp/tests/e2e.sh index 4fa65a7..f889843 100755 --- a/d2x/buildtools/mcpp/tests/e2e.sh +++ b/d2x/buildtools/mcpp/tests/e2e.sh @@ -24,6 +24,17 @@ restore() { [ -e "$d" ] && git checkout -- "$d" 2>/dev/null || true done } + +# 本脚本会把参考答案覆盖到练习上再还原,所以运行前练习必须是干净的 —— +# 否则未提交的改动会被 restore 悄悄丢掉(作者踩过两次:一次丢了脚手架, +# 一次丢了刚修好的练习)。宁可拒绝运行,也不能吃掉别人的工作。 +if ! git diff --quiet -- "${EXERCISE_DIRS[@]}" 2>/dev/null; then + echo "拒绝运行:练习目录有未提交的改动,本测试会在结束时还原它们。" + echo "请先提交或 stash:" + git diff --stat -- "${EXERCISE_DIRS[@]}" | sed 's/^/ /' + exit 2 +fi + trap restore EXIT outcome_of() { # $1 = exercise id diff --git a/dslings/cpp11/04-rvalue-references.cpp b/dslings/cpp11/04-rvalue-references.cpp index 83660b2..b916830 100644 --- a/dslings/cpp11/04-rvalue-references.cpp +++ b/dslings/cpp11/04-rvalue-references.cpp @@ -21,6 +21,7 @@ struct Object; static Object * object_address = nullptr; +static int move_ctor_calls = 0; // 移动构造被调用的次数, 供断言检查 struct Object { int data = 0; @@ -29,7 +30,7 @@ struct Object { object_address = this; } Object(const Object&) { std::cout << "Object(const Object&):" << this << std::endl; } - Object(Object&&) { std::cout << "Object(Object&&):" << this << std::endl; } + Object(Object&&) { ++move_ctor_calls; std::cout << "Object(Object&&):" << this << std::endl; } ~Object() { std::cout << "~Object():" << this << std::endl; } }; @@ -37,8 +38,13 @@ int main() { // 关闭编译器优化 { std::cout << "----> 临时对像 - 右值1" << std::endl; Object(); - std::cout << "----> 临时对像 - 右值2" << std::endl; - Object obj = Object(); + std::cout << "----> 临时对像 - 右值2(具名对象 + std::move)" << std::endl; + // 注意: 不能写 `Object obj = Object();` 来观察移动构造 —— + // C++17 起 prvalue 直接初始化目标, 保证复制省略, 连 + // -fno-elide-constructors 也无法让那次移动发生。 + // 从具名对象 std::move 才是标准无关的观察方式。 + Object named; + Object obj = std::move(named); std::cout << "--------代码可修改区域-开始--------" << std::endl; @@ -51,6 +57,9 @@ int main() { // 关闭编译器优化 objRef.data = 1; // 修改被延长生命周期的临时对象的值(不要直接改动这行代码) std::cout << "objRef.data = " << objRef.data << " - " << &objRef << std::endl; d2x_assert((&objRef == object_address)); + // 钉住移动构造确实发生过。教学漂移之所以能静默发生, 正是因为 + // 从前没有任何断言检查它 —— 输出少了一行, 没人发现。 + d2x_assert((move_ctor_calls >= 1)); } D2X_WAIT diff --git a/dslings/en/cpp11/04-rvalue-references.cpp b/dslings/en/cpp11/04-rvalue-references.cpp index 82da5ff..b8ac8fd 100644 --- a/dslings/en/cpp11/04-rvalue-references.cpp +++ b/dslings/en/cpp11/04-rvalue-references.cpp @@ -21,6 +21,7 @@ struct Object; static Object * object_address = nullptr; +static int move_ctor_calls = 0; // 移动构造被调用的次数, 供断言检查 struct Object { int data = 0; @@ -29,7 +30,7 @@ struct Object { object_address = this; } Object(const Object&) { std::cout << "Object(const Object&):" << this << std::endl; } - Object(Object&&) { std::cout << "Object(Object&&):" << this << std::endl; } + Object(Object&&) { ++move_ctor_calls; std::cout << "Object(Object&&):" << this << std::endl; } ~Object() { std::cout << "~Object():" << this << std::endl; } }; @@ -37,8 +38,13 @@ int main() { // Disable compiler optimization { std::cout << "----> Temporary object - rvalue 1" << std::endl; Object(); - std::cout << "----> Temporary object - rvalue 2" << std::endl; - Object obj = Object(); + std::cout << "----> Temporary object - rvalue 2 (named object + std::move)" << std::endl; + // NOTE: `Object obj = Object();` will NOT show a move constructor. + // Since C++17 a prvalue initialises the target directly (guaranteed + // copy elision), and -fno-elide-constructors cannot bring that move + // back. Moving from a named object is the standard-independent way. + Object named; + Object obj = std::move(named); std::cout << "--------Code modifiable area - Start--------" << std::endl; @@ -51,6 +57,9 @@ int main() { // Disable compiler optimization objRef.data = 1; // Modify the value of the extended lifetime temporary object (do not directly modify this line) std::cout << "objRef.data = " << objRef.data << " - " << &objRef << std::endl; d2x_assert((&objRef == object_address)); + // 钉住移动构造确实发生过。教学漂移之所以能静默发生, 正是因为 + // 从前没有任何断言检查它 —— 输出少了一行, 没人发现。 + d2x_assert((move_ctor_calls >= 1)); } D2X_WAIT From 58d3645e1055045f7a8eee5a55f2f1be966b247f Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Mon, 20 Jul 2026 01:29:44 +0800 Subject: [PATCH 06/25] =?UTF-8?q?fix(provider):=20=E6=8A=A5=E9=94=99?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=94=B9=E4=B8=BA=E7=9B=B8=E5=AF=B9=E4=BB=93?= =?UTF-8?q?=E5=BA=93=E6=A0=B9=EF=BC=8C=E5=8D=8F=E8=AE=AE=E9=87=8C=E4=BB=8D?= =?UTF-8?q?=E7=BB=99=E7=BB=9D=E5=AF=B9=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 学员看到的断言报错原先顶着一长串 /home/... 前缀 —— mcpp 传给编译器的是 绝对路径,__FILE__ 便是绝对的。生成的清单加上 -fmacro-prefix-map 让它 相对仓库根,噪声消失。 但协议要求绝对路径:d2x 靠 diagnostics.file 打开编辑器、监听文件变更。 所以在协议边界上还原成绝对。展示归展示,定位归定位。 --- d2x/buildtools/mcpp/src/main.cpp | 9 ++++++++- d2x/buildtools/mcpp/src/manifest.cppm | 14 ++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/d2x/buildtools/mcpp/src/main.cpp b/d2x/buildtools/mcpp/src/main.cpp index d2e710e..e099694 100644 --- a/d2x/buildtools/mcpp/src/main.cpp +++ b/d2x/buildtools/mcpp/src/main.cpp @@ -98,9 +98,16 @@ int cmd_check(const fs::path& root, std::string_view id) { auto report = d2x::runner::judge_run(ran.exit_code, result_file); + // 侧信道里的 file 来自 __FILE__,而生成的清单加了 -fmacro-prefix-map + // 让它相对仓库根(学员看到的报错才不会顶着一长串 /home/... 前缀)。 + // 但协议要求绝对路径 —— d2x 靠它开编辑器、靠它监听文件变更 —— 所以 + // 在协议边界上还原。展示归展示,定位归定位。 std::vector diags; for (const auto& f : report.failures) { - diags.push_back(d2x::emit::diagnostic(f.file, f.line, f.expr, f.expected, f.actual)); + auto abs = f.file.empty() || fs::path(f.file).is_absolute() + ? f.file + : (root / f.file).lexically_normal().string(); + diags.push_back(d2x::emit::diagnostic(abs, f.line, f.expr, f.expected, f.actual)); } d2x::emit::verdict(d2x::runner::to_string(report.outcome), "run", ran.exit_code, diags); diff --git a/d2x/buildtools/mcpp/src/manifest.cppm b/d2x/buildtools/mcpp/src/manifest.cppm index 9f4ef73..f34c7c8 100644 --- a/d2x/buildtools/mcpp/src/manifest.cppm +++ b/d2x/buildtools/mcpp/src/manifest.cppm @@ -39,7 +39,7 @@ std::string relative_to_member(const fs::path& repo_root, const fs::path& file) return std::format("{}/{}", kToRoot, rel); } -void write_package_header(std::ostream& out, std::string_view name) { +void write_package_header(std::ostream& out, std::string_view name, const fs::path& repo_root) { std::println(out, "# 由 d2x-buildtools-mcpp 生成,请勿手工编辑。"); std::println(out, "[package]"); std::println(out, "name = \"{}\"", name); @@ -49,6 +49,12 @@ void write_package_header(std::ostream& out, std::string_view name) { std::println(out, "[build]"); // 显式清空源码 glob:练习不是这个包的「源码」,它们只是各自 target 的入口。 std::println(out, "sources = []"); + // 让 __FILE__ 变成相对仓库根的路径。mcpp 传给编译器的是绝对路径, + // 于是 d2x_assert 的报错会印出一长串 /home/... 前缀,对学员是噪声。 + // 这只影响宏展开出来的字符串,不影响编译器自身诊断里的路径。 + // 前缀必须是编译器实际看到的绝对路径,不能用 kToRoot 那种相对形式。 + std::println(out, "cxxflags = [\"-fmacro-prefix-map={}/=\"]", + repo_root.generic_string()); std::println(out, ""); // 脚手架走正经的库依赖,而不是把仓库根塞进 include 搜索路径。 // 后者会让练习能 #include 仓库里任何文件,是个隐患。 @@ -96,7 +102,7 @@ export void write_full(const fs::path& repo_root, const std::vector& a std::vector members; for (const auto& [name, list] : by_member) { std::ostringstream buf; - write_package_header(buf, name); + write_package_header(buf, name, repo_root); for (const auto* ex : list) write_target(buf, repo_root, *ex); write_if_changed(root / name / "mcpp.toml", buf.str()); members.push_back(name); @@ -105,7 +111,7 @@ export void write_full(const fs::path& repo_root, const std::vector& a // _current 也是这个 workspace 的成员,占位清单先写空壳 members.push_back("_current"); write_if_changed(root / "_current" / "mcpp.toml", - [&] { std::ostringstream b; write_package_header(b, "_current"); return b.str(); }()); + [&] { std::ostringstream b; write_package_header(b, "_current", repo_root); return b.str(); }()); std::ostringstream ws; std::println(ws, "# 由 d2x-buildtools-mcpp 生成,请勿手工编辑。"); @@ -124,7 +130,7 @@ export void write_full(const fs::path& repo_root, const std::vector& a // 切题 0.118s、切回 0.018s。 export void write_current(const fs::path& repo_root, const Exercise& ex) { std::ostringstream buf; - write_package_header(buf, "_current"); + write_package_header(buf, "_current", repo_root); write_target(buf, repo_root, ex); write_if_changed(build_dir(repo_root) / "_current" / "mcpp.toml", buf.str()); } From e68706e5f07d9962ae31582d56c0b0a37e0bd67e Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Mon, 20 Jul 2026 01:35:04 +0800 Subject: [PATCH 07/25] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20Provider=20?= =?UTF-8?q?=E4=B8=8E=E7=BB=83=E4=B9=A0=E8=84=9A=E6=89=8B=E6=9E=B6=E5=8F=82?= =?UTF-8?q?=E8=80=83=EF=BC=88=E7=9B=AE=E5=BD=95=E7=BA=A6=E5=AE=9A=E3=80=81?= =?UTF-8?q?=E5=88=A4=E5=AE=9A=E6=9C=BA=E5=88=B6=E3=80=81=E5=B7=B2=E7=9F=A5?= =?UTF-8?q?=E7=BC=BA=E5=8F=A3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-07-20-mcpp-provider-reference.md | 262 ++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 .agents/docs/2026-07-20-mcpp-provider-reference.md diff --git a/.agents/docs/2026-07-20-mcpp-provider-reference.md b/.agents/docs/2026-07-20-mcpp-provider-reference.md new file mode 100644 index 0000000..e41f029 --- /dev/null +++ b/.agents/docs/2026-07-20-mcpp-provider-reference.md @@ -0,0 +1,262 @@ +# d2mcpp Provider 与练习脚手架 参考 + +- 日期:2026-07-20 +- 分支:`feat/mcpp-provider` +- 配套调研:[`2026-07-19-mcpp-replace-xmake-research.md`](2026-07-19-mcpp-replace-xmake-research.md)(xmake → mcpp 的可行性论证与 rustlings/cargo 横向对照) +- d2x 侧协议规范:d2x 仓库 `.agents/docs/2026-07-20-d2x-architecture-reference.md` +- 本文定位:**当前实现的参考手册**——目录约定、生成物、判定机制、维护须知 + +--- + +## 1. 全景 + +xmake 已完全退役。构建链路是: + +``` +d2x(通用框架,零内置后端) + └─ Provider Protocol (NDJSON) ─→ d2x/buildtools/mcpp/ ← 本仓库自己的 Provider(C++26) + ├─ 目录约定发现练习 + ├─ 生成 mcpp workspace 清单 + ├─ 调 mcpp build/run + └─ 读侧信道判定通过 + ↓ + mcpp(未做任何改动) +``` + +### 提交进仓库的包 + +``` +mcpp.toml [workspace] members = ["d2x/buildtools/mcpp", "dslings/harness"] +d2x/buildtools/mcpp/ Provider,standard = "c++26" +dslings/harness/ 练习脚手架库,standard = "c++23" +``` + +### 生成物(gitignore) + +``` +.d2x/build/mcpp.toml 独立 workspace 根 +.d2x/build//mcpp.toml 该标准下全部 target —— 供 clangd +.d2x/build/_current/mcpp.toml 只含当前一题 —— 供 checker +.d2x/build/_current/result.ndjson 判定用侧信道 +.d2x/state.json 学员进度(d2x 写) +``` + +--- + +## 2. 引导:零脚本、Windows 安全 + +`.d2x.json`: + +```json +{ "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --" } +``` + +d2x 拼接后得到 `mcpp run -q -p d2x/buildtools/mcpp -- check `。**从仓库根执行、无需 cd、首次自动构建 Provider**,暖开销约 26ms。 + +d2x 从不 `chdir`(它在静态初始化时捕获 CWD),所以免 `cd` 是硬要求。注意 `mcpp` 的 `-p` 匹配的是**目录 basename 或完整相对路径**,不是包名。 + +### ⚠️ 嵌套 mcpp 与 `LD_LIBRARY_PATH` + +`mcpp run` 会把 `LD_LIBRARY_PATH` 指向它私有的 glibc 并注入子进程。Provider 再去 spawn 嵌套的 `mcpp` 时被迫加载错配的 glibc,**在动态链接器里段错误**(冷启动稳定复现,输出只剩 `:\t__vdso_time` 这样的 trace 残片)。 + +`runner.cppm` 在每次 spawn 前 `unsetenv("LD_LIBRARY_PATH")` 绕过;mcpp 会为它自己的子进程重新设置正确的值。 + +d2x 侧对同一问题早有相同处理(`platform.cppm`),仓库里还留着 `workaround_ld_library_path_issue` 分支——**这是 mcpp 的既有问题,值得单独向上游报**。 + +--- + +## 3. 练习发现:目录约定 + 就近指令 + +**没有声明文件。** 放一个 `.cpp` 进去就是全部工作。 + +rustlings 最贵的一课是 PR #1355:Rust edition 同时写在 `rustc` 参数和 `rust-project.json` 里,两边漂移酿成 bug。**任何独立声明文件都是第二套真相源。** 这里的真相只有两处,且都无法漂移:目录结构,和练习文件自己的头部注释。 + +### 目录布局 + +``` +dslings/hello-mcpp.cpp 入门练习(chapter = intro) +dslings//NN-topic-K.cpp zh 练习 +dslings/en//NN-topic-K.cpp en 练习(lang=en 时启用,与 zh 互斥) +solutions//NN-topic-K.cpp 参考答案(zh/en 共用同一份) +``` + +### id / order / chapter 的推导 + +| 文件 | id | chapter | order | +|---|---|---|---| +| `dslings/cpp11/00-auto-and-decltype-0.cpp` | `cpp11-00-auto-and-decltype-0` | `cpp11/00-auto-and-decltype` | 1100000 | +| `dslings/cpp11/03-trailing-return-type.cpp` | `cpp11-03-trailing-return-type` | `cpp11/03-trailing-return-type` | 1100300 | +| `dslings/hello-mcpp.cpp` | `hello-mcpp` | `intro` | -100000 | + +`order = std_rank × 100000 + 章节号 × 100 + 练习序号`。尾部 `-<数字>` 是序号,没有就当 0(仓库里 03/04 两章确实缺 `-0` 后缀,规则把它们归一化)。 + +### 每练习编译选项 + +写在练习文件自己的头部注释里(只扫前 40 行): + +```cpp +// d2x:cxxflags: -O0 -fno-elide-constructors +``` + +仓库现存用例:`04-rvalue-references`(观察移动)、`07-constexpr-0`(`-Wpedantic -Werror` 让 VLA 成为错误)。 + +### id 白名单 + +id 只允许 `[A-Za-z0-9._-]`,违反者**被拒绝并打印到 stderr**,不进入枚举。 + +因为 id 有两个危险去向:d2x 拼进 shell 命令、我们写进生成的 TOML(`[targets.]`)。在源头挡住比在两个下游各自转义更可靠——这类文件名本就是笔误或恶意 PR,与其想办法安全传递,不如让作者改名。 + +--- + +## 4. 双 member 布局 + +**硬约束:dslings 的练习默认就编译不过**(49 个带 `D2X_YOUR_ANSWER`),而 mcpp 的 `build` 和 `run` 都会先全量构建整个包——一个坏兄弟拖垮全部且零产物。实测: + +``` +member 含全部 3 题(后两题未做完)→ mcpp build -p → exit=1 ← 当前这题被拖挂 +只生成当前这一题 → 0.071s,exit=0 +``` + +**member 粒度(标准/特性/练习)本身不解决逐题隔离。** 所以分两个: + +| 目录 | 内容 | 用途 | +|---|---|---| +| `.d2x/build//` | 该标准下全部 target | clangd —— 即使多数练习编译不过,mcpp 仍生成完整 `compile_commands.json`,IDE 对每题都有正确编译参数 | +| `.d2x/build/_current/` | 只含要验证的那一题 | checker | + +实测动态改写清单代价极低:切题 0.118s、切回 0.018s,**fingerprint 目录始终只有 1 个**(改写 target 集合不会让缓存爆炸)。 + +### 其他生成细节 + +- `main` 用 `../../../` 逃逸出包根,**练习源文件原地不动** +- 脚手架走 `[dependencies] path` 依赖,而非把仓库根塞进 include 搜索路径(后者会让练习能 `#include` 仓库里任何文件) +- `-fmacro-prefix-map=/=` 让 `__FILE__` 相对仓库根,学员看到的报错不再顶着一长串 `/home/...` +- 所有清单**内容比对后才落盘**,不推进 mtime,保住 mcpp 的快速路径 +- `check` 会先 `write_full` 再 `write_current`——全新仓库上学员直接跑 `d2x checker` 时根清单还不存在,否则 mcpp 报退出码 2 + +--- + +## 5. 判定机制 + +### 侧信道,不是扫 stdout + +判定原先 100% 是带内信号:断言失败**并不改变退出码**(实测 exit=0),所以只能扫 stdout 找 `❌`。这会误判——一个断言全过的正确解答,只要在说明文字里打了个 `❌` 就被判失败(已复现)。反过来,输出截断或 Windows 控制台代码页不对,判定同样失效。 + +现在 stdout 回归「给人看」,判定走侧信道: + +```jsonc +{"kind":"assert","ok":false,"expr":"a == b","expected":"1","actual":"2", + "file":"dslings/cpp11/xx.cpp","line":33} +{"kind":"wait","file":"dslings/cpp11/xx.cpp","line":40} +``` + +**逐条追加而非退出时统一写** —— 练习段错误时,崩溃之前的断言结果照样保留。 + +`D2X_RESULT_FILE` 环境变量未设置时 harness 只打印不写文件:**学员直接跑二进制零摩擦**。 + +### 判定顺序 + +| 侧信道内容 | Verdict | +|---|---| +| 有 `ok:false` | `fail` + 每条失败转一个 `Diagnostic` | +| 无失败但退出码非 0 | `fail` | +| 无失败、有 `wait` | `blocked` | +| **文件不存在**(练习没用 harness) | 退回「编译通过 + 退出 0 = `pass`」 | + +最后一条让 **harness 自动变成可选的**,不需要额外机制:纯观察型练习(现有 18 个只用 `D2X_WAIT`)可以写成零依赖的纯 C++ 文件,学员能原样拷进 Compiler Explorer。 + +### 路径:展示 vs 定位 + +侧信道里的 `file` 因 `-fmacro-prefix-map` 是相对路径(学员看着舒服),但协议要求绝对路径(d2x 靠它开编辑器、监听变更)。**在协议边界上还原成绝对。** + +--- + +## 6. 练习脚手架 `dslings/harness/` + +按库设计,目前住在本仓库,将来可整体抽成独立 mcpp 包给其他 C++ 课程复用。 + +### 两条路径不等价 + +| 设施 | `#include ` | `import d2x.harness;` | +|---|---|---| +| 断言 | `d2x_assert` / `d2x_assert_eq` 宏 | `d2x::check` / `check_eq`,`source_location` 自动带 file/line | +| 路障 | `D2X_WAIT` | `d2x::wait()` | +| 填空占位 | `D2X_YOUR_ANSWER` | **无等价物** | + +**宏无法跨模块导出**,这是 C++ 的硬约束而非疏漏。`D2X_YOUR_ANSWER` 必须展开为空才能制造编译错误,本质就是宏。**cpp20/cpp23 等模块化章节需要另设填空约定——这是个尚未决策的缺口。** + +`include/` 下保持 `d2x/cpp/common.hpp` 这个既有路径,所以 104 个练习和书本一行都不用改。路径改名可以以后单独做。 + +### 维护须知 + +`d2x_assert_eq` 的**所有分支**都必须走 `d2x::detail::show()`,不能用裸 `std::to_string`——后者没有 `std::string` / `const char*` / scoped enum 的重载,一旦有练习比较这些类型就会硬编译失败。(曾经上报分支用了 `show()` 而日志分支没用,是个潜伏的地雷。) + +--- + +## 7. 测试 + +### `d2x/buildtools/mcpp/tests/e2e.sh` + +断言两件事,缺一不可:**每个练习未完成时不通过**、**每个参考答案放进去后通过**。等价于 rustlings 的 `cargo dev check --require-solutions`。 + +走的是 `d2x checker` 内部同一条 Provider 路径,所以绿灯等价于学员本地能跑通,而非另一条平行路径。 + +**两道防线:** + +1. **防空转**——`pass == 0` 时直接判失败。旧 CI 就是这么绿了很久的:它只挑 `-ref` 目标,而 `solutions/` 早被注释掉,循环一次都没进,job 照样退出 0;第 60 行的 `wc -l` 对空串还打印 "Found 1 reference targets" 把空转掩盖了。同类问题在本脚本自己身上也犯过一次(`${sol#en/}` 剥离顺序错,导致所有英文答案静默 SKIP)。 + +2. **脏树检查**——练习目录有未提交改动时**拒绝运行**并列出文件。脚本会把参考答案覆盖到练习上再还原,天然会吃掉未提交的改动,这个陷阱咬过两次。 + +### 当前状态 + +| 项 | 结果 | +|---|---| +| Provider 枚举 | 52 题(zh:1 + 49 + 2;en 同样 52) | +| 端到端 zh | 51/51 参考答案通过 | +| 端到端 en | 51/51 参考答案通过 | +| d2x session 单测 | 28/28 | +| 事件流 | 纯 JSON,0 污染 | +| 注入防护 | 恶意文件名被拒绝,命令未执行 | + +--- + +## 8. C++ 标准与教学漂移 + +**所有练习按 `c++23` 编译。** mcpp 目前硬拒 `c++11/14/17/20`(`src/manifest/types.cppm` 的白名单),本方案选择不改 mcpp 上游。因此 `cppNN/` 目录现在表示**特性引入于哪个标准**,不再改变编译参数。 + +### 已知代价 + +`04-rvalue-references` 的移动构造教学点。C++17 起 prvalue 直接初始化目标(保证复制省略),`-fno-elide-constructors` 再也无法让 `Object obj = Object();` 产生移动构造。 + +**修法:** 改成从具名对象 `std::move`(任何标准下都必然调用移动构造),并补一条 `d2x_assert(move_ctor_calls >= 1)`。 + +**更重要的是那条断言。** 漂移之所以能静默发生,正是因为从前没有任何断言检查它——输出少一行没人发现。现在它是 CI 可检测的事实。 + +> **教训:教学点如果没有断言覆盖,它的消失就是不可见的。** 新增练习时应问:这一课的核心观测点,有断言在守着吗? + +量化过的风险面:51 个参考答案在「原标准 vs c++23」下逐一编译并 diff 运行输出,**50 个完全一致,仅此 1 个漂移**。但随着课程往 cpp17/20 扩写,这类静默漂移只会变多。 + +--- + +## 9. 撰稿流程的变化 + +`.agents/skills/d2mcpp-authoring/` 已同步更新: + +- **练习不再需要注册**——放进 `dslings//` 即被目录约定发现 +- 新增 `` 章节只需建目录 + 改两个 `SUMMARY.md`,无构建文件接线 +- 每练习编译选项改为文件头部 `// d2x:cxxflags:` 指令 +- 验证命令改为 `mcpp run -q -p d2x/buildtools/mcpp -- check ` 或整体 `bash d2x/buildtools/mcpp/tests/e2e.sh` + +--- + +## 10. 已知缺口 + +| 缺口 | 影响 | +|---|---| +| **模块化练习的填空占位符无约定** | `D2X_YOUR_ANSWER` 是宏,跨不了模块边界;cpp20/cpp23 章节需先决策 | +| **macOS / Windows 从未验证** | `_popen`、`_putenv_s` 的 `#ifdef` 分支全是纸面推断 | +| **新 CI 从未真跑过** | workflow 是手写的,`xlings install -y` 在 CI 环境能否装上 mcpp 未验证 | +| **`diagnostics` 只覆盖运行期断言** | 编译错误尚未解析成结构化诊断 | +| **Provider 侧无单测** | discovery 的 id/order/chapter 推导、manifest 生成都只被端到端间接覆盖 | +| **`hello-mcpp` 无参考答案** | e2e 只能 SKIP 它,入门练习实际未被验证 | +| **`solutions/` 无 en 镜像** | 设计如此(共用一份),但意味着 en 专有的措辞错误无法被答案校验发现 | From 70e88cd6a4eea588d7323396943c1812eb9f8d46 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 20:50:06 +0800 Subject: [PATCH 08/25] =?UTF-8?q?docs:=20=E7=BB=83=E4=B9=A0=E5=8D=B3?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=87=8D=E8=AE=BE=E8=AE=A1=E7=A8=BF=EF=BC=88?= =?UTF-8?q?=E5=8F=8C=E5=85=A5=E5=8F=A3=E3=80=81=E6=97=A0=E5=AE=8F=20harnes?= =?UTF-8?q?s=E3=80=81mcpp=20=E4=B8=8A=E6=B8=B8=E4=B8=89=E6=94=B9=E5=8A=A8?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-07-23-exercises-as-tests-design.md | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 .agents/docs/2026-07-23-exercises-as-tests-design.md diff --git a/.agents/docs/2026-07-23-exercises-as-tests-design.md b/.agents/docs/2026-07-23-exercises-as-tests-design.md new file mode 100644 index 0000000..0953517 --- /dev/null +++ b/.agents/docs/2026-07-23-exercises-as-tests-design.md @@ -0,0 +1,260 @@ +# 练习即测试:d2mcpp 架构重设计(设计稿) + +- 日期:2026-07-23 +- 状态:**设计已讨论定稿,未实现** +- 前置阅读: + - [`2026-07-20-mcpp-provider-reference.md`](2026-07-20-mcpp-provider-reference.md)(当前实现,本设计将取代其中的清单生成与判定部分) + - d2x 仓库 `.agents/docs/2026-07-20-d2x-architecture-reference.md`(Provider 协议,本设计不改协议) +- 本文定位:第二轮重设计的**决策记录与实施蓝图**——双入口定位、练习即 tests/、无宏 harness、mcpp 上游改动清单 + +--- + +## 1. 目标与定位 + +### 双入口 + +| 入口 | 体验 | 谁负责 | +|---|---|---| +| `d2x checker` | **闯关游戏**:顺序推进、进度持久化、将来的随机抽题/错题重练/游戏化呈现 | d2x | +| `mcpp test` / `mcpp run` | **真实工程**:cd 进标准目录直接用 mcpp,进度表就是测试结果,零生成物零魔法 | d2mcpp + mcpp | + +两条入口驱动**同一份练习、同一条判定链路**,不存在平行路径。 + +### 责任划分(重申并收紧) + +- **d2x**:拥有学习循环和它的呈现。判定信号拿去做进度显示还是做成游戏,d2mcpp 不关心也感知不到。将来的「随机出题」等策略是 d2x 会话层的事——协议本就是 `check ` 按需判定,**协议与 d2mcpp 均无需改动**。若将来需要「同一题随机参数」,协议预留 `check --seed N` 扩展位,现在不做。 +- **d2mcpp**:提供关卡(练习内容)+ 判定事实(编译过没过、断言挂没挂、路障拆没拆)。与 mcpp 和模块化深度绑定:harness 是纯 C++ 模块,判定复用 `mcpp test`。 +- **mcpp**:通用构建/测试工具。本设计需要它补三个**通用**能力(§4),均为 cargo/ctest 已验证的形态,不含任何 d2x 定制逻辑。 + +### 教学标准约定(维持既有决策) + +所有练习统一按 **c++23** 编译;`cppNN/` 表示**特性引入于哪个标准**,内容只用该标准的特性,不改变编译参数。教学点必须有断言守着(防静默漂移,见 provider-reference §8)。 + +--- + +## 2. 方案对比与结论 + +| 方案 | 形态 | 结论 | +|---|---|---| +| **A. 标准即工程,练习即 tests/** | `cpp11/` 是真实 mcpp 工程,练习是 `tests/<章节>/<序号>.cpp` | **采纳** | +| B. 章节即工程 | 每章一个 mcpp 工程,workspace 挂 30+ member | 否。频繁 cd + 30 多个 mcpp.toml 纯属维护负担;「章节」用 tests/ 子目录即可表达;仅当章节需要各自不同依赖时才值这个价(现状 52 题里为零) | +| C. 不改上游(现状延续) | Provider 继续生成 `_current` 清单 | 否。原生体验只能到「mcpp run 单题」,拿不到「mcpp test = 进度表」这个最贴合闯关直觉的形态 | + +方案 A 的前提是 mcpp 上游三改动(§4)。实验已证实当前行为不可用:**一个编译不过的测试 → 整体 `error: build failed`,零测试执行**(实测 mcpp v0.0.99,三个测试一好一运行错一编译错)。而「填空未填 = 编译不过」是练习的常态。 + +--- + +## 3. 目录布局 + +``` +d2mcpp/ + mcpp.toml workspace members = [harness, cpp11, cpp14, ...] + harness/ 独立 mcpp 包(standard c++23,纯模块,无宏) + cpp11/ + mcpp.toml 真实工程:standard = "c++23",[dependencies] harness (path) + tests/ + 00-auto-and-decltype/0.cpp + 00-auto-and-decltype/1.cpp + 01-nullptr/0.cpp + ... + cpp14/ ...同构 + solutions/ + cpp11/00-auto-and-decltype/0.cpp 结构镜像 tests/(zh/en 共用一份,维持现状) +``` + +- 章节 = tests/ 下的子目录(`mcpp test` 本就递归收集 `tests/**/*.cpp`)。 +- id 推导沿用目录约定:`cpp11/tests/00-auto-and-decltype/0.cpp` → `cpp11-00-auto-and-decltype-0`,order 公式不变(std_rank × 100000 + 章节 × 100 + 序号)。id 白名单 `[A-Za-z0-9._-]` 在源头拒绝,理由不变(注入纵深防御)。 +- `dslings/` 目录退役;`hello-mcpp` 入门练习移入独立的 `intro/` 工程(同构,一个测试),并补参考答案(闭掉「e2e 只能 SKIP 它」的缺口)。 +- **`.d2x/build/` 不复存在**:无生成清单、无 `_current` 改写。`.d2x/state.json`(d2x 写)保留。 +- clangd:由 mcpp 为 tests/ 生成的 `compile_commands.json` 覆盖(实施时验证:练习编译不过时条目是否仍生成——若否,此项是上游第四个小改动)。 + +### en 练习 + +维持「zh/en 互斥启用」语义。en 练习放 `cpp11/tests-en/` 还是独立 `cpp11-en/` 工程,实施时按 mcpp 对非常规测试目录的支持度定(倾向前者 + mcpp.toml 可配 tests 路径;这也是通用能力)。**本条是实施期待定项,不影响架构。** + +--- + +## 4. mcpp 上游改动清单 + +三个改动,全部是通用能力,d2mcpp 只是第一个消费者。 + +### 4.1 逐测试编译隔离 + +`tests/` 下每个 `.cpp` 本来就是独立二进制 target。改动后:单个测试编译失败 → **该测试**标记 fail(reason = compile),其余照常编译运行;整体退出码非 0 但不再是「build failed 零执行」。ctest/meson 的既有行为;「写坏一个测试导致其他测试全跑不了」对任何 mcpp 用户都是缺陷。 + +**验收**:一好一运行错一编译错的三测试工程,`mcpp test` 三个都有结果,好的那个真的执行了。 + +### 4.2 测试过滤 + +`mcpp test `,按测试名(tests/ 下相对路径去扩展名)子串或 glob 过滤。对齐 `cargo test `。 + +**验收**:`mcpp test 00-auto` 只编译运行匹配的测试;Provider 的 `check ` 走 `mcpp test --filter <精确名>`。 + +### 4.3 机器可读结果 `--message-format json` + +每测试一行 NDJSON: + +```jsonc +{"test":"00-auto-and-decltype/0", + "status":"pass|compile_fail|run_fail", + "exit_code":0, "signal":null, + "compile_output":"...编译器原文...", + "run_output":"...合并的 stdout+stderr..."} +``` + +**必须区分错误层级**:包级构建失败(harness 或 lib target 挂了)≠ 测试级编译失败。包级失败单独一条 `{"error":"package", ...}`,否则 52 题全标红会把「课程基础设施坏了」误报成「你全做错了」(对应 d2x 侧「describe 失败 = Provider 挂了,而非没有练习」原则)。 + +对齐 `cargo --message-format=json`;任何 CI/IDE 集成都需要。 + +### 顺带修复 + +嵌套 mcpp 的 `LD_LIBRARY_PATH` 段错误在上游正式修掉(mcpp spawn 子进程时不让私有 glibc 路径泄漏给非 mcpp 二进制),随后删除 d2mcpp `runner.cppm` 与 d2x `platform.cppm` 两处 `unsetenv` workaround。 + +### 待验证的第四项(小) + +练习编译不过时 `compile_commands.json` 是否仍含该条目(clangd 依赖);若否,补之。 + +--- + +## 5. 判定链路 + +``` +harness(运行期事实) → mcpp test(编译/退出码事实) → Provider(合并成 verdict) → d2x(呈现,d2mcpp 不关心) +``` + +### 三个核心要素的唯一事实来源 + +| 要素 | 来源 | 说明 | +|---|---|---| +| 当前判定的练习文件 | **Provider 的目录约定**(不经过 mcpp) | 发现阶段建立 id ↔ 绝对路径映射;mcpp JSON 里的测试名仅用于对账 | +| 编译期错误 | mcpp JSON(`status:compile_fail` + `compile_output` 原文) | 原文转协议 `output` 事件;`unknown type name 'D2X_YOUR_ANSWER'` 指着要填的行,是教学主通道 | +| 运行期错误 | mcpp JSON(退出码/信号)× 侧信道(断言详情/wait) | 侧信道逐条追加写,崩溃前的记录保得住 | + +### 判定顺序(Provider 内,语义与旧 `judge_run` 一致) + +| 观察 | Verdict | +|---|---| +| mcpp 报 `compile_fail` | `fail` + 编译输出 | +| 侧信道有 `ok:false` | `fail` + 每条失败转一个 Diagnostic | +| 无断言失败但退出码非 0(纯崩溃) | `fail` + 运行输出 | +| 无失败、有 `wait` | `blocked` | +| 侧信道文件不存在(未用 harness 的纯观察题) | 退回「退出码 0 = pass」 | + +结构化的**编译期** diagnostics(file/line 进协议字段)仍是既有缺口,本设计不倒退也不顺带解决;将来经 `-fdiagnostics-format=json` 做独立增量。 + +--- + +## 6. 无宏 harness + +### 关键事实:填空占位符不需要是宏 + +实验对比(clang 21,c++23): + +| 方案 | 报错 | +|---|---| +| 宏展开为空(现状) | `use of undeclared identifier 'b1'` ——指着错误位置、级联报两次 | +| **裸标识符(新)** | `unknown type name 'D2X_YOUR_ANSWER'` ——正好指着要填的地方,只报一次 | + +所以 `D2X_YOUR_ANSWER` 保留拼写但**降格为纯约定**:不定义在任何地方、不需要任何头文件、在模块化练习里天然可用、拷进 Compiler Explorer 也成立。旧文档「宏跨不了模块边界,模块化章节填空无约定」这个缺口是**伪问题,就此关闭**。 + +### harness 包接口(`import d2x.harness;`) + +```cpp +export namespace d2x { + // 断言:source_location 自动带 file/line;同时打印人读输出(✅/❌ 逐条对照,教学的一部分) + // 并在 D2X_RESULT_FILE 设置时逐条追加写侧信道 + bool check(bool ok, std::string_view what = {}, + std::source_location loc = std::source_location::current()); + template + bool check_eq(const A& a, const B& b, std::string_view what = {}, + std::source_location loc = std::source_location::current()); + + // 路障:打印「读完后删掉这一行」提示并记入侧信道 + void wait(std::source_location loc = std::source_location::current()); +} +``` + +- **退出码机制**:首次调用任一 API 时注册 `atexit` 处理器;进程正常退出时若有失败断言或未拆的 wait → `_Exit(1)`。这让裸 `mcpp test` 不需要懂任何 d2x 概念就能显示对错——练习的 `main` 不需要 `return d2x::result()` 这类样板。 +- **`#expr` 字符串化的损失与补偿**:函数版拿不到表达式原文(c++23 无反射)。补偿:`what` 可选参数供作者写语义标签(`check_eq(a1, a2, "auto 与 decltype 推导应一致")`——教学价值高于表达式原文);不传时人读输出退化为值对照 + file:line,学习者看得到出错行。将来 c++26 反射可无侵入恢复原文。 +- 旧的 `d2x/cpp/common.hpp` 经典 include 路径、`honly_logger.hpp`、`D2X_YOUR_ANSWER`/`d2x_assert*`/`D2X_WAIT` 宏全部退役。纯观察型练习依旧可以零依赖(不 import 任何东西)。 +- harness 作为独立 mcpp 包按公共契约维护(本次重设计即冻结接口的时机),将来发 registry 供其他 C++ 课程复用。 + +### 侧信道 schema v2 + +harness 与 Provider 之间的契约,本次显性化 + 加版本: + +```jsonc +{"v":2,"kind":"assert","ok":false,"what":"...","expected":"1","actual":"2", + "file":"cpp11/tests/00-auto-and-decltype/0.cpp","line":38} +{"v":2,"kind":"wait","file":"...","line":45} +``` + +- `D2X_RESULT_FILE` 未设置时只打印不写文件(学习者直接跑二进制零摩擦),语义不变。 +- 逐条追加而非退出时统一写(段错误保留崩溃前记录),语义不变。 +- 写端负责完整 JSON 转义(`"` `\` 控制字符);读端(Provider)字段固定、无嵌套,仍可免 JSON 库。 +- `file` 按编译器给的路径记录,**Provider 在协议边界统一还原为绝对路径**(d2x 靠它开编辑器/监听)。`-fmacro-prefix-map` 不再需要——source_location 在真实工程目录下编译,路径本来就正常。 + +--- + +## 7. Provider 瘦身 + +| 现状 | 新形态 | +|---|---| +| 生成 `.d2x/build/` 三套清单 + `_current` 改写舞蹈 | **全部删除** | +| 自己 popen `mcpp build/run` 并解析 wait status | `mcpp test --filter --message-format json` 一次调用 | +| 判定 = 自己读侧信道 + 自己算退出码 | 判定 = 合并 mcpp JSON × 侧信道(§5 顺序表) | +| 发现 = 扫 `dslings/` | 发现 = 扫 `cpp*/tests/`(约定同构,公式不变) | + +保留:id 白名单、`describe/exercises/check` 三动词、协议事件格式(对 d2x **完全透明**,d2x 零改动)。 + +### 每练习编译选项 + +文件头 `// d2x:cxxflags:` 指令在 tests/ 模式下无法由 Provider 注入(编译由 mcpp 统一驱动)。两个候选:mcpp.toml 支持 per-test 覆盖(通用能力,meson 有先例),或 mcpp 认文件头指令(特化,倾向否)。现存仅 2 题需要(`04-rvalue-references`、`07-constexpr-0`)。**实施期决策项**;若 mcpp.toml 方案成立则指令退役。 + +--- + +## 8. e2e 验证新形态 + +不变的两条硬断言:**每题未完成时不通过、每份参考答案放进去后通过**。 + +- 实现简化:dirty-tree 防护 → 逐题「拷 solution 覆盖 → `mcpp test --filter ` → 还原」,或整体「全部覆盖 → `mcpp test`(此时应全绿)→ 还原」。后者一次构建验全部,优先。 +- 防空转(`pass == 0` 直接失败)与脏树检查两道防线**原样保留**(各自都咬过人,见 provider-reference §7)。 +- `intro/hello-mcpp` 补参考答案后进入 e2e,不再 SKIP。 + +--- + +## 9. 迁移影响面 + +| 项 | 工作量性质 | +|---|---| +| 52 题从 `dslings/` 迁到 `cpp*/tests/` + 宏改函数(`d2x_assert_eq(a,b)` → `d2x::check_eq(a,b)`、`D2X_WAIT` → `d2x::wait();`、`#include ` → `import d2x.harness;`) | 机械,可脚本化 + 人工过一遍 what 标签 | +| 书本(book/)里引用练习路径/代码片段 | 跟随更新,路径变更是主要部分 | +| solutions/ 同步迁移 | 机械 | +| `.agents/skills/d2mcpp-authoring/` 撰稿流程 | 重写「新增练习」章节:放文件进 `cpp*/tests/<章节>/` 即完成注册(约定不变),验证命令改为 `mcpp test ` | +| CI workflow | e2e 命令更新;mcpp 版本 pin 到含三改动的版本 | +| d2x 仓库 | **零改动**(协议不动);仅 smoke CI 的 d2mcpp 分支引用需协调 | + +### 实施顺序(跨仓库依赖) + +``` +mcpp 上游三改动(4.1→4.3,可并行)+ LD_LIBRARY_PATH 修复 → 发版 + → d2mcpp:harness 重写 → 目录迁移 → Provider 瘦身 → e2e/CI → 文档与撰稿技能 + → 与 d2x #31 / d2mcpp #84 的合并顺序问题一并协调(互相咬合,需同步合并 + d2x 发新版) +``` + +本设计建立在 #31/#84 两个 draft PR 之上(协议、三态、侧信道思想全部沿用),是它们的演进而非推翻。 + +--- + +## 10. 本设计关闭/遗留的缺口 + +| provider-reference §10 的缺口 | 状态 | +|---|---| +| 模块化练习填空占位符无约定 | **关闭**(伪问题:裸标识符即编译错误,且报错更好) | +| `hello-mcpp` 无参考答案 | **关闭**(迁入 intro/ 时补) | +| 新 CI 从未真跑过 | 已在 PR #84 上跑绿(2026-07-19),本设计后需再验 | +| macOS / Windows 从未验证 | 遗留;Provider 瘦身后 `_popen`/`_putenv_s` 面积大幅缩小,风险随之降低 | +| 编译错误未结构化 | 遗留;独立增量(`-fdiagnostics-format=json`) | +| Provider 无超时 | 遗留(d2x 侧缺口) | +| en 练习的目录形态 | 新增待定项(§3) | +| per-test 编译选项的 mcpp 承载方式 | 新增待定项(§7) | From bfd7f5b80869c115361c52a484d69508ed45e270 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 22:24:28 +0800 Subject: [PATCH 09/25] =?UTF-8?q?docs:=20=E7=BB=83=E4=B9=A0=E5=8D=B3?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=BF=81=E7=A7=BB=E8=AE=A1=E5=88=92=EF=BC=88?= =?UTF-8?q?=E8=AF=95=E7=82=B9=E5=85=88=E8=A1=8C=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/docs/2026-07-23-migration-plan.md | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .agents/docs/2026-07-23-migration-plan.md diff --git a/.agents/docs/2026-07-23-migration-plan.md b/.agents/docs/2026-07-23-migration-plan.md new file mode 100644 index 0000000..d55bfb6 --- /dev/null +++ b/.agents/docs/2026-07-23-migration-plan.md @@ -0,0 +1,29 @@ +# d2mcpp「练习即测试」迁移 — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 按 `2026-07-23-exercises-as-tests-design.md` 完成 d2mcpp 侧迁移:无宏 harness、`cpp*/tests/` 目录、Provider 瘦身为 `mcpp test --message-format json` 的薄壳,并用本地 musl mcpp 二进制端到端打通 `d2x checker`。 + +**Architecture:** 试点先行——先迁 cpp14(2 题)验证整条链(harness 模块导入、mcpp test 命名/隔离/JSON、Provider 判定合并、d2x checker),再脚本化批量迁移 cpp11×49 + en×52 + solutions×51。mcpp 依赖本地分支 `feat/test-isolation-json` 的 musl 静态二进制(`ls -dt mcpp/target/x86_64-linux-musl/*/bin/mcpp | head -1`),通过 PATH 前置注入。 + +**Tech Stack:** C++23 modules(harness/练习)、C++26(Provider)、bash(迁移脚本 + e2e)。 + +## Global Constraints + +- 分支:`feat/mcpp-provider`(d2mcpp)、`feat/exercise-framework-protocol`(d2x),均不推送,本地交付。 +- musl mcpp 注入方式:`export PATH="$SHIM:$PATH"`,SHIM 目录里只有 mcpp 符号链接。 +- 练习可见输出格式(`[HONLY LOGI]: - ✅ | ...`)**保持不变**——book 里引用了它,格式漂移=文档漂移。 +- 侧信道 v2:每行加 `"v":2`;assert 增可选 `"what"`;判定顺序与设计稿 §5 一致。 +- id/order/chapter 推导公式不变(`cpp11-00-auto-and-decltype-0`,order=rank×100000+章×100+序)。 +- Provider 协议事件(describe/exercise/stage/output/verdict/error)一个字段都不改——d2x 零改动是设计承诺。 +- e2e 两道防线(pass==0 防空转、脏树拒绝)原样保留。 + +## Tasks + +- [ ] **T1 harness/ 新包**:顶层 `harness/`,纯模块 `d2x.harness`(check/check_eq/wait,source_location,`what` 可选参),atexit 退出码(失败或 wait 未拆→`_Exit(1)`),侧信道 v2 内联进模块(report.hpp 内容并入,include 路径退役)。单测:临时 fixture 直接 mcpp test。 +- [ ] **T2 试点 cpp14**:`cpp14/mcpp.toml`(dep harness path)+ `cpp14/tests/00-generic-lambdas/{0,1}.cpp`(内容转换:include→import,宏→函数)+ solutions 镜像;根 workspace 更新;`mcpp test -p cpp14` 原生验证(未完成→fail,答案→pass)。 +- [ ] **T3 Provider 瘦身**:discovery 扫 `cpp*/tests`+`intro/tests`(en 前缀 `en/`);check = spawn `mcpp test --message-format json -p ` + 读侧信道 + 合并判定;manifest.cppm 删除;runner.cppm 换成 mcpp-test 解析器;unsetenv workaround 删(mcpp c1bf311 已根治)。 +- [ ] **T4 批量迁移**:脚本迁 cpp11×49 + en/×52 + intro(hello-mcpp)+ solutions;两道特殊 cxxflags 题改用 `[build] flags = [{glob=...}]`(先验证 glob 对 tests/ 生效,不生效则保留头部指令+Provider 透传······不,tests 由 mcpp 驱动,必须 toml 承载,验证是硬前提);删 dslings/。 +- [ ] **T5 e2e.sh 重写**:两次全量 `mcpp test`(pristine 全 fail + overlay 全 pass)×zh/en + Provider 协议冒烟(枚举 52、check 单题事件流);hello-mcpp 补答案入 e2e。 +- [ ] **T6 d2x 端到端**:构建 d2x(feat/exercise-framework-protocol,musl mcpp on PATH),`d2x checker` 跑通新链路;.d2x.json 不变。 +- [ ] **T7 文档收尾**:book 路径 sed、authoring skill、CI workflow(注明需 mcpp 下一发版)、参考文档更新记录、CHANGELOG 式提交。 From 59eea391c5c2ae4adf608dce6ca50e1979bb59f1 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 22:28:13 +0800 Subject: [PATCH 10/25] =?UTF-8?q?feat(harness+pilot):=20=E6=97=A0=E5=AE=8F?= =?UTF-8?q?=20harness=20=E5=8C=85=20+=20cpp14=20=E8=AF=95=E7=82=B9?= =?UTF-8?q?=E8=BF=81=E7=A7=BB(=E7=BB=83=E4=B9=A0=E5=8D=B3=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=93=BE=E8=B7=AF=E9=AA=8C=E8=AF=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cpp14/mcpp.toml | 11 ++ cpp14/tests/00-generic-lambdas/0.cpp | 55 ++++++ cpp14/tests/00-generic-lambdas/1.cpp | 65 +++++++ harness/mcpp.toml | 9 + harness/src/harness.cppm | 162 ++++++++++++++++++ mcpp.toml | 9 +- solutions/cpp14/00-generic-lambdas-0.cpp | 41 ----- solutions/cpp14/00-generic-lambdas/0.cpp | 41 +++++ .../1.cpp} | 19 +- 9 files changed, 355 insertions(+), 57 deletions(-) create mode 100644 cpp14/mcpp.toml create mode 100644 cpp14/tests/00-generic-lambdas/0.cpp create mode 100644 cpp14/tests/00-generic-lambdas/1.cpp create mode 100644 harness/mcpp.toml create mode 100644 harness/src/harness.cppm delete mode 100644 solutions/cpp14/00-generic-lambdas-0.cpp create mode 100644 solutions/cpp14/00-generic-lambdas/0.cpp rename solutions/cpp14/{00-generic-lambdas-1.cpp => 00-generic-lambdas/1.cpp} (75%) diff --git a/cpp14/mcpp.toml b/cpp14/mcpp.toml new file mode 100644 index 0000000..f1ab7f1 --- /dev/null +++ b/cpp14/mcpp.toml @@ -0,0 +1,11 @@ +# cpp14 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: +# mcpp test -p cpp14 全部练习(进度表) +# mcpp test -p cpp14 00-generic 只跑匹配的练习 +# 内容只用 cpp14 的特性;编译统一 c++23(cppNN 目录表示特性引入于哪个标准)。 +[package] +name = "cpp14" +version = "0.1.0" +standard = "c++23" + +[dependencies] +harness = { path = "../harness" } diff --git a/cpp14/tests/00-generic-lambdas/0.cpp b/cpp14/tests/00-generic-lambdas/0.cpp new file mode 100644 index 0000000..7a39550 --- /dev/null +++ b/cpp14/tests/00-generic-lambdas/0.cpp @@ -0,0 +1,55 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// file: cpp14/tests/00-generic-lambdas/0.cpp +// +// Exercise/练习: cpp14 | 00 - generic lambdas | 泛型 lambda +// +// Tips/提示: +// - lambda 参数使用 auto, 编译器为 operator() 生成隐式模板 +// - 同一个泛型 lambda 可以接受不同类型的参数 +// +// Docs/文档: +// - https://en.cppreference.com/w/cpp/language/lambda +// - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/00-generic-lambdas.md +// +// 练习交流讨论: http://forum.d2learn.org/category/20 +// +// Auto-Checker/自动检测命令: +// +// d2x checker generic-lambdas +// + +import std; +import d2x.harness; + +int main() { + + // 0. 简单泛型 lambda — identity + auto identity = [](D2X_YOUR_ANSWER x) { + return x; + }; + + d2x::check_eq(identity(42), 42, "identity(42) == 42"); + d2x::check(identity(std::string("hello")) == "hello", "identity(std::string(\"hello\")) == \"hello\""); + d2x::check_eq(identity(3.14), D2X_YOUR_ANSWER, "identity(3.14) == D2X_YOUR_ANSWER"); + + // 1. 泛型 lambda 做比较 + auto greater = [](auto a, auto b) { + return D2X_YOUR_ANSWER; + }; + + d2x::check(greater(5, 3), "greater(5, 3)"); + d2x::check(greater(2.5, 1.2), "greater(2.5, 1.2)"); + d2x::check(greater(std::string("z"), std::string("a")), "greater(std::string(\"z\"), std::string(\"a\"))"); + + // 2. 推导类型确认 + auto get_type_size = [](auto x) { + return sizeof(D2X_YOUR_ANSWER); + }; + + d2x::check_eq(get_type_size(42), sizeof(int), "get_type_size(42) == sizeof(int)"); + d2x::check_eq(get_type_size('c'), sizeof(char), "get_type_size('c') == sizeof(char)"); + + d2x::wait(); + return 0; +} diff --git a/cpp14/tests/00-generic-lambdas/1.cpp b/cpp14/tests/00-generic-lambdas/1.cpp new file mode 100644 index 0000000..2bc06a3 --- /dev/null +++ b/cpp14/tests/00-generic-lambdas/1.cpp @@ -0,0 +1,65 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// file: cpp14/tests/00-generic-lambdas/1.cpp +// +// Exercise/练习: cpp14 | 00 - generic lambdas | 泛型 lambda 与 STL 算法 +// +// Tips/提示: +// - 泛型 lambda 可复用于不同元素类型的容器, 同一个 lambda 传给多种 STL 算法 +// - 捕获的变量类型不变, 只有参数用 auto +// +// Docs/文档: +// - https://en.cppreference.com/w/cpp/language/lambda +// - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/00-generic-lambdas.md +// +// 练习交流讨论: http://forum.d2learn.org/category/20 +// +// Auto-Checker/自动检测命令: +// +// d2x checker generic-lambdas +// + +import std; +import d2x.harness; + +int main() { + + // 0. 同一个泛型 lambda 用于不同元素类型的容器排序 + std::vector v1 = {5, 1, 4, 2, 8}; + std::vector v2 = {3.1, 2.7, 8.5, 1.9}; + + auto desc = [](D2X_YOUR_ANSWER a, D2X_YOUR_ANSWER b) { + return a > b; + }; + + std::sort(v1.begin(), v1.end(), desc); + d2x::check_eq(v1[0], 8, "v1[0] == 8"); + d2x::check_eq(v1[4], 1, "v1[4] == 1"); + + std::sort(v2.begin(), v2.end(), desc); + d2x::check_eq(v2[0], D2X_YOUR_ANSWER, "v2[0] == D2X_YOUR_ANSWER"); + + // 1. 带捕获的泛型 lambda — find_if + int threshold = 3; + auto above = [threshold](auto x) { + return x > threshold; + }; + + auto it1 = std::find_if(v1.begin(), v1.end(), above); + d2x::check(*it1 == D2X_YOUR_ANSWER, "*it1 == D2X_YOUR_ANSWER"); + + auto it2 = std::find_if(v2.begin(), v2.end(), above); + d2x::check(*it2 == 8.5, "*it2 == 8.5"); + + // 2. 泛型 lambda 返回 lambda — 函数工厂 + auto make_multiplier = [](auto factor) { + return [factor](auto x) { return x * D2X_YOUR_ANSWER; }; + }; + + auto times2 = make_multiplier(2); + d2x::check_eq(times2(10), 20, "times2(10) == 20"); + d2x::check_eq(times2(0.5), 1.0, "times2(0.5) == 1.0"); + + d2x::wait(); + return 0; +} diff --git a/harness/mcpp.toml b/harness/mcpp.toml new file mode 100644 index 0000000..05ac43c --- /dev/null +++ b/harness/mcpp.toml @@ -0,0 +1,9 @@ +[package] +name = "harness" +version = "0.2.0" +description = "d2mcpp 练习脚手架:断言、路障、侧信道上报(纯模块,无宏)" +license = "Apache-2.0" +standard = "c++23" + +[targets.harness] +kind = "lib" diff --git a/harness/src/harness.cppm b/harness/src/harness.cppm new file mode 100644 index 0000000..40678a6 --- /dev/null +++ b/harness/src/harness.cppm @@ -0,0 +1,162 @@ +module; + +// C 运行时符号走全局模块片段:atexit/_Exit/getenv/fopen 一族 import std 不提供。 +#include +#include + +export module d2x.harness; + +import std; + +// d2mcpp 练习脚手架 —— 唯一路径,纯模块,零宏。 +// +// 填空占位符 D2X_YOUR_ANSWER 不在这里:它是**纯约定**,不定义在任何地方。 +// 一个未定义的标识符本身就是编译错误,而且报错正好指着要填的位置 +// (`unknown type name 'D2X_YOUR_ANSWER'`)——比旧宏展开为空后指着别处的 +// 级联报错更好。所以它不需要头文件、在模块化练习里天然可用、拷进 +// Compiler Explorer 也成立。 +// +// 判定信号走两条路,各有消费者: +// 进程退出码 —— 有失败断言或未拆的 wait() 时退出码变 1。这让裸 +// `mcpp test` 不需要懂任何 d2x 概念就能显示对错。 +// 侧信道 v2 —— D2X_RESULT_FILE 指定的 NDJSON 文件,Provider 读它 +// 把「为什么失败」变成结构化诊断、把 wait 区分成 blocked。 +// 未设置时不写文件:学员直接跑二进制零摩擦。 + +namespace d2x::detail { + +int g_failures = 0; +int g_waits = 0; +bool g_hooked = false; + +// 进程正常退出时,若有失败断言或未拆的路障,把退出码改成 1。 +// 只在这两种情况下覆盖——练习自己 return 非 0 时不动它。 +// _Exit 跳过后续清理,所以先冲刷 stdio。 +void exit_hook() { + if (d2x::detail::g_failures > 0 || d2x::detail::g_waits > 0) { + std::fflush(stdout); + std::fflush(stderr); + std::_Exit(1); + } +} + +void ensure_hook() { + if (!g_hooked) { + g_hooked = true; + std::atexit(exit_hook); + } +} + +inline std::string escape(std::string_view s) { + std::string out; + out.reserve(s.size() + 8); + for (unsigned char c : s) { + switch (c) { + case '"': out += "\\\""; break; + case '\\': out += "\\\\"; break; + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: + if (c < 0x20) out += std::format("\\u{:04x}", static_cast(c)); + else out += static_cast(c); + } + } + return out; +} + +// 每次都重新打开并追加。比持有 FILE* 慢,但换来两个性质: +// 进程异常终止不丢已写入的行;无需依赖静态析构顺序。 +inline void emit(const std::string& json_line) { + const char* path = std::getenv("D2X_RESULT_FILE"); + if (!path || !*path) return; // 学员直接运行:什么都不做 + + if (std::FILE* f = std::fopen(path, "a")) { + std::fputs(json_line.c_str(), f); + std::fputc('\n', f); + std::fclose(f); + } +} + +inline void report_assert(bool ok, std::string_view what, + const std::string& expected, const std::string& actual, + const char* file, int line) { + std::string out = "{\"v\":2,\"kind\":\"assert\",\"ok\":"; + out += ok ? "true" : "false"; + out += ",\"what\":\"" + escape(what) + "\""; + out += ",\"expected\":\"" + escape(expected) + "\""; + out += ",\"actual\":\"" + escape(actual) + "\""; + out += ",\"file\":\"" + escape(file ? file : "") + "\""; + out += ",\"line\":" + std::to_string(line) + "}"; + emit(out); +} + +inline void report_wait(const char* file, int line) { + std::string out = "{\"v\":2,\"kind\":\"wait\",\"file\":\""; + out += escape(file ? file : ""); + out += "\",\"line\":" + std::to_string(line) + "}"; + emit(out); +} + +inline std::string show_impl(const auto& v) { + if constexpr (requires { std::format("{}", v); }) return std::format("{}", v); + else return {}; +} + +} // namespace d2x::detail + +export namespace d2x { + +// 可见输出格式沿用旧宏时代的样子([HONLY LOGI]: - ✅ | ...)——book 里 +// 引用了这些输出,格式漂移就是文档漂移。`what` 是断言的语义标签(通常是 +// 表达式原文,由撰稿或迁移脚本填写),补上 c++23 无反射拿不到 #expr 的缺。 + +inline bool check(bool ok, std::string_view what = {}, + std::source_location loc = std::source_location::current()) { + d2x::detail::ensure_hook(); + if (!ok) ++d2x::detail::g_failures; + + auto label = what.empty() ? std::string_view("check") : what; + d2x::detail::report_assert(ok, label, "true", ok ? "true" : "false", + loc.file_name(), static_cast(loc.line())); + if (ok) std::print("\033[32m[HONLY LOGI]: - ✅ | {}\033[0m\n", label); + else std::print("\033[33m[HONLY LOGW]: {}:{} - ❌(error) | {}\033[0m\n", + loc.file_name(), loc.line(), label); + std::fflush(stdout); + return ok; +} + +template +inline bool check_eq(const A& a, const B& b, std::string_view what = {}, + std::source_location loc = std::source_location::current()) { + d2x::detail::ensure_hook(); + const bool ok = (a == b); + if (!ok) ++d2x::detail::g_failures; + + auto sa = d2x::detail::show_impl(a); + auto sb = d2x::detail::show_impl(b); + auto label = what.empty() ? std::format("{} == {}", sa, sb) : std::string(what); + + d2x::detail::report_assert(ok, label, sb, sa, + loc.file_name(), static_cast(loc.line())); + + if (ok) std::print("\033[32m[HONLY LOGI]: - ✅ | {} ({} == {})\033[0m\n", label, sa, sb); + else std::print("\033[33m[HONLY LOGW]: {}:{} - ❌ | {} ({} == {})\033[0m\n", + loc.file_name(), loc.line(), label, sa, sb); + std::fflush(stdout); + return ok; +} + +// 显式路障:学员读完说明、删掉这一行才算真正完成这一题。 +// 记录后照常返回(不中断程序)——后续断言还要跑;退出码由 exit_hook 收口, +// Provider 再根据侧信道把「只剩 wait」区分成 blocked。 +inline void wait(std::source_location loc = std::source_location::current()) { + d2x::detail::ensure_hook(); + ++d2x::detail::g_waits; + d2x::detail::report_wait(loc.file_name(), static_cast(loc.line())); + std::print("\033[33m[HONLY LOGW]: {}:{} - 🥳 Delete the d2x::wait() to continue...\033[0m\n", + loc.file_name(), loc.line()); + std::fflush(stdout); +} + +} // namespace d2x diff --git a/mcpp.toml b/mcpp.toml index 963815c..5b9a12b 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,4 @@ -# d2mcpp 根 workspace。 -# -# 这里只登记「提交进仓库」的包。生成物(.d2x/build/ 下按 C++ 标准分的 -# member、以及只含当前一题的 _current)自成一个独立 workspace,由 -# Provider 生成——不放进这里,避免「根清单引用尚未生成的成员」的循环。 +# d2mcpp 根 workspace。练习即测试:每个 C++ 标准一个真实工程, +# 练习是它的 tests/,mcpp test 直接就是进度表;d2x checker 驱动同一条链。 [workspace] -members = ["d2x/buildtools/mcpp", "dslings/harness"] +members = ["harness", "cpp14", "d2x/buildtools/mcpp", "dslings/harness"] diff --git a/solutions/cpp14/00-generic-lambdas-0.cpp b/solutions/cpp14/00-generic-lambdas-0.cpp deleted file mode 100644 index 473711b..0000000 --- a/solutions/cpp14/00-generic-lambdas-0.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// d2mcpp: https://github.com/mcpp-community/d2mcpp -// license: Apache-2.0 -// reference solution for: dslings/cpp14/00-generic-lambdas-0.cpp -// -// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 -// 教程练习入口: dslings/cpp14/00-generic-lambdas-0.cpp -// - -#include -#include - -int main() { - - // 0. 简单泛型 lambda — identity - auto identity = [](auto x) { - return x; - }; - - d2x_assert_eq(identity(42), 42); - d2x_assert(identity(std::string("hello")) == "hello"); - d2x_assert_eq(identity(3.14), 3.14); - - // 1. 泛型 lambda 做比较 - auto greater = [](auto a, auto b) { - return a > b; - }; - - d2x_assert(greater(5, 3)); - d2x_assert(greater(2.5, 1.2)); - d2x_assert(greater(std::string("z"), std::string("a"))); - - // 2. 推导类型确认 - auto get_type_size = [](auto x) { - return sizeof(x); - }; - - d2x_assert_eq(get_type_size(42), sizeof(int)); - d2x_assert_eq(get_type_size('c'), sizeof(char)); - - return 0; -} diff --git a/solutions/cpp14/00-generic-lambdas/0.cpp b/solutions/cpp14/00-generic-lambdas/0.cpp new file mode 100644 index 0000000..5ba5c2d --- /dev/null +++ b/solutions/cpp14/00-generic-lambdas/0.cpp @@ -0,0 +1,41 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp14/00-generic-lambdas-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp14/00-generic-lambdas-0.cpp +// + +import std; +import d2x.harness; + +int main() { + + // 0. 简单泛型 lambda — identity + auto identity = [](auto x) { + return x; + }; + + d2x::check_eq(identity(42), 42, "identity(42) == 42"); + d2x::check(identity(std::string("hello")) == "hello", "identity(std::string(\"hello\")) == \"hello\""); + d2x::check_eq(identity(3.14), 3.14, "identity(3.14) == 3.14"); + + // 1. 泛型 lambda 做比较 + auto greater = [](auto a, auto b) { + return a > b; + }; + + d2x::check(greater(5, 3), "greater(5, 3)"); + d2x::check(greater(2.5, 1.2), "greater(2.5, 1.2)"); + d2x::check(greater(std::string("z"), std::string("a")), "greater(std::string(\"z\"), std::string(\"a\"))"); + + // 2. 推导类型确认 + auto get_type_size = [](auto x) { + return sizeof(x); + }; + + d2x::check_eq(get_type_size(42), sizeof(int), "get_type_size(42) == sizeof(int)"); + d2x::check_eq(get_type_size('c'), sizeof(char), "get_type_size('c') == sizeof(char)"); + + return 0; +} diff --git a/solutions/cpp14/00-generic-lambdas-1.cpp b/solutions/cpp14/00-generic-lambdas/1.cpp similarity index 75% rename from solutions/cpp14/00-generic-lambdas-1.cpp rename to solutions/cpp14/00-generic-lambdas/1.cpp index c977d7d..61887e7 100644 --- a/solutions/cpp14/00-generic-lambdas-1.cpp +++ b/solutions/cpp14/00-generic-lambdas/1.cpp @@ -6,9 +6,8 @@ // 教程练习入口: dslings/cpp14/00-generic-lambdas-1.cpp // -#include -#include -#include +import std; +import d2x.harness; int main() { @@ -21,11 +20,11 @@ int main() { }; std::sort(v1.begin(), v1.end(), desc); - d2x_assert_eq(v1[0], 8); - d2x_assert_eq(v1[4], 1); + d2x::check_eq(v1[0], 8, "v1[0] == 8"); + d2x::check_eq(v1[4], 1, "v1[4] == 1"); std::sort(v2.begin(), v2.end(), desc); - d2x_assert_eq(v2[0], 8.5); + d2x::check_eq(v2[0], 8.5, "v2[0] == 8.5"); // 1. 带捕获的泛型 lambda — find_if int threshold = 3; @@ -34,10 +33,10 @@ int main() { }; auto it1 = std::find_if(v1.begin(), v1.end(), above); - d2x_assert(*it1 == 8); + d2x::check(*it1 == 8, "*it1 == 8"); auto it2 = std::find_if(v2.begin(), v2.end(), above); - d2x_assert(*it2 == 8.5); + d2x::check(*it2 == 8.5, "*it2 == 8.5"); // 2. 泛型 lambda 返回 lambda — 函数工厂 auto make_multiplier = [](auto factor) { @@ -45,8 +44,8 @@ int main() { }; auto times2 = make_multiplier(2); - d2x_assert_eq(times2(10), 20); - d2x_assert_eq(times2(0.5), 1.0); + d2x::check_eq(times2(10), 20, "times2(10) == 20"); + d2x::check_eq(times2(0.5), 1.0, "times2(0.5) == 1.0"); return 0; } From e2010f2ad65d563ef3f85e69bfd739c8c3615a42 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 22:31:40 +0800 Subject: [PATCH 11/25] =?UTF-8?q?feat(provider):=20=E7=98=A6=E8=BA=AB?= =?UTF-8?q?=E4=B8=BA=20mcpp=20test=20JSON=20=E8=96=84=E5=A3=B3=E2=80=94?= =?UTF-8?q?=E2=80=94=E6=B8=85=E5=8D=95=E7=94=9F=E6=88=90/=E8=87=AA?= =?UTF-8?q?=E5=BB=BA=E5=88=A4=E5=AE=9A/unsetenv=20workaround=20=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- d2x/buildtools/mcpp/src/discovery.cppm | 196 +++++++++++-------------- d2x/buildtools/mcpp/src/main.cpp | 84 ++++++----- d2x/buildtools/mcpp/src/manifest.cppm | 138 ----------------- d2x/buildtools/mcpp/src/runner.cppm | 185 +++++++++++++---------- 4 files changed, 246 insertions(+), 357 deletions(-) delete mode 100644 d2x/buildtools/mcpp/src/manifest.cppm diff --git a/d2x/buildtools/mcpp/src/discovery.cppm b/d2x/buildtools/mcpp/src/discovery.cppm index 7a35184..371eb45 100644 --- a/d2x/buildtools/mcpp/src/discovery.cppm +++ b/d2x/buildtools/mcpp/src/discovery.cppm @@ -1,9 +1,16 @@ -// 练习发现:目录约定 + 练习文件内的就近指令。 +// 练习发现:目录约定,别无其他。 // // 刻意不引入独立的声明文件(exercises.toml 之类)。rustlings 最贵的一课 // 是 PR #1355:edition 同时写在 rustc 参数和 rust-project.json 里,两边漂移 -// 酿成 bug。任何独立声明文件都是第二套真相源。这里的真相只有两处,且都 -// 无法漂移:目录结构,和练习文件自己的头部注释。 +// 酿成 bug。任何独立声明文件都是第二套真相源。这里的真相只有一处,且 +// 无法漂移:目录结构本身。 +// +// intro/tests/hello-mcpp.cpp → id: hello-mcpp (chapter: intro) +// cpp11/tests/00-auto-and-decltype/0.cpp → id: cpp11-00-auto-and-decltype-0 +// en/cpp11/tests/... lang=en 时启用,与 zh 互斥 +// +// 每个 / 是真实 mcpp 工程,练习就是它的 tests/ —— 学员可以绕过 d2x +// 直接 `mcpp test -p cpp11` 看进度表,Provider 走的是同一条路。 module; #include // stderr @@ -17,13 +24,13 @@ namespace fs = std::filesystem; namespace d2x::discovery { export struct Exercise { - std::string id; // cpp11-00-auto-and-decltype-0 - int order{}; // 显式顺序,不依赖字典序 - std::string title; // auto and decltype (0) - std::string chapter; // cpp11/00-auto-and-decltype - std::string std_dir; // cpp11 —— 决定它属于哪个 member - fs::path file; // 绝对路径 - std::vector cxxflags; // 来自 // d2x:cxxflags: 指令 + std::string id; // cpp11-00-auto-and-decltype-0(与旧布局完全一致,进度不丢) + int order{}; // 显式顺序,不依赖字典序 + std::string title; // auto and decltype (0) + std::string chapter; // cpp11/00-auto-and-decltype + std::string member; // workspace 成员:cpp11 / intro / en/cpp11 … + std::string test_name; // mcpp test 里的名字:00-auto-and-decltype/0 / hello-mcpp + fs::path file; // 绝对路径 }; // .d2x.json 里我们只关心 "lang"。写一个完整 JSON 解析器是杀鸡用牛刀, @@ -52,58 +59,21 @@ int std_rank(std::string_view dir) { auto [_, ec] = std::from_chars(rest.data(), rest.data() + rest.size(), n); if (ec == std::errc{}) return n; } - return -1; // 根目录下的 hello-mcpp -} - -// 从练习文件头部读 `// d2x:cxxflags: -O0 -fno-elide-constructors`。 -// 只扫前 40 行——指令属于文件头的元信息区,不该藏在代码中间。 -std::vector read_cxxflags(const fs::path& file) { - std::ifstream in(file); - if (!in) return {}; - - constexpr std::string_view kMarker = "d2x:cxxflags:"; - std::string line; - for (int n = 0; n < 40 && std::getline(in, line); ++n) { - auto pos = line.find(kMarker); - if (pos == std::string::npos) continue; - - std::vector flags; - std::istringstream rest(line.substr(pos + kMarker.size())); - for (std::string flag; rest >> flag; ) flags.push_back(flag); - return flags; - } - return {}; + return -1; // intro } -// 00-auto-and-decltype-0 -> { chapter_no=0, topic="auto-and-decltype", index=0 } -// 03-trailing-return-type -> { chapter_no=3, topic="trailing-return-type", index=0 } -// -// 尾部的 -<数字> 是练习序号;没有就当 0。注意 d2mcpp 现有命名里 -// 03/04 两章确实缺了 -0 后缀,这个规则把它们归一化掉。 -struct NameParts { int chapter_no; std::string topic; int index; }; +// 00-auto-and-decltype -> { chapter_no=0, topic="auto-and-decltype" } +struct ChapterParts { int chapter_no; std::string topic; }; -std::optional parse_stem(std::string_view stem) { - auto dash = stem.find('-'); +std::optional parse_chapter(std::string_view dir_name) { + auto dash = dir_name.find('-'); if (dash == std::string_view::npos) return std::nullopt; int chapter_no = 0; - auto head = stem.substr(0, dash); + auto head = dir_name.substr(0, dash); if (std::from_chars(head.data(), head.data() + head.size(), chapter_no).ec != std::errc{}) return std::nullopt; - - auto rest = stem.substr(dash + 1); - int index = 0; - auto last = rest.rfind('-'); - if (last != std::string_view::npos) { - auto tail = rest.substr(last + 1); - int parsed = 0; - auto [_, ec] = std::from_chars(tail.data(), tail.data() + tail.size(), parsed); - if (ec == std::errc{} && !tail.empty()) { - index = parsed; - rest = rest.substr(0, last); - } - } - return NameParts{chapter_no, std::string(rest), index}; + return ChapterParts{chapter_no, std::string(dir_name.substr(dash + 1))}; } std::string humanize(std::string_view topic) { @@ -112,13 +82,10 @@ std::string humanize(std::string_view topic) { return out; } -// 练习 id 直接来自文件名,而它有两个危险去向: +// 练习 id 直接来自目录/文件名,而它有两个危险去向: // 1. d2x 把它拼进一条 shell 命令(` check `) -// 2. 我们把它写进生成的 TOML(`[targets.]`) -// 带反引号、`]`、引号或换行的文件名能在任一处越界。 -// -// 在源头挡住比在两个下游各自转义更可靠 —— 课程里本就不该出现这种文件名, -// 与其想办法安全地传递它,不如明确拒绝并让作者改名。 +// 2. 我们把 test 名拼进 `mcpp test ` 命令行 +// 带反引号、引号或换行的名字能在任一处越界。在源头挡住比在下游转义更可靠。 export bool valid_id(std::string_view id) { if (id.empty()) return false; return std::ranges::all_of(id, [](unsigned char c) { @@ -127,72 +94,83 @@ export bool valid_id(std::string_view id) { }); } -// 扫描练习根目录。lang=zh 用 dslings/,lang=en 用 dslings/en/。 -export std::vector scan(const fs::path& repo_root, std::string_view lang) { - fs::path base = repo_root / "dslings"; - if (lang == "en") base /= "en"; - if (!fs::is_directory(base)) return {}; +// test 名多允许一个 '/'(章节目录分隔符),其余同 id 白名单。 +export bool valid_test_name(std::string_view name) { + if (name.empty()) return false; + return std::ranges::all_of(name, [](unsigned char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' + || c == '/'; + }); +} - std::vector found; +// 扫描一个成员工程的 tests/。std_dir 是去掉 en/ 前缀后的标准名(intro 为空串)。 +void scan_member(const fs::path& repo_root, const std::string& member, + const std::string& std_dir, std::vector& out) { + auto tests = repo_root / member / "tests"; + if (!fs::is_directory(tests)) return; - auto add = [&](const fs::path& file, const std::string& std_dir) { - auto stem = file.stem().string(); - auto parts = parse_stem(stem); + for (const auto& entry : fs::recursive_directory_iterator(tests)) { + if (!entry.is_regular_file() || entry.path().extension() != ".cpp") continue; + auto rel = fs::relative(entry.path(), tests); Exercise ex; - ex.file = fs::absolute(file).lexically_normal(); - ex.std_dir = std_dir; - ex.cxxflags = read_cxxflags(file); - + ex.file = fs::absolute(entry.path()).lexically_normal(); + ex.member = member; + ex.test_name = rel.parent_path().empty() + ? rel.stem().string() + : std::format("{}/{}", rel.parent_path().generic_string(), + rel.stem().string()); + + auto chapter_dir = rel.parent_path().filename().string(); + auto parts = chapter_dir.empty() ? std::nullopt : parse_chapter(chapter_dir); if (parts) { - ex.id = std_dir.empty() ? stem : std::format("{}-{}", std_dir, stem); - ex.chapter = std_dir.empty() - ? std::format("{:02}-{}", parts->chapter_no, parts->topic) - : std::format("{}/{:02}-{}", std_dir, parts->chapter_no, parts->topic); - ex.title = std::format("{} ({})", humanize(parts->topic), parts->index); - ex.order = std_rank(std_dir) * 100'000 + parts->chapter_no * 100 + parts->index; + int index = 0; + auto stem = rel.stem().string(); + std::from_chars(stem.data(), stem.data() + stem.size(), index); + ex.id = std::format("{}-{}-{}", std_dir, chapter_dir, stem); + ex.chapter = std::format("{}/{}", std_dir, chapter_dir); + ex.title = std::format("{} ({})", humanize(parts->topic), index); + ex.order = std_rank(std_dir) * 100'000 + parts->chapter_no * 100 + index; } else { - // 不符合 NN-topic 约定的(如根目录的 hello-mcpp.cpp) - ex.id = std_dir.empty() ? stem : std::format("{}-{}", std_dir, stem); - ex.chapter = std_dir.empty() ? "intro" : std_dir; - ex.title = humanize(stem); - ex.order = std_rank(std_dir) * 100'000; + // intro 之类不带章节目录的练习(tests/hello-mcpp.cpp) + ex.id = rel.stem().string(); + ex.chapter = "intro"; + ex.title = humanize(rel.stem().string()); + ex.order = -100'000; } - // 拒绝而不是转义:这类文件名是课程作者的笔误或恶意 PR, + // 拒绝而不是转义:这类名字是课程作者的笔误或恶意 PR, // 静默接受只会把问题推到下游。 - if (!valid_id(ex.id)) { + if (!valid_id(ex.id) || !valid_test_name(ex.test_name)) { std::println(stderr, - "d2x-buildtools-mcpp: 跳过 '{}' —— 练习 id 只允许 [A-Za-z0-9._-]", + "d2x-buildtools-mcpp: 跳过 '{}' —— 练习 id/名字 只允许 [A-Za-z0-9._/-]", ex.file.string()); - return; + continue; } - found.push_back(std::move(ex)); - }; - - // 根目录下的入门练习(hello-mcpp.cpp) - for (const auto& entry : fs::directory_iterator(base)) { - if (entry.is_regular_file() && entry.path().extension() == ".cpp") add(entry.path(), ""); + out.push_back(std::move(ex)); } +} - // 按标准分的子目录 - std::vector std_dirs; - for (const auto& entry : fs::directory_iterator(base)) { - if (entry.is_directory() && entry.path().filename().string().starts_with("cpp")) - std_dirs.push_back(entry.path()); - } - std::ranges::sort(std_dirs); +// 扫描全部练习。lang=zh 用根下的工程,lang=en 用 en/ 前缀的镜像工程。 +export std::vector scan(const fs::path& repo_root, std::string_view lang) { + std::string prefix = (lang == "en") ? "en/" : ""; + std::vector found; + + scan_member(repo_root, prefix + "intro", "", found); - for (const auto& dir : std_dirs) { - auto std_dir = dir.filename().string(); - std::vector files; - for (const auto& entry : fs::directory_iterator(dir)) { - if (entry.is_regular_file() && entry.path().extension() == ".cpp") - files.push_back(entry.path()); + std::vector std_dirs; + auto base = (lang == "en") ? repo_root / "en" : repo_root; + if (fs::is_directory(base)) { + for (const auto& entry : fs::directory_iterator(base)) { + auto name = entry.path().filename().string(); + if (entry.is_directory() && name.starts_with("cpp") + && fs::is_directory(entry.path() / "tests")) + std_dirs.push_back(name); } - std::ranges::sort(files); - for (const auto& f : files) add(f, std_dir); } + std::ranges::sort(std_dirs); + for (const auto& d : std_dirs) scan_member(repo_root, prefix + d, d, found); std::ranges::sort(found, {}, &Exercise::order); return found; diff --git a/d2x/buildtools/mcpp/src/main.cpp b/d2x/buildtools/mcpp/src/main.cpp index e099694..39df051 100644 --- a/d2x/buildtools/mcpp/src/main.cpp +++ b/d2x/buildtools/mcpp/src/main.cpp @@ -7,6 +7,11 @@ // 全部输出为 NDJSON 事件流,一行一个事件。d2x 忽略解析不了的行, // 所以 `mcpp run` 的前导空行之类的噪声不会破坏协议。 // +// 练习即测试:check 就是一次 `mcpp test -p --message-format +// json`,编译/退出码事实来自 mcpp 的 JSON 记录,运行期语义(断言/路障) +// 来自 harness 的侧信道 v2 —— 学员绕过 d2x 直接 `mcpp test -p cpp11` +// 走的是完全相同的链路,不存在平行路径。 +// // 用法(由 .d2x.json 的 buildtools 字段驱动,从仓库根执行,无需 cd): // mcpp run -q -p d2x/buildtools/mcpp -- check cpp11-00-auto-and-decltype-0 @@ -14,7 +19,6 @@ import std; import d2x.provider.emit; import d2x.provider.discovery; -import d2x.provider.manifest; import d2x.provider.runner; namespace fs = std::filesystem; @@ -23,12 +27,12 @@ namespace { constexpr int kProtocolVersion = 1; -// 向上找到仓库根:认 .d2x.json + dslings/ 同时存在。 +// 向上找到仓库根:认 .d2x.json + mcpp.toml 同时存在。 // 不能依赖可执行文件位置——它在 target///bin/ 里,层数不固定。 std::optional find_repo_root() { auto dir = fs::current_path(); for (int depth = 0; depth < 12; ++depth) { - if (fs::exists(dir / ".d2x.json") && fs::is_directory(dir / "dslings")) + if (fs::exists(dir / ".d2x.json") && fs::exists(dir / "mcpp.toml")) return dir; if (!dir.has_parent_path() || dir.parent_path() == dir) break; dir = dir.parent_path(); @@ -45,15 +49,11 @@ int cmd_exercises(const fs::path& root) { auto lang = d2x::discovery::read_lang(root); auto all = d2x::discovery::scan(root, lang); if (all.empty()) { - d2x::emit::error(std::format("no exercises found under {}/dslings (lang={})", + d2x::emit::error(std::format("no exercises found under {} (lang={})", root.string(), lang)); return 1; } - // 顺手刷新全量清单:clangd 依赖它拿到每个练习的编译参数。 - // 这里只写文件不构建——构建是 check 的事,枚举必须快。 - d2x::manifest::write_full(root, all); - for (const auto& ex : all) { std::vector files{ex.file.string()}; d2x::emit::exercise(ex.id, ex.order, ex.title, ex.chapter, files); @@ -61,6 +61,14 @@ int cmd_exercises(const fs::path& root) { return 0; } +// 侧信道里的 file 若是相对路径(编译器以工程目录为基准时),在协议边界 +// 还原成绝对 —— d2x 靠它开编辑器、监听文件变更。展示归展示,定位归定位。 +std::string to_abs(const fs::path& root, const std::string& member, + const std::string& file) { + if (file.empty() || fs::path(file).is_absolute()) return file; + return (root / member / file).lexically_normal().string(); +} + int cmd_check(const fs::path& root, std::string_view id) { auto lang = d2x::discovery::read_lang(root); auto all = d2x::discovery::scan(root, lang); @@ -71,46 +79,50 @@ int cmd_check(const fs::path& root, std::string_view id) { return 1; } - // 先保证整个 workspace 存在。check 可能是全新仓库上的第一条命令 - // (学员直接跑 d2x checker),此时根清单还没生成,mcpp 会以退出码 2 - // 报 "workspace member not found"。write_* 都是内容比对后才落盘, - // 重复调用不会推进 mtime、不会让 mcpp 的快速路径失效。 - d2x::manifest::write_full(root, all); + // 侧信道放在 .d2x/ 下:学员进度旁边,不污染工程目录。 + auto result_file = root / ".d2x" / "result.ndjson"; - // 再把这一题单独写进 _current,其余练习(多半还编译不过)不参与构建 - d2x::manifest::write_current(root, *it); - d2x::runner::enter(d2x::manifest::build_dir(root)); + // 从仓库根 spawn(Provider 由 d2x 在仓库根启动,mcpp 自己解析 workspace) + std::error_code ec; + fs::current_path(root, ec); d2x::emit::stage("compile"); - auto compiled = d2x::runner::build_current(); - d2x::emit::output(compiled.output); - if (compiled.exit_code != 0) { - d2x::emit::verdict("fail", "compile", compiled.exit_code); + auto res = d2x::runner::run_mcpp_test(it->member, it->test_name, result_file); + + if (!res.package_error.empty()) { + // 包级构建失败:harness 或工程本身坏了 —— 这是课程基础设施问题, + // 不是这道题没做对。按协议约定不发 verdict(d2x 视为异常并原样呈现)。 + d2x::emit::output(res.package_error); + d2x::emit::error("课程构建失败(不是这道练习的问题)——请向课程仓库反馈"); + return 1; + } + if (!res.record) { + d2x::emit::error(std::format( + "mcpp test 没有返回 '{}' 的记录{}", it->test_name, + res.saw_any ? "" : "(mcpp 可能不支持 --message-format json,需要 >= 0.0.104)")); + return 1; + } + + const auto& rec = *res.record; + d2x::emit::output(rec.compile_output); + if (rec.status == "compile_fail") { + d2x::emit::verdict("fail", "compile", rec.exit_code); return 0; // 练习没通过是正常业务路径,不是 Provider 出错 } d2x::emit::stage("run"); + d2x::emit::output(rec.run_output); - // 侧信道放在构建目录里:与生成物同生共死,不污染仓库 - auto result_file = d2x::manifest::build_dir(root) / "_current" / "result.ndjson"; - auto ran = d2x::runner::run_current(result_file); - d2x::emit::output(ran.output); - - auto report = d2x::runner::judge_run(ran.exit_code, result_file); + auto report = d2x::runner::judge_run(rec.exit_code, result_file); - // 侧信道里的 file 来自 __FILE__,而生成的清单加了 -fmacro-prefix-map - // 让它相对仓库根(学员看到的报错才不会顶着一长串 /home/... 前缀)。 - // 但协议要求绝对路径 —— d2x 靠它开编辑器、靠它监听文件变更 —— 所以 - // 在协议边界上还原。展示归展示,定位归定位。 std::vector diags; for (const auto& f : report.failures) { - auto abs = f.file.empty() || fs::path(f.file).is_absolute() - ? f.file - : (root / f.file).lexically_normal().string(); - diags.push_back(d2x::emit::diagnostic(abs, f.line, f.expr, f.expected, f.actual)); + diags.push_back(d2x::emit::diagnostic(to_abs(root, it->member, f.file), + f.line, f.what, f.expected, f.actual)); } - d2x::emit::verdict(d2x::runner::to_string(report.outcome), "run", ran.exit_code, diags); + d2x::emit::verdict(d2x::runner::to_string(report.outcome), "run", + rec.exit_code, diags); return 0; } @@ -128,7 +140,7 @@ int main(int argc, char** argv) { auto root = find_repo_root(); if (!root) { - d2x::emit::error("repo root not found (looking for .d2x.json + dslings/)"); + d2x::emit::error("repo root not found (looking for .d2x.json + mcpp.toml)"); return 2; } diff --git a/d2x/buildtools/mcpp/src/manifest.cppm b/d2x/buildtools/mcpp/src/manifest.cppm deleted file mode 100644 index f34c7c8..0000000 --- a/d2x/buildtools/mcpp/src/manifest.cppm +++ /dev/null @@ -1,138 +0,0 @@ -// 生成 mcpp workspace 清单。 -// -// 双 member 布局,解决一个硬约束:dslings 的练习默认就编译不过,而 mcpp 的 -// build/run 都会先全量构建整个包——一个坏兄弟拖垮全部且零产物。所以: -// -// .d2x/build// 全量 target,供 clangd 拿到完整 compile_commands.json -// .d2x/build/_current/ 每次只写当前一题,checker 只构建它 -// -// 生成物全部在 .d2x/build/ 下,练习源文件原地不动(main 用 ../ 逃逸出包根)。 -export module d2x.provider.manifest; - -import std; -import d2x.provider.discovery; - -namespace fs = std::filesystem; - -namespace d2x::manifest { - -using discovery::Exercise; - -// 所有练习统一按 c++23 编译。mcpp 目前硬拒 c++11/14/17/20 -// (src/manifest/types.cppm 的白名单),本方案选择不改 mcpp 上游。 -constexpr std::string_view kStandard = "c++23"; - -// 生成的 member 位于 .d2x/build//,回到仓库根要退三层。 -constexpr std::string_view kToRoot = "../../.."; - -export fs::path build_dir(const fs::path& repo_root) { - return repo_root / ".d2x" / "build"; -} - -std::string member_name(const Exercise& ex) { - return ex.std_dir.empty() ? "intro" : ex.std_dir; -} - -// 把绝对路径转成相对 member 目录的形式。mcpp 的 main 接受 ../ 逃逸。 -std::string relative_to_member(const fs::path& repo_root, const fs::path& file) { - auto rel = fs::relative(file, repo_root).generic_string(); - return std::format("{}/{}", kToRoot, rel); -} - -void write_package_header(std::ostream& out, std::string_view name, const fs::path& repo_root) { - std::println(out, "# 由 d2x-buildtools-mcpp 生成,请勿手工编辑。"); - std::println(out, "[package]"); - std::println(out, "name = \"{}\"", name); - std::println(out, "version = \"0.1.0\""); - std::println(out, "standard = \"{}\"", kStandard); - std::println(out, ""); - std::println(out, "[build]"); - // 显式清空源码 glob:练习不是这个包的「源码」,它们只是各自 target 的入口。 - std::println(out, "sources = []"); - // 让 __FILE__ 变成相对仓库根的路径。mcpp 传给编译器的是绝对路径, - // 于是 d2x_assert 的报错会印出一长串 /home/... 前缀,对学员是噪声。 - // 这只影响宏展开出来的字符串,不影响编译器自身诊断里的路径。 - // 前缀必须是编译器实际看到的绝对路径,不能用 kToRoot 那种相对形式。 - std::println(out, "cxxflags = [\"-fmacro-prefix-map={}/=\"]", - repo_root.generic_string()); - std::println(out, ""); - // 脚手架走正经的库依赖,而不是把仓库根塞进 include 搜索路径。 - // 后者会让练习能 #include 仓库里任何文件,是个隐患。 - std::println(out, "[dependencies]"); - std::println(out, "harness = {{ path = \"{}/dslings/harness\" }}", kToRoot); -} - -void write_target(std::ostream& out, const fs::path& repo_root, const Exercise& ex) { - std::println(out, ""); - std::println(out, "[targets.{}]", ex.id); - std::println(out, "kind = \"bin\""); - std::println(out, "main = \"{}\"", relative_to_member(repo_root, ex.file)); - if (!ex.cxxflags.empty()) { - std::string list; - for (std::size_t i = 0; i < ex.cxxflags.size(); ++i) { - if (i) list += ", "; - list += std::format("\"{}\"", ex.cxxflags[i]); - } - std::println(out, "cxxflags = [{}]", list); - } -} - -// 只有内容真的变了才落盘。避免无谓地推进 mtime,让 mcpp 的快速路径失效。 -bool write_if_changed(const fs::path& path, std::string_view content) { - if (fs::exists(path)) { - std::ifstream in(path); - std::string existing((std::istreambuf_iterator(in)), {}); - if (existing == content) return false; - } - fs::create_directories(path.parent_path()); - std::ofstream out(path); - out << content; - return true; -} - -// 全量清单:每个 C++ 标准一个 member,持有该标准下的全部练习。 -// 供 clangd —— 即使大多数练习编译不过,mcpp 仍会生成完整的 -// compile_commands.json(已实测),所以 IDE 对每个练习都有正确的编译参数。 -export void write_full(const fs::path& repo_root, const std::vector& all) { - auto root = build_dir(repo_root); - - std::map> by_member; - for (const auto& ex : all) by_member[member_name(ex)].push_back(&ex); - - std::vector members; - for (const auto& [name, list] : by_member) { - std::ostringstream buf; - write_package_header(buf, name, repo_root); - for (const auto* ex : list) write_target(buf, repo_root, *ex); - write_if_changed(root / name / "mcpp.toml", buf.str()); - members.push_back(name); - } - - // _current 也是这个 workspace 的成员,占位清单先写空壳 - members.push_back("_current"); - write_if_changed(root / "_current" / "mcpp.toml", - [&] { std::ostringstream b; write_package_header(b, "_current", repo_root); return b.str(); }()); - - std::ostringstream ws; - std::println(ws, "# 由 d2x-buildtools-mcpp 生成,请勿手工编辑。"); - std::println(ws, "[workspace]"); - std::print(ws, "members = ["); - for (std::size_t i = 0; i < members.size(); ++i) { - if (i) std::print(ws, ", "); - std::print(ws, "\"{}\"", members[i]); - } - std::println(ws, "]"); - write_if_changed(root / "mcpp.toml", ws.str()); -} - -// 单题清单:checker 每次 check 前重写它,只含要验证的那一题。 -// 实测改写 target 集合不会让 fingerprint 目录爆炸(始终只有 1 个), -// 切题 0.118s、切回 0.018s。 -export void write_current(const fs::path& repo_root, const Exercise& ex) { - std::ostringstream buf; - write_package_header(buf, "_current", repo_root); - write_target(buf, repo_root, ex); - write_if_changed(build_dir(repo_root) / "_current" / "mcpp.toml", buf.str()); -} - -} // namespace d2x::manifest diff --git a/d2x/buildtools/mcpp/src/runner.cppm b/d2x/buildtools/mcpp/src/runner.cppm index 8f9b71a..511f231 100644 --- a/d2x/buildtools/mcpp/src/runner.cppm +++ b/d2x/buildtools/mcpp/src/runner.cppm @@ -1,12 +1,16 @@ -// 调用 mcpp 构建/运行单个练习,并判定结果。 +// 调 `mcpp test --message-format json` 验证单个练习,并合并侧信道判定。 +// +// 这一层不再生成任何清单、不再自己算退出码语义 —— 编译/运行的事实由 +// mcpp 的 JSON 记录提供(status/exit_code/compile_output/run_output), +// 运行期语义(哪条断言挂了、路障拆没拆)由 harness 的侧信道 v2 提供, +// 两个来源在这里合并成 Provider 协议的 verdict。 module; -// popen/pclose 是 POSIX,WIFEXITED 等是宏——都不在 import std 的范围内。 #include #ifndef _WIN32 # include -# include // unsetenv #endif +#include // setenv / _putenv_s export module d2x.provider.runner; @@ -21,22 +25,15 @@ export struct Captured { std::string output; }; -// 合并 stderr —— 编译错误和练习的运行输出都要原样呈现给学员。 -// 这些内容最终进 JSON 的 output 字段,不会污染协议。 -// -// 必须先清掉 LD_LIBRARY_PATH:本 Provider 由 `mcpp run` 启动时,mcpp 会把 -// LD_LIBRARY_PATH 指向它私有的 glibc(~/.mcpp/registry/.../xim-x-glibc/*/lib64) -// 并注入子进程。我们接着去 spawn 嵌套的 mcpp —— 那是另一个二进制,被迫加载 -// 错配的 glibc 后会在动态链接器里段错误。清空后 mcpp 会为它自己的子进程重新 -// 设置正确的值,所以练习程序照常能跑。 +// 只要 stdout:JSON 协议流在 stdout,stderr 的人读错误信息这里不需要 +// (包级失败在 stdout 也有 {"error":"package"} 记录)。 // -// d2x 侧对同一问题有相同的处理(platform.cppm 的 run_command_capture)。 -export Captured capture(const std::string& cmd) { +// 注:旧实现这里要先 unsetenv("LD_LIBRARY_PATH") 绕嵌套 mcpp 的 glibc +// 段错误 —— mcpp 已在上游根治(merged_environ 剥离私有 glibc 条目), +// workaround 随之删除。 +export Captured capture_stdout(const std::string& cmd) { Captured result; -#ifndef _WIN32 - ::unsetenv("LD_LIBRARY_PATH"); -#endif - std::string full = cmd + " 2>&1"; + std::string full = cmd + " 2>/dev/null"; #ifdef _WIN32 FILE* pipe = ::_popen(full.c_str(), "r"); @@ -62,34 +59,23 @@ export Captured capture(const std::string& cmd) { return result; } -// 这套判定是「课程约定」,不是「构建工具行为」——所以它属于 Provider, -// 而不是 d2x 框架。换一门 Rust 课程,这里会完全不同。 -export enum class Outcome { Pass, Fail, Blocked }; - -export std::string_view to_string(Outcome o) { - switch (o) { - case Outcome::Pass: return "pass"; - case Outcome::Fail: return "fail"; - case Outcome::Blocked: return "blocked"; - } - return "fail"; -} - -export struct Failure { - std::string expr; - std::string expected; - std::string actual; - std::string file; - int line{}; +// mcpp 的一条 per-test JSON 记录(我们关心的子集)。 +export struct TestRecord { + std::string test; // 相对 tests/ 的路径名,如 00-auto-and-decltype/0 + std::string status; // pass | compile_fail | run_fail + int exit_code{}; + std::string compile_output; + std::string run_output; }; -export struct RunReport { - Outcome outcome{Outcome::Pass}; - std::vector failures; +export struct McppTestResult { + std::optional record; // 精确匹配 test 名的那条 + std::string package_error; // {"error":"package"} 的 compile_output + bool saw_any = false; }; -// 从侧信道 NDJSON 里取一个字段。这里不引入 JSON 库: -// 格式由我们自己的 harness 产出,字段固定、无嵌套、无数组。 +// 从一行 JSON 里取字段。格式由 mcpp --message-format json 产出: +// 字段固定、无嵌套对象(summary 行不取),不引入 JSON 库。 std::string field(std::string_view line, std::string_view key) { auto pat = std::format("\"{}\":", key); auto at = line.find(pat); @@ -107,6 +93,16 @@ std::string field(std::string_view line, std::string_view key) { case 'n': out += '\n'; break; case 't': out += '\t'; break; case 'r': out += '\r'; break; + case 'u': { // \u00XX —— mcpp 只对 <0x20 编码 + if (at + 4 < line.size()) { + int v = 0; + auto hex = line.substr(at + 1, 4); + std::from_chars(hex.data(), hex.data() + 4, v, 16); + out += static_cast(v); + at += 4; + } + break; + } default: out += line[at]; } } else { @@ -116,13 +112,83 @@ std::string field(std::string_view line, std::string_view key) { } return out; } - auto end = line.find_first_of(",}", at); // 裸值(数字/布尔) + auto end = line.find_first_of(",}", at); // 裸值(数字/布尔/null) return std::string(line.substr(at, end == std::string_view::npos ? end : end - at)); } +// 跑 `mcpp test` 并抽出目标测试的记录。pattern 是子串匹配,可能带出 +// 邻居测试(如 …/1 匹配 …/10),所以逐行解析后按 test 名精确挑。 +export McppTestResult run_mcpp_test(const std::string& member, + const std::string& test_name, + const fs::path& result_file) { + std::error_code ec; + fs::remove(result_file, ec); // harness 是追加写的,清掉上一轮残留 + fs::create_directories(result_file.parent_path(), ec); +#ifndef _WIN32 + ::setenv("D2X_RESULT_FILE", result_file.string().c_str(), 1); +#else + ::_putenv_s("D2X_RESULT_FILE", result_file.string().c_str()); +#endif + + auto cmd = std::format("mcpp test -q -p {} {} --message-format json", + member, test_name); + auto cap = capture_stdout(cmd); + + McppTestResult out; + std::istringstream lines(cap.output); + for (std::string line; std::getline(lines, line); ) { + if (line.empty() || line.front() != '{') continue; + if (line.find("\"error\":\"package\"") != std::string::npos) { + out.package_error = field(line, "compile_output"); + out.saw_any = true; + continue; + } + auto name = field(line, "test"); + if (name.empty()) continue; // summary 行等 + out.saw_any = true; + if (name != test_name) continue; + TestRecord rec; + rec.test = name; + rec.status = field(line, "status"); + rec.compile_output = field(line, "compile_output"); + rec.run_output = field(line, "run_output"); + auto raw = field(line, "exit_code"); + std::from_chars(raw.data(), raw.data() + raw.size(), rec.exit_code); + out.record = std::move(rec); + } + return out; +} + +// —— 侧信道判定(语义与设计稿 §5 的顺序表一致)—— + +export enum class Outcome { Pass, Fail, Blocked }; + +export std::string_view to_string(Outcome o) { + switch (o) { + case Outcome::Pass: return "pass"; + case Outcome::Fail: return "fail"; + case Outcome::Blocked: return "blocked"; + } + return "fail"; +} + +export struct Failure { + std::string what; + std::string expected; + std::string actual; + std::string file; + int line{}; +}; + +export struct RunReport { + Outcome outcome{Outcome::Pass}; + std::vector failures; +}; + // 判定顺序: // 有 ok:false → Fail,每条失败都能转成一个 Diagnostic -// 无失败但有 wait → Blocked(答案已对,只差拆路障) +// 无失败但退出码非 0 → Fail(纯崩溃 / 练习自己 return 非 0) +// 无失败、有 wait → Blocked(答案已对,只差拆路障) // 侧信道文件不存在 → 退回「退出码为 0 即通过」 // // 最后一条让 harness 自动变成可选的:纯观察型练习可以是零依赖的 @@ -130,14 +196,8 @@ std::string field(std::string_view line, std::string_view key) { export RunReport judge_run(int exit_code, const fs::path& result_file) { RunReport report; - if (exit_code != 0) { - report.outcome = Outcome::Fail; - // 即使退出码非 0,已写入的断言仍然有价值(崩溃前过了几条) - } - std::ifstream in(result_file); if (!in) { - // 没有侧信道:练习没用 harness,退出码就是全部信息 report.outcome = (exit_code == 0) ? Outcome::Pass : Outcome::Fail; return report; } @@ -154,7 +214,7 @@ export RunReport judge_run(int exit_code, const fs::path& result_file) { std::from_chars(raw.data(), raw.data() + raw.size(), line_no); report.failures.push_back(Failure{ - .expr = field(line, "expr"), + .what = field(line, "what"), .expected = field(line, "expected"), .actual = field(line, "actual"), .file = field(line, "file"), @@ -163,34 +223,11 @@ export RunReport judge_run(int exit_code, const fs::path& result_file) { } if (!report.failures.empty()) report.outcome = Outcome::Fail; - else if (exit_code != 0) report.outcome = Outcome::Fail; else if (saw_wait) report.outcome = Outcome::Blocked; + else if (exit_code != 0) report.outcome = Outcome::Fail; else report.outcome = Outcome::Pass; return report; } -// mcpp 需要在 workspace 根(.d2x/build/)下执行。Provider 是独立进程, -// 直接 chdir 比拼 `cd X && ...` 更干净,也避开 Windows 的 shell 差异。 -export void enter(const fs::path& workspace_root) { - fs::current_path(workspace_root); -} - -export Captured build_current() { - return capture("mcpp build -q -p _current"); -} - -// 运行前把侧信道路径告诉 harness,并清掉上一轮的残留 —— -// harness 是追加写的,不清会把上次的失败算进这次。 -export Captured run_current(const fs::path& result_file) { - std::error_code ec; - fs::remove(result_file, ec); -#ifndef _WIN32 - ::setenv("D2X_RESULT_FILE", result_file.string().c_str(), 1); -#else - ::_putenv_s("D2X_RESULT_FILE", result_file.string().c_str()); -#endif - return capture("mcpp run -q -p _current"); -} - } // namespace d2x::runner From 77d14117cd683e26ccc4339016a7ce34f582985b Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 22:38:05 +0800 Subject: [PATCH 12/25] =?UTF-8?q?feat:=20dslings=20=E5=85=A8=E9=87=8F?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E5=88=B0=E3=80=8C=E7=BB=83=E4=B9=A0=E5=8D=B3?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E3=80=8D=E5=B8=83=E5=B1=80(104=20=E7=BB=83?= =?UTF-8?q?=E4=B9=A0=20+=2051=20=E7=AD=94=E6=A1=88=20+=207=20=E6=88=90?= =?UTF-8?q?=E5=91=98=E5=B7=A5=E7=A8=8B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cpp11/mcpp.toml | 10 +++ .../tests/00-auto-and-decltype/0.cpp | 20 ++--- .../tests/00-auto-and-decltype/1.cpp | 16 ++-- .../tests/00-auto-and-decltype/2.cpp | 15 ++-- .../tests/00-auto-and-decltype/3.cpp | 16 ++-- .../tests/00-auto-and-decltype/4.cpp | 21 +++-- .../tests/00-auto-and-decltype/5.cpp | 17 ++-- .../tests/01-default-and-delete/0.cpp | 9 +-- .../tests/01-default-and-delete/1.cpp | 17 ++-- .../tests/01-default-and-delete/2.cpp | 9 +-- .../tests/02-final-and-override/0.cpp | 10 +-- .../tests/02-final-and-override/1.cpp | 18 ++--- .../tests/02-final-and-override/2.cpp | 10 +-- .../tests/03-trailing-return-type/0.cpp | 19 +++-- .../tests/04-rvalue-references/0.cpp | 14 ++-- .../tests/05-move-semantics/0.cpp | 15 ++-- .../tests/05-move-semantics/1.cpp | 15 ++-- .../tests/05-move-semantics/2.cpp | 15 ++-- .../tests/06-scoped-enums/0.cpp | 17 ++-- .../tests/06-scoped-enums/1.cpp | 21 +++-- .../tests/07-constexpr/0.cpp | 11 ++- .../tests/07-constexpr/1.cpp | 9 +-- .../tests/08-literal-type/0.cpp | 11 +-- .../tests/08-literal-type/1.cpp | 9 +-- .../tests/09-list-initialization/0.cpp | 27 +++---- .../tests/09-list-initialization/1.cpp | 13 ++- .../tests/09-list-initialization/2.cpp | 19 ++--- .../tests/09-list-initialization/3.cpp | 19 ++--- .../tests/10-delegating-constructors/0.cpp | 22 +++-- .../tests/10-delegating-constructors/1.cpp | 16 ++-- .../tests/11-inherited-constructors/0.cpp | 10 +-- .../tests/11-inherited-constructors/1.cpp | 32 ++++---- .../tests/11-inherited-constructors/2.cpp | 22 +++-- .../tests/12-nullptr/0.cpp | 25 +++--- .../tests/12-nullptr/1.cpp | 16 ++-- .../tests/12-nullptr/2.cpp | 9 +-- .../tests/13-long-long/0.cpp | 15 ++-- .../tests/13-long-long/1.cpp | 19 +++-- .../tests/14-type-alias/0.cpp | 19 +++-- .../tests/14-type-alias/1.cpp | 18 ++--- .../tests/14-type-alias/2.cpp | 23 +++--- .../tests/14-type-alias/3.cpp | 13 ++- .../tests/15-variadic-templates/0.cpp | 11 ++- .../tests/15-variadic-templates/1.cpp | 14 ++-- .../tests/16-generalized-unions/0.cpp | 10 +-- .../tests/16-generalized-unions/1.cpp | 11 ++- .../tests/16-generalized-unions/2.cpp | 22 +++-- .../tests/17-pod-type/0.cpp | 16 ++-- .../tests/17-pod-type/1.cpp | 16 ++-- .../tests/17-pod-type/2.cpp | 18 ++--- dslings/cpp14/00-generic-lambdas-0.cpp | 56 ------------- dslings/cpp14/00-generic-lambdas-1.cpp | 67 --------------- dslings/cpp17/README.md | 1 - dslings/cpp20/README.md | 1 - dslings/cpp23/README.md | 1 - dslings/harness/include/d2x/cpp/common.hpp | 72 ----------------- .../harness/include/d2x/cpp/honly_logger.hpp | 18 ----- dslings/harness/include/d2x/cpp/report.hpp | 81 ------------------- dslings/harness/mcpp.toml | 15 ---- dslings/harness/src/harness.cppm | 62 -------------- en/cpp11/mcpp.toml | 10 +++ .../cpp11/tests/00-auto-and-decltype/0.cpp | 20 ++--- .../cpp11/tests/00-auto-and-decltype/1.cpp | 16 ++-- .../cpp11/tests/00-auto-and-decltype/2.cpp | 15 ++-- .../cpp11/tests/00-auto-and-decltype/3.cpp | 16 ++-- .../cpp11/tests/00-auto-and-decltype/4.cpp | 21 +++-- .../cpp11/tests/00-auto-and-decltype/5.cpp | 17 ++-- .../cpp11/tests/01-default-and-delete/0.cpp | 9 +-- .../cpp11/tests/01-default-and-delete/1.cpp | 17 ++-- .../cpp11/tests/01-default-and-delete/2.cpp | 9 +-- .../cpp11/tests/02-final-and-override/0.cpp | 10 +-- .../cpp11/tests/02-final-and-override/1.cpp | 18 ++--- .../cpp11/tests/02-final-and-override/2.cpp | 10 +-- .../cpp11/tests/03-trailing-return-type/0.cpp | 19 +++-- .../cpp11/tests/04-rvalue-references/0.cpp | 14 ++-- .../cpp11/tests/05-move-semantics/0.cpp | 15 ++-- .../cpp11/tests/05-move-semantics/1.cpp | 15 ++-- .../cpp11/tests/05-move-semantics/2.cpp | 15 ++-- .../cpp11/tests/06-scoped-enums/0.cpp | 17 ++-- .../cpp11/tests/06-scoped-enums/1.cpp | 21 +++-- .../cpp11/tests/07-constexpr/0.cpp | 11 ++- .../cpp11/tests/07-constexpr/1.cpp | 9 +-- .../cpp11/tests/08-literal-type/0.cpp | 11 +-- .../cpp11/tests/08-literal-type/1.cpp | 9 +-- .../cpp11/tests/09-list-initialization/0.cpp | 27 +++---- .../cpp11/tests/09-list-initialization/1.cpp | 13 ++- .../cpp11/tests/09-list-initialization/2.cpp | 19 ++--- .../cpp11/tests/09-list-initialization/3.cpp | 19 ++--- .../tests/10-delegating-constructors/0.cpp | 22 +++-- .../tests/10-delegating-constructors/1.cpp | 16 ++-- .../tests/11-inherited-constructors/0.cpp | 10 +-- .../tests/11-inherited-constructors/1.cpp | 32 ++++---- .../tests/11-inherited-constructors/2.cpp | 22 +++-- .../cpp11/tests/12-nullptr/0.cpp | 25 +++--- .../cpp11/tests/12-nullptr/1.cpp | 16 ++-- .../cpp11/tests/12-nullptr/2.cpp | 9 +-- .../cpp11/tests/13-long-long/0.cpp | 15 ++-- .../cpp11/tests/13-long-long/1.cpp | 19 +++-- .../cpp11/tests/14-type-alias/0.cpp | 19 +++-- .../cpp11/tests/14-type-alias/1.cpp | 18 ++--- .../cpp11/tests/14-type-alias/2.cpp | 23 +++--- .../cpp11/tests/14-type-alias/3.cpp | 13 ++- .../cpp11/tests/15-variadic-templates/0.cpp | 11 ++- .../cpp11/tests/15-variadic-templates/1.cpp | 14 ++-- .../cpp11/tests/16-generalized-unions/0.cpp | 10 +-- .../cpp11/tests/16-generalized-unions/1.cpp | 11 ++- .../cpp11/tests/16-generalized-unions/2.cpp | 22 +++-- .../cpp11/tests/17-pod-type/0.cpp | 16 ++-- .../cpp11/tests/17-pod-type/1.cpp | 16 ++-- .../cpp11/tests/17-pod-type/2.cpp | 18 ++--- en/cpp14/mcpp.toml | 10 +++ .../cpp14/tests/00-generic-lambdas/0.cpp | 25 +++--- .../cpp14/tests/00-generic-lambdas/1.cpp | 24 +++--- en/intro/mcpp.toml | 10 +++ {dslings/en => en/intro/tests}/hello-mcpp.cpp | 9 ++- intro/mcpp.toml | 10 +++ {dslings => intro/tests}/hello-mcpp.cpp | 9 ++- mcpp.toml | 6 +- .../0.cpp} | 15 ++-- .../1.cpp} | 11 +-- .../2.cpp} | 10 +-- .../3.cpp} | 11 ++- .../4.cpp} | 16 ++-- .../5.cpp} | 12 +-- .../0.cpp} | 4 +- .../1.cpp} | 12 +-- .../2.cpp} | 4 +- .../0.cpp} | 5 +- .../1.cpp} | 13 ++- .../2.cpp} | 5 +- .../0.cpp} | 14 ++-- .../0.cpp} | 9 +-- .../0.cpp} | 10 +-- .../1.cpp} | 10 +-- .../2.cpp} | 10 +-- .../0.cpp} | 8 +- .../1.cpp} | 16 ++-- .../0.cpp} | 6 +- .../1.cpp} | 4 +- .../0.cpp} | 6 +- .../1.cpp} | 4 +- .../0.cpp} | 22 ++--- .../1.cpp} | 8 +- .../2.cpp} | 14 ++-- .../3.cpp} | 13 ++- .../0.cpp} | 17 ++-- .../1.cpp} | 11 ++- .../0.cpp} | 5 +- .../1.cpp} | 27 +++---- .../2.cpp} | 17 ++-- .../{12-nullptr-0.cpp => 12-nullptr/0.cpp} | 21 +++-- .../{12-nullptr-1.cpp => 12-nullptr/1.cpp} | 11 +-- .../{12-nullptr-2.cpp => 12-nullptr/2.cpp} | 4 +- .../0.cpp} | 10 +-- .../1.cpp} | 14 ++-- .../0.cpp} | 14 ++-- .../1.cpp} | 13 ++- .../2.cpp} | 19 ++--- .../3.cpp} | 8 +- .../0.cpp} | 6 +- .../1.cpp} | 9 ++- .../0.cpp} | 5 +- .../1.cpp} | 6 +- .../2.cpp} | 17 ++-- .../{17-pod-type-0.cpp => 17-pod-type/0.cpp} | 12 +-- .../{17-pod-type-1.cpp => 17-pod-type/1.cpp} | 11 ++- .../{17-pod-type-2.cpp => 17-pod-type/2.cpp} | 13 ++- 167 files changed, 1068 insertions(+), 1549 deletions(-) create mode 100644 cpp11/mcpp.toml rename dslings/cpp11/00-auto-and-decltype-0.cpp => cpp11/tests/00-auto-and-decltype/0.cpp (73%) rename dslings/cpp11/00-auto-and-decltype-1.cpp => cpp11/tests/00-auto-and-decltype/1.cpp (75%) rename dslings/cpp11/00-auto-and-decltype-2.cpp => cpp11/tests/00-auto-and-decltype/2.cpp (84%) rename dslings/cpp11/00-auto-and-decltype-3.cpp => cpp11/tests/00-auto-and-decltype/3.cpp (74%) rename dslings/cpp11/00-auto-and-decltype-4.cpp => cpp11/tests/00-auto-and-decltype/4.cpp (70%) rename dslings/cpp11/00-auto-and-decltype-5.cpp => cpp11/tests/00-auto-and-decltype/5.cpp (76%) rename dslings/cpp11/01-default-and-delete-0.cpp => cpp11/tests/01-default-and-delete/0.cpp (88%) rename dslings/cpp11/01-default-and-delete-1.cpp => cpp11/tests/01-default-and-delete/1.cpp (62%) rename dslings/cpp11/01-default-and-delete-2.cpp => cpp11/tests/01-default-and-delete/2.cpp (85%) rename dslings/cpp11/02-final-and-override-0.cpp => cpp11/tests/02-final-and-override/0.cpp (88%) rename dslings/cpp11/02-final-and-override-1.cpp => cpp11/tests/02-final-and-override/1.cpp (66%) rename dslings/cpp11/02-final-and-override-2.cpp => cpp11/tests/02-final-and-override/2.cpp (93%) rename dslings/cpp11/03-trailing-return-type.cpp => cpp11/tests/03-trailing-return-type/0.cpp (64%) rename dslings/cpp11/04-rvalue-references.cpp => cpp11/tests/04-rvalue-references/0.cpp (89%) rename dslings/cpp11/05-move-semantics-0.cpp => cpp11/tests/05-move-semantics/0.cpp (83%) rename dslings/cpp11/05-move-semantics-1.cpp => cpp11/tests/05-move-semantics/1.cpp (87%) rename dslings/cpp11/05-move-semantics-2.cpp => cpp11/tests/05-move-semantics/2.cpp (85%) rename dslings/cpp11/06-scoped-enums-0.cpp => cpp11/tests/06-scoped-enums/0.cpp (78%) rename dslings/cpp11/06-scoped-enums-1.cpp => cpp11/tests/06-scoped-enums/1.cpp (69%) rename dslings/cpp11/07-constexpr-0.cpp => cpp11/tests/07-constexpr/0.cpp (84%) rename dslings/cpp11/07-constexpr-1.cpp => cpp11/tests/07-constexpr/1.cpp (94%) rename dslings/cpp11/08-literal-type-0.cpp => cpp11/tests/08-literal-type/0.cpp (91%) rename dslings/cpp11/08-literal-type-1.cpp => cpp11/tests/08-literal-type/1.cpp (87%) rename dslings/cpp11/09-list-initialization-0.cpp => cpp11/tests/09-list-initialization/0.cpp (62%) rename dslings/cpp11/09-list-initialization-1.cpp => cpp11/tests/09-list-initialization/1.cpp (81%) rename dslings/cpp11/09-list-initialization-2.cpp => cpp11/tests/09-list-initialization/2.cpp (74%) rename dslings/cpp11/09-list-initialization-3.cpp => cpp11/tests/09-list-initialization/3.cpp (82%) rename dslings/cpp11/10-delegating-constructors-0.cpp => cpp11/tests/10-delegating-constructors/0.cpp (78%) rename dslings/cpp11/10-delegating-constructors-1.cpp => cpp11/tests/10-delegating-constructors/1.cpp (85%) rename dslings/cpp11/11-inherited-constructors-0.cpp => cpp11/tests/11-inherited-constructors/0.cpp (92%) rename dslings/cpp11/11-inherited-constructors-1.cpp => cpp11/tests/11-inherited-constructors/1.cpp (62%) rename dslings/cpp11/11-inherited-constructors-2.cpp => cpp11/tests/11-inherited-constructors/2.cpp (80%) rename dslings/cpp11/12-nullptr-0.cpp => cpp11/tests/12-nullptr/0.cpp (70%) rename dslings/cpp11/12-nullptr-1.cpp => cpp11/tests/12-nullptr/1.cpp (75%) rename dslings/cpp11/12-nullptr-2.cpp => cpp11/tests/12-nullptr/2.cpp (92%) rename dslings/cpp11/13-long-long-0.cpp => cpp11/tests/13-long-long/0.cpp (77%) rename dslings/cpp11/13-long-long-1.cpp => cpp11/tests/13-long-long/1.cpp (64%) rename dslings/cpp11/14-type-alias-0.cpp => cpp11/tests/14-type-alias/0.cpp (72%) rename dslings/cpp11/14-type-alias-1.cpp => cpp11/tests/14-type-alias/1.cpp (78%) rename dslings/cpp11/14-type-alias-2.cpp => cpp11/tests/14-type-alias/2.cpp (70%) rename dslings/cpp11/14-type-alias-3.cpp => cpp11/tests/14-type-alias/3.cpp (83%) rename dslings/cpp11/15-variadic-templates-0.cpp => cpp11/tests/15-variadic-templates/0.cpp (86%) rename dslings/cpp11/15-variadic-templates-1.cpp => cpp11/tests/15-variadic-templates/1.cpp (77%) rename dslings/cpp11/16-generalized-unions-0.cpp => cpp11/tests/16-generalized-unions/0.cpp (84%) rename dslings/cpp11/16-generalized-unions-1.cpp => cpp11/tests/16-generalized-unions/1.cpp (86%) rename dslings/cpp11/16-generalized-unions-2.cpp => cpp11/tests/16-generalized-unions/2.cpp (80%) rename dslings/cpp11/17-pod-type-0.cpp => cpp11/tests/17-pod-type/0.cpp (87%) rename dslings/cpp11/17-pod-type-1.cpp => cpp11/tests/17-pod-type/1.cpp (72%) rename dslings/cpp11/17-pod-type-2.cpp => cpp11/tests/17-pod-type/2.cpp (78%) delete mode 100644 dslings/cpp14/00-generic-lambdas-0.cpp delete mode 100644 dslings/cpp14/00-generic-lambdas-1.cpp delete mode 100644 dslings/cpp17/README.md delete mode 100644 dslings/cpp20/README.md delete mode 100644 dslings/cpp23/README.md delete mode 100644 dslings/harness/include/d2x/cpp/common.hpp delete mode 100644 dslings/harness/include/d2x/cpp/honly_logger.hpp delete mode 100644 dslings/harness/include/d2x/cpp/report.hpp delete mode 100644 dslings/harness/mcpp.toml delete mode 100644 dslings/harness/src/harness.cppm create mode 100644 en/cpp11/mcpp.toml rename dslings/en/cpp11/00-auto-and-decltype-0.cpp => en/cpp11/tests/00-auto-and-decltype/0.cpp (73%) rename dslings/en/cpp11/00-auto-and-decltype-1.cpp => en/cpp11/tests/00-auto-and-decltype/1.cpp (75%) rename dslings/en/cpp11/00-auto-and-decltype-2.cpp => en/cpp11/tests/00-auto-and-decltype/2.cpp (83%) rename dslings/en/cpp11/00-auto-and-decltype-3.cpp => en/cpp11/tests/00-auto-and-decltype/3.cpp (73%) rename dslings/en/cpp11/00-auto-and-decltype-4.cpp => en/cpp11/tests/00-auto-and-decltype/4.cpp (69%) rename dslings/en/cpp11/00-auto-and-decltype-5.cpp => en/cpp11/tests/00-auto-and-decltype/5.cpp (76%) rename dslings/en/cpp11/01-default-and-delete-0.cpp => en/cpp11/tests/01-default-and-delete/0.cpp (88%) rename dslings/en/cpp11/01-default-and-delete-1.cpp => en/cpp11/tests/01-default-and-delete/1.cpp (61%) rename dslings/en/cpp11/01-default-and-delete-2.cpp => en/cpp11/tests/01-default-and-delete/2.cpp (85%) rename dslings/en/cpp11/02-final-and-override-0.cpp => en/cpp11/tests/02-final-and-override/0.cpp (88%) rename dslings/en/cpp11/02-final-and-override-1.cpp => en/cpp11/tests/02-final-and-override/1.cpp (65%) rename dslings/en/cpp11/02-final-and-override-2.cpp => en/cpp11/tests/02-final-and-override/2.cpp (93%) rename dslings/en/cpp11/03-trailing-return-type.cpp => en/cpp11/tests/03-trailing-return-type/0.cpp (63%) rename dslings/en/cpp11/04-rvalue-references.cpp => en/cpp11/tests/04-rvalue-references/0.cpp (89%) rename dslings/en/cpp11/05-move-semantics-0.cpp => en/cpp11/tests/05-move-semantics/0.cpp (83%) rename dslings/en/cpp11/05-move-semantics-1.cpp => en/cpp11/tests/05-move-semantics/1.cpp (87%) rename dslings/en/cpp11/05-move-semantics-2.cpp => en/cpp11/tests/05-move-semantics/2.cpp (85%) rename dslings/en/cpp11/06-scoped-enums-0.cpp => en/cpp11/tests/06-scoped-enums/0.cpp (78%) rename dslings/en/cpp11/06-scoped-enums-1.cpp => en/cpp11/tests/06-scoped-enums/1.cpp (70%) rename dslings/en/cpp11/07-constexpr-0.cpp => en/cpp11/tests/07-constexpr/0.cpp (84%) rename dslings/en/cpp11/07-constexpr-1.cpp => en/cpp11/tests/07-constexpr/1.cpp (94%) rename dslings/en/cpp11/08-literal-type-0.cpp => en/cpp11/tests/08-literal-type/0.cpp (91%) rename dslings/en/cpp11/08-literal-type-1.cpp => en/cpp11/tests/08-literal-type/1.cpp (87%) rename dslings/en/cpp11/09-list-initialization-0.cpp => en/cpp11/tests/09-list-initialization/0.cpp (61%) rename dslings/en/cpp11/09-list-initialization-1.cpp => en/cpp11/tests/09-list-initialization/1.cpp (80%) rename dslings/en/cpp11/09-list-initialization-2.cpp => en/cpp11/tests/09-list-initialization/2.cpp (74%) rename dslings/en/cpp11/09-list-initialization-3.cpp => en/cpp11/tests/09-list-initialization/3.cpp (82%) rename dslings/en/cpp11/10-delegating-constructors-0.cpp => en/cpp11/tests/10-delegating-constructors/0.cpp (77%) rename dslings/en/cpp11/10-delegating-constructors-1.cpp => en/cpp11/tests/10-delegating-constructors/1.cpp (85%) rename dslings/en/cpp11/11-inherited-constructors-0.cpp => en/cpp11/tests/11-inherited-constructors/0.cpp (92%) rename dslings/en/cpp11/11-inherited-constructors-1.cpp => en/cpp11/tests/11-inherited-constructors/1.cpp (62%) rename dslings/en/cpp11/11-inherited-constructors-2.cpp => en/cpp11/tests/11-inherited-constructors/2.cpp (80%) rename dslings/en/cpp11/12-nullptr-0.cpp => en/cpp11/tests/12-nullptr/0.cpp (70%) rename dslings/en/cpp11/12-nullptr-1.cpp => en/cpp11/tests/12-nullptr/1.cpp (75%) rename dslings/en/cpp11/12-nullptr-2.cpp => en/cpp11/tests/12-nullptr/2.cpp (92%) rename dslings/en/cpp11/13-long-long-0.cpp => en/cpp11/tests/13-long-long/0.cpp (77%) rename dslings/en/cpp11/13-long-long-1.cpp => en/cpp11/tests/13-long-long/1.cpp (65%) rename dslings/en/cpp11/14-type-alias-0.cpp => en/cpp11/tests/14-type-alias/0.cpp (72%) rename dslings/en/cpp11/14-type-alias-1.cpp => en/cpp11/tests/14-type-alias/1.cpp (78%) rename dslings/en/cpp11/14-type-alias-2.cpp => en/cpp11/tests/14-type-alias/2.cpp (70%) rename dslings/en/cpp11/14-type-alias-3.cpp => en/cpp11/tests/14-type-alias/3.cpp (82%) rename dslings/en/cpp11/15-variadic-templates-0.cpp => en/cpp11/tests/15-variadic-templates/0.cpp (86%) rename dslings/en/cpp11/15-variadic-templates-1.cpp => en/cpp11/tests/15-variadic-templates/1.cpp (77%) rename dslings/en/cpp11/16-generalized-unions-0.cpp => en/cpp11/tests/16-generalized-unions/0.cpp (84%) rename dslings/en/cpp11/16-generalized-unions-1.cpp => en/cpp11/tests/16-generalized-unions/1.cpp (85%) rename dslings/en/cpp11/16-generalized-unions-2.cpp => en/cpp11/tests/16-generalized-unions/2.cpp (81%) rename dslings/en/cpp11/17-pod-type-0.cpp => en/cpp11/tests/17-pod-type/0.cpp (87%) rename dslings/en/cpp11/17-pod-type-1.cpp => en/cpp11/tests/17-pod-type/1.cpp (72%) rename dslings/en/cpp11/17-pod-type-2.cpp => en/cpp11/tests/17-pod-type/2.cpp (77%) create mode 100644 en/cpp14/mcpp.toml rename dslings/en/cpp14/00-generic-lambdas-0.cpp => en/cpp14/tests/00-generic-lambdas/0.cpp (57%) rename dslings/en/cpp14/00-generic-lambdas-1.cpp => en/cpp14/tests/00-generic-lambdas/1.cpp (75%) create mode 100644 en/intro/mcpp.toml rename {dslings/en => en/intro/tests}/hello-mcpp.cpp (90%) create mode 100644 intro/mcpp.toml rename {dslings => intro/tests}/hello-mcpp.cpp (90%) rename solutions/cpp11/{00-auto-and-decltype-0.cpp => 00-auto-and-decltype/0.cpp} (70%) rename solutions/cpp11/{00-auto-and-decltype-1.cpp => 00-auto-and-decltype/1.cpp} (73%) rename solutions/cpp11/{00-auto-and-decltype-2.cpp => 00-auto-and-decltype/2.cpp} (83%) rename solutions/cpp11/{00-auto-and-decltype-3.cpp => 00-auto-and-decltype/3.cpp} (68%) rename solutions/cpp11/{00-auto-and-decltype-4.cpp => 00-auto-and-decltype/4.cpp} (65%) rename solutions/cpp11/{00-auto-and-decltype-5.cpp => 00-auto-and-decltype/5.cpp} (72%) rename solutions/cpp11/{01-default-and-delete-0.cpp => 01-default-and-delete/0.cpp} (93%) rename solutions/cpp11/{01-default-and-delete-1.cpp => 01-default-and-delete/1.cpp} (57%) rename solutions/cpp11/{01-default-and-delete-2.cpp => 01-default-and-delete/2.cpp} (91%) rename solutions/cpp11/{02-final-and-override-0.cpp => 02-final-and-override/0.cpp} (93%) rename solutions/cpp11/{02-final-and-override-1.cpp => 02-final-and-override/1.cpp} (69%) rename solutions/cpp11/{02-final-and-override-2.cpp => 02-final-and-override/2.cpp} (96%) rename solutions/cpp11/{03-trailing-return-type.cpp => 03-trailing-return-type/0.cpp} (64%) rename solutions/cpp11/{04-rvalue-references.cpp => 04-rvalue-references/0.cpp} (92%) rename solutions/cpp11/{05-move-semantics-0.cpp => 05-move-semantics/0.cpp} (86%) rename solutions/cpp11/{05-move-semantics-1.cpp => 05-move-semantics/1.cpp} (88%) rename solutions/cpp11/{05-move-semantics-2.cpp => 05-move-semantics/2.cpp} (86%) rename solutions/cpp11/{06-scoped-enums-0.cpp => 06-scoped-enums/0.cpp} (89%) rename solutions/cpp11/{06-scoped-enums-1.cpp => 06-scoped-enums/1.cpp} (69%) rename solutions/cpp11/{07-constexpr-0.cpp => 07-constexpr/0.cpp} (89%) rename solutions/cpp11/{07-constexpr-1.cpp => 07-constexpr/1.cpp} (97%) rename solutions/cpp11/{08-literal-type-0.cpp => 08-literal-type/0.cpp} (95%) rename solutions/cpp11/{08-literal-type-1.cpp => 08-literal-type/1.cpp} (92%) rename solutions/cpp11/{09-list-initialization-0.cpp => 09-list-initialization/0.cpp} (67%) rename solutions/cpp11/{09-list-initialization-1.cpp => 09-list-initialization/1.cpp} (84%) rename solutions/cpp11/{09-list-initialization-2.cpp => 09-list-initialization/2.cpp} (71%) rename solutions/cpp11/{09-list-initialization-3.cpp => 09-list-initialization/3.cpp} (86%) rename solutions/cpp11/{10-delegating-constructors-0.cpp => 10-delegating-constructors/0.cpp} (83%) rename solutions/cpp11/{10-delegating-constructors-1.cpp => 10-delegating-constructors/1.cpp} (89%) rename solutions/cpp11/{11-inherited-constructors-0.cpp => 11-inherited-constructors/0.cpp} (96%) rename solutions/cpp11/{11-inherited-constructors-1.cpp => 11-inherited-constructors/1.cpp} (63%) rename solutions/cpp11/{11-inherited-constructors-2.cpp => 11-inherited-constructors/2.cpp} (84%) rename solutions/cpp11/{12-nullptr-0.cpp => 12-nullptr/0.cpp} (66%) rename solutions/cpp11/{12-nullptr-1.cpp => 12-nullptr/1.cpp} (77%) rename solutions/cpp11/{12-nullptr-2.cpp => 12-nullptr/2.cpp} (94%) rename solutions/cpp11/{13-long-long-0.cpp => 13-long-long/0.cpp} (77%) rename solutions/cpp11/{13-long-long-1.cpp => 13-long-long/1.cpp} (62%) rename solutions/cpp11/{14-type-alias-0.cpp => 14-type-alias/0.cpp} (68%) rename solutions/cpp11/{14-type-alias-1.cpp => 14-type-alias/1.cpp} (76%) rename solutions/cpp11/{14-type-alias-2.cpp => 14-type-alias/2.cpp} (68%) rename solutions/cpp11/{14-type-alias-3.cpp => 14-type-alias/3.cpp} (85%) rename solutions/cpp11/{15-variadic-templates-0.cpp => 15-variadic-templates/0.cpp} (86%) rename solutions/cpp11/{15-variadic-templates-1.cpp => 15-variadic-templates/1.cpp} (81%) rename solutions/cpp11/{16-generalized-unions-0.cpp => 16-generalized-unions/0.cpp} (87%) rename solutions/cpp11/{16-generalized-unions-1.cpp => 16-generalized-unions/1.cpp} (88%) rename solutions/cpp11/{16-generalized-unions-2.cpp => 16-generalized-unions/2.cpp} (75%) rename solutions/cpp11/{17-pod-type-0.cpp => 17-pod-type/0.cpp} (90%) rename solutions/cpp11/{17-pod-type-1.cpp => 17-pod-type/1.cpp} (66%) rename solutions/cpp11/{17-pod-type-2.cpp => 17-pod-type/2.cpp} (73%) diff --git a/cpp11/mcpp.toml b/cpp11/mcpp.toml new file mode 100644 index 0000000..97c1c0c --- /dev/null +++ b/cpp11/mcpp.toml @@ -0,0 +1,10 @@ +# cpp11 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: +# mcpp test -p cpp11 全部练习(进度表) +# mcpp test -p cpp11 <子串> 只跑匹配的练习 +[package] +name = "cpp11" +version = "0.1.0" +standard = "c++23" + +[dependencies] +harness = { path = "../harness" } diff --git a/dslings/cpp11/00-auto-and-decltype-0.cpp b/cpp11/tests/00-auto-and-decltype/0.cpp similarity index 73% rename from dslings/cpp11/00-auto-and-decltype-0.cpp rename to cpp11/tests/00-auto-and-decltype/0.cpp index 3e8e3a4..77c4b50 100644 --- a/dslings/cpp11/00-auto-and-decltype-0.cpp +++ b/cpp11/tests/00-auto-and-decltype/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-0.cpp +// file: cpp11/tests/00-auto-and-decltype/0.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 自动类型推导 // @@ -18,7 +18,8 @@ // d2x checker auto-and-decltype // -#include +import std; +import d2x.harness; int main() { @@ -35,14 +36,13 @@ int main() { D2X_YOUR_ANSWER c1 = c; D2X_YOUR_ANSWER c2 = c; - d2x_assert_eq(a, a1); - d2x_assert_eq(a1, a2); - d2x_assert_eq(b, b1); - d2x_assert_eq(b1, b2); - d2x_assert_eq(c, c1); - d2x_assert_eq(c1, c2); - - D2X_WAIT + d2x::check_eq(a, a1, "a == a1"); + d2x::check_eq(a1, a2, "a1 == a2"); + d2x::check_eq(b, b1, "b == b1"); + d2x::check_eq(b1, b2, "b1 == b2"); + d2x::check_eq(c, c1, "c == c1"); + d2x::check_eq(c1, c2, "c1 == c2"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/00-auto-and-decltype-1.cpp b/cpp11/tests/00-auto-and-decltype/1.cpp similarity index 75% rename from dslings/cpp11/00-auto-and-decltype-1.cpp rename to cpp11/tests/00-auto-and-decltype/1.cpp index 29d76f9..32feffe 100644 --- a/dslings/cpp11/00-auto-and-decltype-1.cpp +++ b/cpp11/tests/00-auto-and-decltype/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-1.cpp +// file: cpp11/tests/00-auto-and-decltype/1.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 表达式类型推导 // @@ -19,7 +19,8 @@ // -#include +import std; +import d2x.harness; int main() { @@ -36,12 +37,11 @@ int main() { D2X_YOUR_ANSWER c1 = 1 + c; D2X_YOUR_ANSWER c2 = 2 + 'a'; - d2x_assert_eq(a2, a + 2 + 1.1); - d2x_assert_eq(b1, a + 0.1); - d2x_assert_eq(c1, 1 + c); - d2x_assert_eq(c2, 2 + 'a'); - - D2X_WAIT + d2x::check_eq(a2, a + 2 + 1.1, "a2 == a + 2 + 1.1"); + d2x::check_eq(b1, a + 0.1, "b1 == a + 0.1"); + d2x::check_eq(c1, 1 + c, "c1 == 1 + c"); + d2x::check_eq(c2, 2 + 'a', "c2 == 2 + 'a'"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/00-auto-and-decltype-2.cpp b/cpp11/tests/00-auto-and-decltype/2.cpp similarity index 84% rename from dslings/cpp11/00-auto-and-decltype-2.cpp rename to cpp11/tests/00-auto-and-decltype/2.cpp index 8b3fa4c..6ae104c 100644 --- a/dslings/cpp11/00-auto-and-decltype-2.cpp +++ b/cpp11/tests/00-auto-and-decltype/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-2.cpp +// file: cpp11/tests/00-auto-and-decltype/2.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 复杂类型推导 // @@ -18,11 +18,9 @@ // d2x checker auto-and-decltype-2 // -#include +import std; +import d2x.harness; -#include -#include -#include int add_func(int a, int b) { return a + b; @@ -53,10 +51,9 @@ int main() { minus_func }; - d2x_assert_eq(funcVec[0](1, 2), 3); - d2x_assert_eq(funcVec[1](1, 2), -1); - - D2X_WAIT + d2x::check_eq(funcVec[0](1, 2), 3, "funcVec[0](1, 2) == 3"); + d2x::check_eq(funcVec[1](1, 2), -1, "funcVec[1](1, 2) == -1"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/00-auto-and-decltype-3.cpp b/cpp11/tests/00-auto-and-decltype/3.cpp similarity index 74% rename from dslings/cpp11/00-auto-and-decltype-3.cpp rename to cpp11/tests/00-auto-and-decltype/3.cpp index a75fc96..299ab26 100644 --- a/dslings/cpp11/00-auto-and-decltype-3.cpp +++ b/cpp11/tests/00-auto-and-decltype/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-3.cpp +// file: cpp11/tests/00-auto-and-decltype/3.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 函数返回值类型推导 // @@ -18,10 +18,9 @@ // d2x checker auto-and-decltype-3 // -#include +import std; +import d2x.harness; -#include -#include // 3. 函数返回值类型 @@ -36,11 +35,10 @@ D2X_YOUR_ANSWER minus_func(T1 a, T2 b) -> D2X_YOUR_ANSWER { int main() { - d2x_assert_eq(minus_func(1, 2), -1); - d2x_assert_eq(minus_func(2, 1), 1); - d2x_assert_eq(minus_func(1, 2.1), -1.1); - - D2X_WAIT + d2x::check_eq(minus_func(1, 2), -1, "minus_func(1, 2) == -1"); + d2x::check_eq(minus_func(2, 1), 1, "minus_func(2, 1) == 1"); + d2x::check_eq(minus_func(1, 2.1), -1.1, "minus_func(1, 2.1) == -1.1"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/00-auto-and-decltype-4.cpp b/cpp11/tests/00-auto-and-decltype/4.cpp similarity index 70% rename from dslings/cpp11/00-auto-and-decltype-4.cpp rename to cpp11/tests/00-auto-and-decltype/4.cpp index aadb47f..f4f3d64 100644 --- a/dslings/cpp11/00-auto-and-decltype-4.cpp +++ b/cpp11/tests/00-auto-and-decltype/4.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-4.cpp +// file: cpp11/tests/00-auto-and-decltype/4.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 类/结构体成员类型推导 // @@ -18,9 +18,9 @@ // d2x checker auto-and-decltype-4 // -#include +import std; +import d2x.harness; -#include // 4. 类/结构体成员类型推导 @@ -38,23 +38,22 @@ int main() { // obj的类型推导 和 (obj) 的类型推导 type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // obj.a的类型推导 和 (obj.a) 的类型推导 type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // obj.b的类型推导 和 (obj.b) 的类型推导 type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line - - D2X_WAIT + d2x::check(type_check, "type_check"); type_check = false; // dont change this line + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/00-auto-and-decltype-5.cpp b/cpp11/tests/00-auto-and-decltype/5.cpp similarity index 76% rename from dslings/cpp11/00-auto-and-decltype-5.cpp rename to cpp11/tests/00-auto-and-decltype/5.cpp index 0cd35bd..2006c8b 100644 --- a/dslings/cpp11/00-auto-and-decltype-5.cpp +++ b/cpp11/tests/00-auto-and-decltype/5.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-5.cpp +// file: cpp11/tests/00-auto-and-decltype/5.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | const 与引用的剥离和保留 // @@ -18,9 +18,9 @@ // d2x checker auto-and-decltype-5 // -#include +import std; +import d2x.harness; -#include // 5. const 与引用的剥离和保留 @@ -35,24 +35,23 @@ int main() { // auto 剥离顶层 const: a 推导成什么? auto a = ci; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // auto 剥离引用: b 推导成什么?(b 是 n 的独立副本) auto b = ri; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // 想保留 const 引用 const int&: cr 该怎么声明? D2X_YOUR_ANSWER cr = ci; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // 想精确保留声明类型 const int: d 该怎么声明? D2X_YOUR_ANSWER d = ci; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line - - D2X_WAIT + d2x::check(type_check, "type_check"); type_check = false; // dont change this line + d2x::wait(); return 0; } diff --git a/dslings/cpp11/01-default-and-delete-0.cpp b/cpp11/tests/01-default-and-delete/0.cpp similarity index 88% rename from dslings/cpp11/01-default-and-delete-0.cpp rename to cpp11/tests/01-default-and-delete/0.cpp index 63d45da..034f754 100644 --- a/dslings/cpp11/01-default-and-delete-0.cpp +++ b/cpp11/tests/01-default-and-delete/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/01-default-and-delete-0.cpp +// file: cpp11/tests/01-default-and-delete/0.cpp // // Exercise/练习: cpp11 | 01 - default and delete | 显示指定构造函数生成行为 // @@ -15,9 +15,9 @@ // d2x checker default-and-delete // -#include +import std; +import d2x.harness; -#include // default和delete显式控制 -> 编译器默认构造函数的生成行为 struct A { }; @@ -35,7 +35,6 @@ int main() { // 不要直接修改main函数中的代码 B b; C c(1); - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/01-default-and-delete-1.cpp b/cpp11/tests/01-default-and-delete/1.cpp similarity index 62% rename from dslings/cpp11/01-default-and-delete-1.cpp rename to cpp11/tests/01-default-and-delete/1.cpp index a0b4e58..2d0bd88 100644 --- a/dslings/cpp11/01-default-and-delete-1.cpp +++ b/cpp11/tests/01-default-and-delete/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/01-default-and-delete-1.cpp +// file: cpp11/tests/01-default-and-delete/1.cpp // // Exercise/练习: cpp11 | 01 - default and delete | 不可拷贝对像 // @@ -15,9 +15,9 @@ // d2x checker default-and-delete-1 // -#include +import std; +import d2x.harness; -#include // 实现std::unique_ptr不可以拷贝, 但可以移动的属性 struct UniquePtr { @@ -32,17 +32,16 @@ int main() { // 不要直接修改main函数中的代码 // 对像不可拷贝/复制 // std::unique_ptr b = a; // error - d2x_assert(std::is_copy_constructible::value == false); + d2x::check(std::is_copy_constructible::value == false, "std::is_copy_constructible::value == false"); // a = b; // error - d2x_assert(std::is_copy_assignable::value == false); + d2x::check(std::is_copy_assignable::value == false, "std::is_copy_assignable::value == false"); // 对像可移动 // std::unique_ptr c = std::move(a); // ok - d2x_assert(std::is_move_constructible::value == true); + d2x::check(std::is_move_constructible::value == true, "std::is_move_constructible::value == true"); // a = std::move(c); // ok - d2x_assert(std::is_move_assignable::value == true); - - D2X_WAIT + d2x::check(std::is_move_assignable::value == true, "std::is_move_assignable::value == true"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/01-default-and-delete-2.cpp b/cpp11/tests/01-default-and-delete/2.cpp similarity index 85% rename from dslings/cpp11/01-default-and-delete-2.cpp rename to cpp11/tests/01-default-and-delete/2.cpp index 7f57468..3a77334 100644 --- a/dslings/cpp11/01-default-and-delete-2.cpp +++ b/cpp11/tests/01-default-and-delete/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/01-default-and-delete-2.cpp +// file: cpp11/tests/01-default-and-delete/2.cpp // // Exercise/练习: cpp11 | 01 - default and delete | 禁止函数重载 // @@ -15,9 +15,9 @@ // d2x checker default-and-delete-2 // -#include +import std; +import d2x.harness; -#include void func(int x) { std::cout << "x = " << x << std::endl; @@ -31,7 +31,6 @@ int main() { func(1); // int func(1.1f); // float - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/02-final-and-override-0.cpp b/cpp11/tests/02-final-and-override/0.cpp similarity index 88% rename from dslings/cpp11/02-final-and-override-0.cpp rename to cpp11/tests/02-final-and-override/0.cpp index f676de1..d9e7900 100644 --- a/dslings/cpp11/02-final-and-override-0.cpp +++ b/cpp11/tests/02-final-and-override/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/02-final-and-override-0.cpp +// file: cpp11/tests/02-final-and-override/0.cpp // // Exercise/练习: cpp11 | 02 - final and override // @@ -15,10 +15,9 @@ // d2x checker final-and-override // -#include +import std; +import d2x.harness; -#include -#include struct A { virtual void func1() { @@ -51,7 +50,6 @@ int main() { a->func1(); // B::func1() a->func2(); // A::func2() - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/02-final-and-override-1.cpp b/cpp11/tests/02-final-and-override/1.cpp similarity index 66% rename from dslings/cpp11/02-final-and-override-1.cpp rename to cpp11/tests/02-final-and-override/1.cpp index 104b886..b76d0a8 100644 --- a/dslings/cpp11/02-final-and-override-1.cpp +++ b/cpp11/tests/02-final-and-override/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/02-final-and-override-1.cpp +// file: cpp11/tests/02-final-and-override/1.cpp // // Exercise/练习: cpp11 | 02 - final and override // @@ -15,10 +15,9 @@ // d2x checker final-and-override-1 // -#include +import std; +import d2x.harness; -#include -#include struct A { virtual int func1() final { @@ -45,14 +44,13 @@ struct C : B { int main() { B final; // 不要直接修改main函数中的代码 - d2x_assert_eq(final.func1(), 3); // B::func1() - d2x_assert_eq(final.func2(), 4); // B::func2() + d2x::check_eq(final.func1(), 3, "final.func1() == 3"); // B::func1() + d2x::check_eq(final.func2(), 4, "final.func2() == 4"); // B::func2() A *a = &final; - d2x_assert_eq(a->func1(), 3); // B::func1() - d2x_assert_eq(a->func2(), 2); // A::func2() - - D2X_WAIT + d2x::check_eq(a->func1(), 3, "a->func1() == 3"); // B::func1() + d2x::check_eq(a->func2(), 2, "a->func2() == 2"); // A::func2() + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/02-final-and-override-2.cpp b/cpp11/tests/02-final-and-override/2.cpp similarity index 93% rename from dslings/cpp11/02-final-and-override-2.cpp rename to cpp11/tests/02-final-and-override/2.cpp index 51f08da..122be66 100644 --- a/dslings/cpp11/02-final-and-override-2.cpp +++ b/cpp11/tests/02-final-and-override/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/02-final-and-override-2.cpp +// file: cpp11/tests/02-final-and-override/2.cpp // // Exercise/练习: cpp11 | 02 - final and override // @@ -15,10 +15,9 @@ // d2x checker final-and-override-2 // -#include +import std; +import d2x.harness; -#include -#include struct AudioPlayer { // 不要直接修改AudioPlayer类 virtual void play() final { @@ -77,7 +76,6 @@ int main() { // 不要直接修改main函数中的代码 delete player2; delete player3; - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/03-trailing-return-type.cpp b/cpp11/tests/03-trailing-return-type/0.cpp similarity index 64% rename from dslings/cpp11/03-trailing-return-type.cpp rename to cpp11/tests/03-trailing-return-type/0.cpp index 06c0c46..158a76b 100644 --- a/dslings/cpp11/03-trailing-return-type.cpp +++ b/cpp11/tests/03-trailing-return-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/03-trailing-return-type.cpp +// file: cpp11/tests/03-trailing-return-type/0.cpp // // Exercise/练习: cpp11 | 03 - trailing return type // @@ -14,9 +14,9 @@ // d2x checker trailing-return-type // -#include +import std; +import d2x.harness; -#include int add0(double a, int b) { return a + b; @@ -37,13 +37,12 @@ auto add3 = [](double a, double b) -> D2X_YOUR_ANSWER { int main() { - d2x_assert_eq(add0(1.1, 2), 3); - d2x_assert_eq(add1(1.1, 2), 3); - d2x_assert_eq(add2(1.1, 2), 3.1); - d2x_assert_eq(add2(1, 2.1), 3.1); - d2x_assert_eq(add3(1.1, 2.1), 3); - - D2X_WAIT + d2x::check_eq(add0(1.1, 2), 3, "add0(1.1, 2) == 3"); + d2x::check_eq(add1(1.1, 2), 3, "add1(1.1, 2) == 3"); + d2x::check_eq(add2(1.1, 2), 3.1, "add2(1.1, 2) == 3.1"); + d2x::check_eq(add2(1, 2.1), 3.1, "add2(1, 2.1) == 3.1"); + d2x::check_eq(add3(1.1, 2.1), 3, "add3(1.1, 2.1) == 3"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/04-rvalue-references.cpp b/cpp11/tests/04-rvalue-references/0.cpp similarity index 89% rename from dslings/cpp11/04-rvalue-references.cpp rename to cpp11/tests/04-rvalue-references/0.cpp index b916830..f16992c 100644 --- a/dslings/cpp11/04-rvalue-references.cpp +++ b/cpp11/tests/04-rvalue-references/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/04-rvalue-references.cpp +// file: cpp11/tests/04-rvalue-references/0.cpp // // Exercise/练习: cpp11 | 04 - rvalue references // @@ -14,10 +14,9 @@ // d2x checker rvalue-references // -#include +import std; +import d2x.harness; -#include -#include struct Object; static Object * object_address = nullptr; @@ -56,13 +55,12 @@ int main() { // 关闭编译器优化 objRef.data = 1; // 修改被延长生命周期的临时对象的值(不要直接改动这行代码) std::cout << "objRef.data = " << objRef.data << " - " << &objRef << std::endl; - d2x_assert((&objRef == object_address)); + d2x::check((&objRef == object_address), "(&objRef == object_address)"); // 钉住移动构造确实发生过。教学漂移之所以能静默发生, 正是因为 // 从前没有任何断言检查它 —— 输出少了一行, 没人发现。 - d2x_assert((move_ctor_calls >= 1)); + d2x::check((move_ctor_calls >= 1), "(move_ctor_calls >= 1)"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/05-move-semantics-0.cpp b/cpp11/tests/05-move-semantics/0.cpp similarity index 83% rename from dslings/cpp11/05-move-semantics-0.cpp rename to cpp11/tests/05-move-semantics/0.cpp index 2ee7f3a..982c807 100644 --- a/dslings/cpp11/05-move-semantics-0.cpp +++ b/cpp11/tests/05-move-semantics/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/05-move-semantics-0.cpp +// file: cpp11/tests/05-move-semantics/0.cpp // // Exercise/练习: cpp11 | 05 - move semantics | 移动构造与触发时机 // @@ -14,9 +14,9 @@ // d2x checker move-semantics // -#include +import std; +import d2x.harness; -#include struct Buffer { int *data; @@ -57,20 +57,19 @@ int main() { Buffer buff2(std::move(buff1)); auto buff2DataPtr = buff2.data_ptr(); - d2x_assert(buff1DataPtr == buff2DataPtr); + d2x::check(buff1DataPtr == buff2DataPtr, "buff1DataPtr == buff2DataPtr"); Buffer buff3 = buff2; auto buff3DataPtr = buff3.data_ptr(); - d2x_assert(buff2DataPtr == buff3DataPtr); + d2x::check(buff2DataPtr == buff3DataPtr, "buff2DataPtr == buff3DataPtr"); Buffer buff4 = process(buff3); auto buff4DataPtr = buff4.data_ptr(); - d2x_assert(buff3DataPtr == buff4DataPtr); + d2x::check(buff3DataPtr == buff4DataPtr, "buff3DataPtr == buff4DataPtr"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/05-move-semantics-1.cpp b/cpp11/tests/05-move-semantics/1.cpp similarity index 87% rename from dslings/cpp11/05-move-semantics-1.cpp rename to cpp11/tests/05-move-semantics/1.cpp index 4bbda9b..ab08a16 100644 --- a/dslings/cpp11/05-move-semantics-1.cpp +++ b/cpp11/tests/05-move-semantics/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/05-move-semantics-0.cpp +// file: cpp11/tests/05-move-semantics/1.cpp // // Exercise/练习: cpp11 | 05 - move semantics | 移动赋值与触发时机 // @@ -14,9 +14,9 @@ // d2x checker move-semantics // -#include +import std; +import d2x.harness; -#include static int move_assignment_counter = 0; @@ -80,21 +80,20 @@ int main() { // 无编译器优化 buff1 = Buffer(); // 情况1: 临时对象赋值 - d2x_assert_eq(move_assignment_counter, 1); + d2x::check_eq(move_assignment_counter, 1, "move_assignment_counter == 1"); Buffer buff2; buff2 = process(buff1); // 情况2: 中间对象赋值 - d2x_assert_eq(move_assignment_counter, 2); + d2x::check_eq(move_assignment_counter, 2, "move_assignment_counter == 2"); buff2 = buff1; // 情况3: 显示移动赋值 - d2x_assert_eq(move_assignment_counter, 3); + d2x::check_eq(move_assignment_counter, 3, "move_assignment_counter == 3"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/05-move-semantics-2.cpp b/cpp11/tests/05-move-semantics/2.cpp similarity index 85% rename from dslings/cpp11/05-move-semantics-2.cpp rename to cpp11/tests/05-move-semantics/2.cpp index 3c25e8a..651aa03 100644 --- a/dslings/cpp11/05-move-semantics-2.cpp +++ b/cpp11/tests/05-move-semantics/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/05-move-semantics-2.cpp +// file: cpp11/tests/05-move-semantics/2.cpp // // Exercise/练习: cpp11 | 05 - move semantics | 移动的是资源而不是对象 // @@ -14,9 +14,9 @@ // d2x checker move-semantics-2 // -#include +import std; +import d2x.harness; -#include struct Buffer { int *data; @@ -72,13 +72,12 @@ int main() { // 移动语义 - 移动的是资源而不是对象演示 Buffer b2 = b1; // std::move(b1); - d2x_assert(&b1 != &b2); // b1 和 b2 是不同的对象 - d2x_assert(old_b1_data_ptr == b2.data_ptr()); - d2x_assert(b1.data_ptr() == nullptr); // b1 的资源被移动了 + d2x::check(&b1 != &b2, "&b1 != &b2"); // b1 和 b2 是不同的对象 + d2x::check(old_b1_data_ptr == b2.data_ptr(), "old_b1_data_ptr == b2.data_ptr()"); + d2x::check(b1.data_ptr() == nullptr, "b1.data_ptr() == nullptr"); // b1 的资源被移动了 } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/06-scoped-enums-0.cpp b/cpp11/tests/06-scoped-enums/0.cpp similarity index 78% rename from dslings/cpp11/06-scoped-enums-0.cpp rename to cpp11/tests/06-scoped-enums/0.cpp index 1aa8b5a..e217071 100644 --- a/dslings/cpp11/06-scoped-enums-0.cpp +++ b/cpp11/tests/06-scoped-enums/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/06-scoped-enums-0.cpp +// file: cpp11/tests/06-scoped-enums/0.cpp // // Exercise/练习: cpp11 | 06 - scoped enums | 传统枚举类型潜在问题 // @@ -14,9 +14,9 @@ // d2x checker scoped-enums // -#include +import std; +import d2x.harness; -#include enum Color { RED, @@ -36,20 +36,19 @@ int main() { Color color = RED; Fruit fruit = Apple; - d2x_assert_eq(color, RED); - d2x_assert_eq(fruit, Apple); + d2x::check_eq(color, RED, "color == RED"); + d2x::check_eq(fruit, Apple, "fruit == Apple"); // 2.符合语法, 但逻辑错误的类型匹配 if (color == Apple) { // 不要删除这行代码 // 代码会运行到这里 - D2X_WAIT + d2x::wait(); } if (fruit == RED) { - D2X_WAIT + d2x::wait(); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/06-scoped-enums-1.cpp b/cpp11/tests/06-scoped-enums/1.cpp similarity index 69% rename from dslings/cpp11/06-scoped-enums-1.cpp rename to cpp11/tests/06-scoped-enums/1.cpp index 980ed3b..4f3cacd 100644 --- a/dslings/cpp11/06-scoped-enums-1.cpp +++ b/cpp11/tests/06-scoped-enums/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/06-scoped-enums-1.cpp +// file: cpp11/tests/06-scoped-enums/1.cpp // // Exercise/练习: cpp11 | 06 - scoped enums | 范围枚举类型基本用法 // @@ -14,9 +14,9 @@ // d2x checker scoped-enums-1 // -#include +import std; +import d2x.harness; -#include enum class Color { RED, @@ -37,12 +37,12 @@ int main() { Color color = Color::ORANGE; Fruit fruit = Fruit::ORANGE; - d2x_assert(color == Color::ORANGE); - d2x_assert(fruit == Fruit::ORANGE); + d2x::check(color == Color::ORANGE, "color == Color::ORANGE"); + d2x::check(fruit == Fruit::ORANGE, "fruit == Fruit::ORANGE"); // 2.类型安全: 防止不同类型的枚举值之间的比较 if (color == Fruit::ORANGE) { // 使用Color类型修复编译错误 - d2x_assert(color == Color::ORANGE); + d2x::check(color == Color::ORANGE, "color == Color::ORANGE"); } // 3.类型检查: 默认情况下, 范围枚举类型的值是不可隐式转换 @@ -56,8 +56,8 @@ int main() { ORANGE // 橙色 }; - d2x_assert_eq(sizeof(Color), sizeof(int)); // 默认类型是int - d2x_assert_eq(sizeof(Color8Bit), sizeof(int8_t)); // 可自定义类型int8_t + d2x::check_eq(sizeof(Color), sizeof(int), "sizeof(Color) == sizeof(int)"); // 默认类型是int + d2x::check_eq(sizeof(Color8Bit), sizeof(int8_t), "sizeof(Color8Bit) == sizeof(int8_t)"); // 可自定义类型int8_t // 5.自定义起始值: 默认情况下, 范围枚举类型的值从0开始, 往下递增 enum class ErrorCode : int { @@ -67,9 +67,8 @@ int main() { ERROR_3 }; - d2x_assert_eq(static_cast(ErrorCode::ERROR_3), 3); - - D2X_WAIT + d2x::check_eq(static_cast(ErrorCode::ERROR_3), 3, "static_cast(ErrorCode::ERROR_3) == 3"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/07-constexpr-0.cpp b/cpp11/tests/07-constexpr/0.cpp similarity index 84% rename from dslings/cpp11/07-constexpr-0.cpp rename to cpp11/tests/07-constexpr/0.cpp index 8c67383..3ac2086 100644 --- a/dslings/cpp11/07-constexpr-0.cpp +++ b/cpp11/tests/07-constexpr/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/07-constexpr-0.cpp +// file: cpp11/tests/07-constexpr/0.cpp // // Exercise/练习: cpp11 | 07 - constexpr | 编译期计算基础: constexpr 和 const的区别 // @@ -14,9 +14,9 @@ // d2x checker constexpr // -#include +import std; +import d2x.harness; -#include int sum_for_1_to(int n) { return n == 1 ? 1 : n + sum_for_1_to(n - 1); @@ -34,10 +34,9 @@ int main() { { // 2. 编译期计算基础 constexpr int s = sum_for_1_to(4); - d2x_assert_eq(s, 1 + 2 + 3 + 4); + d2x::check_eq(s, 1 + 2 + 3 + 4, "s == 1 + 2 + 3 + 4"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/07-constexpr-1.cpp b/cpp11/tests/07-constexpr/1.cpp similarity index 94% rename from dslings/cpp11/07-constexpr-1.cpp rename to cpp11/tests/07-constexpr/1.cpp index 1347c54..2a1ac25 100644 --- a/dslings/cpp11/07-constexpr-1.cpp +++ b/cpp11/tests/07-constexpr/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/07-constexpr-1.cpp +// file: cpp11/tests/07-constexpr/1.cpp // // Exercise/练习: cpp11 | 07 - constexpr | 编译期计算应用示例 // @@ -14,9 +14,9 @@ // d2x checker constexpr // -#include +import std; +import d2x.harness; -#include template struct Sum { @@ -67,7 +67,6 @@ int main() { constexpr double sin30 = mysin(30.0); std::cout << "mysin(30): " << sin30 << " " << std::endl; - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/08-literal-type-0.cpp b/cpp11/tests/08-literal-type/0.cpp similarity index 91% rename from dslings/cpp11/08-literal-type-0.cpp rename to cpp11/tests/08-literal-type/0.cpp index 5943da4..989b20d 100644 --- a/dslings/cpp11/08-literal-type-0.cpp +++ b/cpp11/tests/08-literal-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/08-literal-type-0.cpp +// file: cpp11/tests/08-literal-type/0.cpp // // Exercise/练习: cpp11 | 08 - literal type | 什么是字面值类型 // @@ -14,11 +14,9 @@ // d2x checker literal-type-0 // -#include +import std; +import d2x.harness; -#include -#include -#include constexpr char compile_time_compute(char c, int a) { return a + c; @@ -54,7 +52,6 @@ int main() { std::cout << "1 + 2 + 3 = " << sum << std::endl; - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/08-literal-type-1.cpp b/cpp11/tests/08-literal-type/1.cpp similarity index 87% rename from dslings/cpp11/08-literal-type-1.cpp rename to cpp11/tests/08-literal-type/1.cpp index 59299b4..929d04b 100644 --- a/dslings/cpp11/08-literal-type-1.cpp +++ b/cpp11/tests/08-literal-type/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/08-literal-type-1.cpp +// file: cpp11/tests/08-literal-type/1.cpp // // Exercise/练习: cpp11 | 08 - literal type | 自定义字面值类型 // @@ -14,9 +14,9 @@ // d2x checker literal-type-1 // -#include +import std; +import d2x.harness; -#include struct Vector { int x, y; @@ -34,7 +34,6 @@ int main() { std::cout << "[ " << v3.x << ", " << v3.y << " ]" << std::endl; - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/09-list-initialization-0.cpp b/cpp11/tests/09-list-initialization/0.cpp similarity index 62% rename from dslings/cpp11/09-list-initialization-0.cpp rename to cpp11/tests/09-list-initialization/0.cpp index 254fc7a..e9e0cef 100644 --- a/dslings/cpp11/09-list-initialization-0.cpp +++ b/cpp11/tests/09-list-initialization/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/09-list-initialization-0.cpp +// file: cpp11/tests/09-list-initialization/0.cpp // // Exercise/练习: cpp11 | 09 - list initialization | 窄化检查 // @@ -15,19 +15,19 @@ // d2x checker list-initialization // -#include +import std; +import d2x.harness; -#include int main() { int a1 = 1.1; - d2x_assert_eq(a1, 1); + d2x::check_eq(a1, 1, "a1 == 1"); int a2 = 1.1; int a3 = { 1.1 }; - d2x_assert_eq(a2, 1); - d2x_assert_eq(a3, 1); + d2x::check_eq(a2, 1, "a2 == 1"); + d2x::check_eq(a3, 1, "a3 == 1"); double b1 { 1.1 }; constexpr double c1 { 2.2 }; @@ -35,18 +35,17 @@ int main() { int b2 { b1 }; int c2 { c1 }; - d2x_assert_eq(b2, 1); - d2x_assert_eq(c2, 2); + d2x::check_eq(b2, 1, "b2 == 1"); + d2x::check_eq(c2, 2, "c2 == 2"); int arr1[] = { 1, 2.2, 3 }; - d2x_assert_eq(arr1[1], 2); + d2x::check_eq(arr1[1], 2, "arr1[1] == 2"); int arr2[4] { 1, b1, c1 }; - d2x_assert_eq(arr2[1], 1); - d2x_assert_eq(arr2[2], 2); - d2x_assert_eq(arr2[3], 0); - - D2X_WAIT + d2x::check_eq(arr2[1], 1, "arr2[1] == 1"); + d2x::check_eq(arr2[2], 2, "arr2[2] == 2"); + d2x::check_eq(arr2[3], 0, "arr2[3] == 0"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/09-list-initialization-1.cpp b/cpp11/tests/09-list-initialization/1.cpp similarity index 81% rename from dslings/cpp11/09-list-initialization-1.cpp rename to cpp11/tests/09-list-initialization/1.cpp index f37ac9d..6fc9934 100644 --- a/dslings/cpp11/09-list-initialization-1.cpp +++ b/cpp11/tests/09-list-initialization/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/09-list-initialization-1.cpp +// file: cpp11/tests/09-list-initialization/1.cpp // // Exercise/练习: cpp11 | 09 - list initialization | 默认初始化语法陷阱 // @@ -15,9 +15,9 @@ // d2x checker list-initialization // -#include +import std; +import d2x.harness; -#include struct Object { Object() { @@ -35,10 +35,9 @@ int main() { Object obj1(); Object obj2(2); - d2x_assert_eq(obj1.x, 0); - d2x_assert_eq(obj2.x, 2); - - D2X_WAIT + d2x::check_eq(obj1.x, 0, "obj1.x == 0"); + d2x::check_eq(obj2.x, 2, "obj2.x == 2"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/09-list-initialization-2.cpp b/cpp11/tests/09-list-initialization/2.cpp similarity index 74% rename from dslings/cpp11/09-list-initialization-2.cpp rename to cpp11/tests/09-list-initialization/2.cpp index d1aee65..f37fe73 100644 --- a/dslings/cpp11/09-list-initialization-2.cpp +++ b/cpp11/tests/09-list-initialization/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/09-list-initialization-2.cpp +// file: cpp11/tests/09-list-initialization/2.cpp // // Exercise/练习: cpp11 | 09 - list initialization | 容器列表初始化 // @@ -16,11 +16,9 @@ // d2x checker list-initialization // -#include +import std; +import d2x.harness; -#include -#include -#include class MyVector { int mSize; @@ -36,16 +34,15 @@ class MyVector { int main() { std::vector vec1 = { 1, 2, 3 }; - d2x_assert_eq(vec1.size(), 3); + d2x::check_eq(vec1.size(), 3, "vec1.size() == 3"); std::vector vec2 { 1, 2, 3, 4, 5 }; - d2x_assert_eq(vec2.size(), 5); + d2x::check_eq(vec2.size(), 5, "vec2.size() == 5"); MyVector myVec1 = { 1, 2, 3 }; - d2x_assert_eq(myVec1.size(), 3); + d2x::check_eq(myVec1.size(), 3, "myVec1.size() == 3"); MyVector myVec2 { 1, 2, 3, 4, 5 }; - d2x_assert_eq(myVec2.size(), 5); - - D2X_WAIT + d2x::check_eq(myVec2.size(), 5, "myVec2.size() == 5"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/09-list-initialization-3.cpp b/cpp11/tests/09-list-initialization/3.cpp similarity index 82% rename from dslings/cpp11/09-list-initialization-3.cpp rename to cpp11/tests/09-list-initialization/3.cpp index 0f80762..bbe5d32 100644 --- a/dslings/cpp11/09-list-initialization-3.cpp +++ b/cpp11/tests/09-list-initialization/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/09-list-initialization-3.cpp +// file: cpp11/tests/09-list-initialization/3.cpp // // Exercise/练习: cpp11 | 09 - list initialization | 注意事项 // @@ -16,11 +16,9 @@ // d2x checker list-initialization // -#include +import std; +import d2x.harness; -#include -#include -#include class MyVector { int mSize; @@ -73,16 +71,15 @@ int main() { Point p2 {3, 4}; MyVector vec1(1); - d2x_assert_eq(vec1.size(), 1); + d2x::check_eq(vec1.size(), 1, "vec1.size() == 1"); MyVector vec2 { 1 }; - d2x_assert_eq(vec2.size(), 1); + d2x::check_eq(vec2.size(), 1, "vec2.size() == 1"); MyVector vec3(1, 10); - d2x_assert_eq(vec3.size(), 10); + d2x::check_eq(vec3.size(), 10, "vec3.size() == 10"); MyVector vec4 { 1, 10 }; - d2x_assert_eq(vec4.size(), 10); - - D2X_WAIT + d2x::check_eq(vec4.size(), 10, "vec4.size() == 10"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/10-delegating-constructors-0.cpp b/cpp11/tests/10-delegating-constructors/0.cpp similarity index 78% rename from dslings/cpp11/10-delegating-constructors-0.cpp rename to cpp11/tests/10-delegating-constructors/0.cpp index 2ed5578..edb0a56 100644 --- a/dslings/cpp11/10-delegating-constructors-0.cpp +++ b/cpp11/tests/10-delegating-constructors/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/10-delegating-constructors-0.cpp +// file: cpp11/tests/10-delegating-constructors/0.cpp // // Exercise/练习: cpp11 | 10 - delegating constructors | 委托构造函数 // @@ -15,10 +15,9 @@ // d2x checker delegating-constructors // -#include +import std; +import d2x.harness; -#include -#include static int construction_counter { 0 }; @@ -60,26 +59,23 @@ class Account { int main() { // 不要修改main函数中的代码 Account a1 { "1111" }; - d2x_assert_eq(construction_counter, 3); + d2x::check_eq(construction_counter, 3, "construction_counter == 3"); std::cout << a1.to_string() << std::endl; Account a2 { "2222", "wukong" }; - d2x_assert_eq(construction_counter, 5); + d2x::check_eq(construction_counter, 5, "construction_counter == 5"); std::cout << a2.to_string() << std::endl; Account a3 { "3333", "mcpp", 100 }; - d2x_assert_eq(construction_counter, 6); + d2x::check_eq(construction_counter, 6, "construction_counter == 6"); std::cout << a3.to_string() << std::endl; Account gi { "0000", "GImpact", 648 }; std::cout << gi.to_string() << std::endl; - d2x_assert( - gi.to_string() == - "Account { id: 0000, name: GImpact, coin: 648原石 }" - ); - - D2X_WAIT + d2x::check(gi.to_string() == + "Account { id: 0000, name: GImpact, coin: 648原石 }", "gi.to_string() == \"Account { id: 0000, name: GImpact, coin: 648原石 }\""); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/10-delegating-constructors-1.cpp b/cpp11/tests/10-delegating-constructors/1.cpp similarity index 85% rename from dslings/cpp11/10-delegating-constructors-1.cpp rename to cpp11/tests/10-delegating-constructors/1.cpp index 6dbbd57..0e1fcb2 100644 --- a/dslings/cpp11/10-delegating-constructors-1.cpp +++ b/cpp11/tests/10-delegating-constructors/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/10-delegating-constructors-1.cpp +// file: cpp11/tests/10-delegating-constructors/1.cpp // // Exercise/练习: cpp11 | 10 - delegating constructors | 委托构造函数注意事项 // @@ -15,10 +15,9 @@ // d2x checker delegating-constructors // -#include +import std; +import d2x.harness; -#include -#include struct Object { // 不要修改这个类的代码 static int construction_counter; @@ -77,16 +76,15 @@ int main() { // 不要修改main函数中的代码 Account a1 { "1111", "hello" }; std::cout << a1.to_string() << std::endl; - d2x_assert(a1.get_id() == "1111"); + d2x::check(a1.get_id() == "1111", "a1.get_id() == \"1111\""); Object::construction_counter = 0; Account a2 { "2222", "d2learn", 100 }; std::cout << a2.to_string() << std::endl; - d2x_assert(a2.get_object_name() == "d2learn"); - d2x_assert_eq(Object::construction_counter, 1); - - D2X_WAIT + d2x::check(a2.get_object_name() == "d2learn", "a2.get_object_name() == \"d2learn\""); + d2x::check_eq(Object::construction_counter, 1, "Object::construction_counter == 1"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/11-inherited-constructors-0.cpp b/cpp11/tests/11-inherited-constructors/0.cpp similarity index 92% rename from dslings/cpp11/11-inherited-constructors-0.cpp rename to cpp11/tests/11-inherited-constructors/0.cpp index 8ac418a..0419e6d 100644 --- a/dslings/cpp11/11-inherited-constructors-0.cpp +++ b/cpp11/tests/11-inherited-constructors/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/11-inherited-constructors-0.cpp +// file: cpp11/tests/11-inherited-constructors/0.cpp // // Exercise/练习: cpp11 | 11 - inherited constructors | 继承构造函数 // @@ -15,10 +15,9 @@ // d2x checker inherited-constructors // -#include +import std; +import d2x.harness; -#include -#include class ObjectBase { public: @@ -66,7 +65,6 @@ int main() { // 不要直接修改 main 函数中的代码 a1.tips_a(); b1.tips_b(); - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/11-inherited-constructors-1.cpp b/cpp11/tests/11-inherited-constructors/1.cpp similarity index 62% rename from dslings/cpp11/11-inherited-constructors-1.cpp rename to cpp11/tests/11-inherited-constructors/1.cpp index 3e49842..44cca90 100644 --- a/dslings/cpp11/11-inherited-constructors-1.cpp +++ b/cpp11/tests/11-inherited-constructors/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/11-inherited-constructors-1.cpp +// file: cpp11/tests/11-inherited-constructors/1.cpp // // Exercise/练习: cpp11 | 11 - inherited constructors | 继承构造函数于功能扩展 // @@ -15,10 +15,9 @@ // d2x checker inherited-constructors // -#include +import std; +import d2x.harness; -#include -#include class Student { // 不要直接修改 Student 类中的代码 protected: @@ -62,35 +61,34 @@ int main() { // 不要直接修改 main 函数中的代码 { // 基础测试 StudentTest studentTest; - d2x_assert(studentTest.age_valid() == true); - d2x_assert(studentTest.score_valid() == true); - d2x_assert(studentTest.to_string() == "{001, 张三, 18, 0}"); + d2x::check(studentTest.age_valid() == true, "studentTest.age_valid() == true"); + d2x::check(studentTest.score_valid() == true, "studentTest.score_valid() == true"); + d2x::check(studentTest.to_string() == "{001, 张三, 18, 0}", "studentTest.to_string() == \"{001, 张三, 18, 0}\""); } { // 边界测试 StudentTest studentTest("002", "张三", 201); - d2x_assert(studentTest.score_valid() == true); - d2x_assert(studentTest.age_valid() == false); + d2x::check(studentTest.score_valid() == true, "studentTest.score_valid() == true"); + d2x::check(studentTest.age_valid() == false, "studentTest.age_valid() == false"); studentTest.set_score(101); studentTest.set_age(200); - d2x_assert(studentTest.score_valid() == false); - d2x_assert(studentTest.age_valid() == true); + d2x::check(studentTest.score_valid() == false, "studentTest.score_valid() == false"); + d2x::check(studentTest.age_valid() == true, "studentTest.age_valid() == true"); studentTest.set_score(0); studentTest.set_age(1); - d2x_assert(studentTest.score_valid() == true); - d2x_assert(studentTest.age_valid() == true); + d2x::check(studentTest.score_valid() == true, "studentTest.score_valid() == true"); + d2x::check(studentTest.age_valid() == true, "studentTest.age_valid() == true"); studentTest.set_score(-1); studentTest.set_age(0); - d2x_assert(studentTest.score_valid() == false); - d2x_assert(studentTest.age_valid() == false); + d2x::check(studentTest.score_valid() == false, "studentTest.score_valid() == false"); + d2x::check(studentTest.age_valid() == false, "studentTest.age_valid() == false"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/11-inherited-constructors-2.cpp b/cpp11/tests/11-inherited-constructors/2.cpp similarity index 80% rename from dslings/cpp11/11-inherited-constructors-2.cpp rename to cpp11/tests/11-inherited-constructors/2.cpp index 02d5171..425ce63 100644 --- a/dslings/cpp11/11-inherited-constructors-2.cpp +++ b/cpp11/tests/11-inherited-constructors/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/11-inherited-constructors-2.cpp +// file: cpp11/tests/11-inherited-constructors/2.cpp // // Exercise/练习: cpp11 | 11 - inherited constructors | 继承构造函数在泛型装饰器的应用 // @@ -16,10 +16,9 @@ // d2x checker inherited-constructors // -#include +import std; +import d2x.harness; -#include -#include struct Point { double mX, mY; @@ -54,13 +53,13 @@ int main() { auto p11 = p1; std::cout << "p11: " << p11.to_string() << std::endl; - d2x_assert_eq(p1.mX, p11.mX); - d2x_assert_eq(p1.mY, p11.mY); + d2x::check_eq(p1.mX, p11.mX, "p1.mX == p11.mX"); + d2x::check_eq(p1.mY, p11.mY, "p1.mY == p11.mY"); decltype(p2) p22 = p2; // by std::move? std::cout << "p22: " << p22.to_string() << std::endl; - d2x_assert_eq(p2.mX, p22.mX); - d2x_assert_eq(p2.mY, p22.mY); + d2x::check_eq(p2.mX, p22.mX, "p2.mX == p22.mX"); + d2x::check_eq(p2.mY, p22.mY, "p2.mY == p22.mY"); NoMove p3(3, 3); std::cout << "p3: " << p3.to_string() << std::endl; @@ -68,10 +67,9 @@ int main() { NoMove p33(0, 0); p33 = std::move(p3); // by copy? std::cout << "p33: " << p33.to_string() << std::endl; - d2x_assert_eq(p3.mX, p33.mX); - d2x_assert_eq(p3.mY, p33.mY); - - D2X_WAIT + d2x::check_eq(p3.mX, p33.mX, "p3.mX == p33.mX"); + d2x::check_eq(p3.mY, p33.mY, "p3.mY == p33.mY"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/cpp11/12-nullptr-0.cpp b/cpp11/tests/12-nullptr/0.cpp similarity index 70% rename from dslings/cpp11/12-nullptr-0.cpp rename to cpp11/tests/12-nullptr/0.cpp index 44da275..f0617ab 100644 --- a/dslings/cpp11/12-nullptr-0.cpp +++ b/cpp11/tests/12-nullptr/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/12-nullptr-0.cpp +// file: cpp11/tests/12-nullptr/0.cpp // // Exercise/练习: cpp11 | 12 - nullptr | 指针字面量 - 基础用法 // @@ -17,9 +17,9 @@ // d2x checker nullptr // -#include +import std; +import d2x.harness; -#include int main() { @@ -28,13 +28,13 @@ int main() { int* ptr2 = NULL; // 修复这里,添加正确类型 int* ptr3 = 0; // 不推荐的传统用法 - d2x_assert(ptr1 == nullptr); - d2x_assert(ptr2 == nullptr); - d2x_assert(ptr3 == nullptr); + d2x::check(ptr1 == nullptr, "ptr1 == nullptr"); + d2x::check(ptr2 == nullptr, "ptr2 == nullptr"); + d2x::check(ptr3 == nullptr, "ptr3 == nullptr"); // 2. nullptr的类型 bool ok = std::is_same::value; - d2x_assert(ok); + d2x::check(ok, "ok"); // 3. 使用 nullptr 进行指针比较 int value = 42; @@ -42,7 +42,7 @@ int main() { if (ptr4 != nullptr) { *ptr4 = D2X_YOUR_ANSWER; - d2x_assert_eq(*ptr4, 2233); + d2x::check_eq(*ptr4, 2233, "*ptr4 == 2233"); } // 4. 不同类型的指针都可以使用 nullptr @@ -50,11 +50,10 @@ int main() { char* cptr = nullptr void* vptr = nullptr; - d2x_assert(dptr == nullptr); - d2x_assert(cptr == nullptr); - d2x_assert(vptr == nullptr); - - D2X_WAIT + d2x::check(dptr == nullptr, "dptr == nullptr"); + d2x::check(cptr == nullptr, "cptr == nullptr"); + d2x::check(vptr == nullptr, "vptr == nullptr"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/12-nullptr-1.cpp b/cpp11/tests/12-nullptr/1.cpp similarity index 75% rename from dslings/cpp11/12-nullptr-1.cpp rename to cpp11/tests/12-nullptr/1.cpp index 671e32c..1cc4d64 100644 --- a/dslings/cpp11/12-nullptr-1.cpp +++ b/cpp11/tests/12-nullptr/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/12-nullptr-1.cpp +// file: cpp11/tests/12-nullptr/1.cpp // // Exercise/练习: cpp11 | 12 - nullptr | 指针字面量 - 函数重载 // @@ -17,7 +17,8 @@ // d2x checker nullptr // -#include +import std; +import d2x.harness; bool process_int_called = false; bool process_ptr_called = false; @@ -47,12 +48,11 @@ int main() { display(NULL); process(NULL); - d2x_assert(process_int_called); - d2x_assert(display_int_called); - d2x_assert(display_ptr_called); - d2x_assert(display_ptr_called); - - D2X_WAIT + d2x::check(process_int_called, "process_int_called"); + d2x::check(display_int_called, "display_int_called"); + d2x::check(display_ptr_called, "display_ptr_called"); + d2x::check(display_ptr_called, "display_ptr_called"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/12-nullptr-2.cpp b/cpp11/tests/12-nullptr/2.cpp similarity index 92% rename from dslings/cpp11/12-nullptr-2.cpp rename to cpp11/tests/12-nullptr/2.cpp index cb1f5fe..bee59fb 100644 --- a/dslings/cpp11/12-nullptr-2.cpp +++ b/cpp11/tests/12-nullptr/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/12-nullptr-2.cpp +// file: cpp11/tests/12-nullptr/2.cpp // // Exercise/练习: cpp11 | 12 - nullptr | 指针字面量 - 模板编程 // @@ -17,8 +17,8 @@ // d2x checker nullptr // -#include -#include +import std; +import d2x.harness; // 模板函数示例 template @@ -44,7 +44,6 @@ int main() { processPointer(clone(nullptr)); processPointer(clone(nullptr)); - D2X_WAIT - + d2x::wait(); return 0; } diff --git a/dslings/cpp11/13-long-long-0.cpp b/cpp11/tests/13-long-long/0.cpp similarity index 77% rename from dslings/cpp11/13-long-long-0.cpp rename to cpp11/tests/13-long-long/0.cpp index b25e40c..5fff88b 100644 --- a/dslings/cpp11/13-long-long-0.cpp +++ b/cpp11/tests/13-long-long/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/13-long-long-0.cpp +// file: cpp11/tests/13-long-long/0.cpp // // Exercise/练习: cpp11 | 13 - long long | 64位整数类型 - 基础用法 // @@ -17,9 +17,9 @@ // d2x checker long-long // -#include +import std; +import d2x.harness; -#include int main() { @@ -29,7 +29,7 @@ int main() { // 2. 整数表示范围 unsigned int uVal1 = 18446744073709551615; - d2x_assert_eq(uVal1, 18446744073709551615ULL); + d2x::check_eq(uVal1, 18446744073709551615ULL, "uVal1 == 18446744073709551615ULL"); // 3. 类型推导和字面量 // 修复下面的声明,让 auto 正确推导类型 @@ -39,10 +39,9 @@ int main() { bool is_longlong = std::is_same::value; bool is_ulonglong = std::is_same::value; - d2x_assert(is_longlong == true); - d2x_assert(is_ulonglong == true); - - D2X_WAIT + d2x::check(is_longlong == true, "is_longlong == true"); + d2x::check(is_ulonglong == true, "is_ulonglong == true"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/13-long-long-1.cpp b/cpp11/tests/13-long-long/1.cpp similarity index 64% rename from dslings/cpp11/13-long-long-1.cpp rename to cpp11/tests/13-long-long/1.cpp index 26a5461..f1ea697 100644 --- a/dslings/cpp11/13-long-long-1.cpp +++ b/cpp11/tests/13-long-long/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/13-long-long-1.cpp +// file: cpp11/tests/13-long-long/1.cpp // // Exercise/练习: cpp11 | 13 - long long | 64位整数类型 - 大数应用和边界值 // @@ -17,9 +17,9 @@ // d2x checker long-long // -#include +import std; +import d2x.harness; -#include int main() { @@ -29,16 +29,15 @@ int main() { auto minLL = std::numeric_limits::min(); auto maxULL = std::numeric_limits::max(); - d2x_assert_eq(maxInt, 2147483647); - d2x_assert_eq(maxLL, 9223372036854775807LL); - d2x_assert_eq(minLL, -9223372036854775807LL - 1); - d2x_assert_eq(maxULL, 18446744073709551615ULL); + d2x::check_eq(maxInt, 2147483647, "maxInt == 2147483647"); + d2x::check_eq(maxLL, 9223372036854775807LL, "maxLL == 9223372036854775807LL"); + d2x::check_eq(minLL, -9223372036854775807LL - 1, "minLL == -9223372036854775807LL - 1"); + d2x::check_eq(maxULL, 18446744073709551615ULL, "maxULL == 18446744073709551615ULL"); // 2. 大整数应用 - 表示世界人口 int currentPopulation = 7800000000; - d2x_assert_eq(currentPopulation, 7800000000ULL); - - D2X_WAIT + d2x::check_eq(currentPopulation, 7800000000ULL, "currentPopulation == 7800000000ULL"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/14-type-alias-0.cpp b/cpp11/tests/14-type-alias/0.cpp similarity index 72% rename from dslings/cpp11/14-type-alias-0.cpp rename to cpp11/tests/14-type-alias/0.cpp index d7b255f..73f1dff 100644 --- a/dslings/cpp11/14-type-alias-0.cpp +++ b/cpp11/tests/14-type-alias/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/14-type-alias-0.cpp +// file: cpp11/tests/14-type-alias/0.cpp // // Exercise/练习: cpp11 | 14 - type alias | 基本类型别名 // @@ -17,9 +17,9 @@ // d2x checker type-alias // -#include +import std; +import d2x.harness; -#include int main() { @@ -27,24 +27,23 @@ int main() { D2X_YOUR_ANSWER Integer = int; D2X_YOUR_ANSWER = int; - bool ok = std::is_same::value; d2x_assert(ok); - ok = std::is_same::value; d2x_assert(ok); + bool ok = std::is_same::value; d2x::check(ok, "ok"); + ok = std::is_same::value; d2x::check(ok, "ok"); // 2. 使用类型别名 Integer a = 42; Real b = 3.14; // 3. 验证类型别名 - d2x_assert_eq(a, 42); - d2x_assert_eq(b, 3.14); + d2x::check_eq(a, 42, "a == 42"); + d2x::check_eq(b, 3.14, "b == 3.14"); // 4. 类型别名本质相同 int c = 100; Integer d = c; // 可以赋值,因为本质都是int - d2x_assert_eq(c, d); - - D2X_WAIT + d2x::check_eq(c, d, "c == d"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/14-type-alias-1.cpp b/cpp11/tests/14-type-alias/1.cpp similarity index 78% rename from dslings/cpp11/14-type-alias-1.cpp rename to cpp11/tests/14-type-alias/1.cpp index 1edf7da..894a05f 100644 --- a/dslings/cpp11/14-type-alias-1.cpp +++ b/cpp11/tests/14-type-alias/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/14-type-alias-1.cpp +// file: cpp11/tests/14-type-alias/1.cpp // // Exercise/练习: cpp11 | 14 - type alias | 复杂类型和函数指针别名 // @@ -17,10 +17,9 @@ // d2x checker type-alias // -#include +import std; +import d2x.harness; -#include -#include static int func_called = 0; @@ -37,7 +36,7 @@ int main() { FuncPtr func = example_func; func(1, 2); - d2x_assert_eq(func_called, 3); + d2x::check_eq(func_called, 3, "func_called == 3"); // 2. 容器类型别名 // 使用using定义vector的别名 @@ -54,11 +53,10 @@ int main() { Container::ValueType value = 100; // 4. 验证类型别名 - d2x_assert(strings[0] == "hello"); - d2x_assert(strings[1] == "world"); - d2x_assert_eq(value, 100); - - D2X_WAIT + d2x::check(strings[0] == "hello", "strings[0] == \"hello\""); + d2x::check(strings[1] == "world", "strings[1] == \"world\""); + d2x::check_eq(value, 100, "value == 100"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/14-type-alias-2.cpp b/cpp11/tests/14-type-alias/2.cpp similarity index 70% rename from dslings/cpp11/14-type-alias-2.cpp rename to cpp11/tests/14-type-alias/2.cpp index 53c12a3..542f620 100644 --- a/dslings/cpp11/14-type-alias-2.cpp +++ b/cpp11/tests/14-type-alias/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/14-type-alias-2.cpp +// file: cpp11/tests/14-type-alias/2.cpp // // Exercise/练习: cpp11 | 14 - type alias | 别名模板基础 // @@ -17,11 +17,9 @@ // d2x checker type-alias // -#include +import std; +import d2x.harness; -#include -#include -#include // 1. 基本别名模板 template @@ -41,15 +39,14 @@ int main() { Vec3 v3 = {1.0f, 2.0f, 3.0f}; Heap minHeap; - d2x_assert_eq(numbers[0], 1); - d2x_assert_eq(numbers[1], 2); - d2x_assert_eq(numbers[2], 3); + d2x::check_eq(numbers[0], 1, "numbers[0] == 1"); + d2x::check_eq(numbers[1], 2, "numbers[1] == 2"); + d2x::check_eq(numbers[2], 3, "numbers[2] == 3"); - d2x_assert_eq(v3[0], 1.0f); - d2x_assert_eq(v3[1], 2.0f); - d2x_assert_eq(v3[2], 3.0f); - - D2X_WAIT + d2x::check_eq(v3[0], 1.0f, "v3[0] == 1.0f"); + d2x::check_eq(v3[1], 2.0f, "v3[1] == 2.0f"); + d2x::check_eq(v3[2], 3.0f, "v3[2] == 3.0f"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/14-type-alias-3.cpp b/cpp11/tests/14-type-alias/3.cpp similarity index 83% rename from dslings/cpp11/14-type-alias-3.cpp rename to cpp11/tests/14-type-alias/3.cpp index 815f6ee..e7040df 100644 --- a/dslings/cpp11/14-type-alias-3.cpp +++ b/cpp11/tests/14-type-alias/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/14-type-alias-3.cpp +// file: cpp11/tests/14-type-alias/3.cpp // // Exercise/练习: cpp11 | 14 - type alias | 标准库中的别名模板应用 // @@ -17,8 +17,8 @@ // d2x checker type-alias // -#include -#include +import std; +import d2x.harness; int main() { @@ -30,10 +30,9 @@ int main() { bool ok = std::is_same, int*>::value; - d2x_assert(ok); - d2x_assert_eq(*ptr, 20); - - D2X_WAIT + d2x::check(ok, "ok"); + d2x::check_eq(*ptr, 20, "*ptr == 20"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/15-variadic-templates-0.cpp b/cpp11/tests/15-variadic-templates/0.cpp similarity index 86% rename from dslings/cpp11/15-variadic-templates-0.cpp rename to cpp11/tests/15-variadic-templates/0.cpp index e2774ad..153674d 100644 --- a/dslings/cpp11/15-variadic-templates-0.cpp +++ b/cpp11/tests/15-variadic-templates/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/15-variadic-templates-0.cpp +// file: cpp11/tests/15-variadic-templates/0.cpp // // Exercise/练习: cpp11 | 15 - variadic templates | 可变参数模板基础 // @@ -20,8 +20,8 @@ // d2x checker variadic-templates // -#include -#include +import std; +import d2x.harness; std::stringstream ss; @@ -41,9 +41,8 @@ int main() { print(1, "hello", 3.14); std::string result = ss.str(); - d2x_assert(result == "1 hello 3.14 "); - - D2X_WAIT + d2x::check(result == "1 hello 3.14 ", "result == \"1 hello 3.14 \""); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/15-variadic-templates-1.cpp b/cpp11/tests/15-variadic-templates/1.cpp similarity index 77% rename from dslings/cpp11/15-variadic-templates-1.cpp rename to cpp11/tests/15-variadic-templates/1.cpp index c7453ab..92a7f1b 100644 --- a/dslings/cpp11/15-variadic-templates-1.cpp +++ b/cpp11/tests/15-variadic-templates/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/15-variadic-templates-1.cpp +// file: cpp11/tests/15-variadic-templates/1.cpp // // Exercise/练习: cpp11 | 15 - variadic templates | 可变参数模板求和 // @@ -13,7 +13,8 @@ // - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/15-variadic-templates.md // -#include +import std; +import d2x.harness; D2X_YOUR_ANSWER @@ -24,17 +25,16 @@ T sum(T first, D2X_YOUR_ANSWER) { int main() { int res1 = sum(1, 2, 3, 4, 5); - d2x_assert_eq(res1, 15); + d2x::check_eq(res1, 15, "res1 == 15"); double res2 = sum(1.5, 2.5, 3.0); - d2x_assert(res2 == 7.0); + d2x::check(res2 == 7.0, "res2 == 7.0"); // 混合类型 // 注意: 返回类型由第一个参数 T 决定 int res3 = sum(10, 20.5); - d2x_assert_eq(res3, 30); - - D2X_WAIT + d2x::check_eq(res3, 30, "res3 == 30"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/16-generalized-unions-0.cpp b/cpp11/tests/16-generalized-unions/0.cpp similarity index 84% rename from dslings/cpp11/16-generalized-unions-0.cpp rename to cpp11/tests/16-generalized-unions/0.cpp index 0511f62..847b132 100644 --- a/dslings/cpp11/16-generalized-unions-0.cpp +++ b/cpp11/tests/16-generalized-unions/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/16-generalized-unions-0.cpp +// file: cpp11/tests/16-generalized-unions/0.cpp // // Exercise/练习: cpp11 | 16 - generalized unions | 广义非平凡联合体 // @@ -18,7 +18,8 @@ // d2x checker generalized-unions // -#include +import std; +import d2x.harness; union M @@ -34,7 +35,7 @@ int main() { M u1; //初始化成员 - d2x_assert(u1.a1 == 42); + d2x::check(u1.a1 == 42, "u1.a1 == 42"); u1.a2 = 21; double val = 3.14; @@ -42,7 +43,6 @@ int main() { u1.c = 'x'; - D2X_WAIT - + d2x::wait(); return 0; } diff --git a/dslings/cpp11/16-generalized-unions-1.cpp b/cpp11/tests/16-generalized-unions/1.cpp similarity index 86% rename from dslings/cpp11/16-generalized-unions-1.cpp rename to cpp11/tests/16-generalized-unions/1.cpp index b658310..c3b18a7 100644 --- a/dslings/cpp11/16-generalized-unions-1.cpp +++ b/cpp11/tests/16-generalized-unions/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/16-generalized-unions-1.cpp +// file: cpp11/tests/16-generalized-unions/1.cpp // // Exercise/练习: cpp11 | 16 - generalized unions | 广义非平凡联合体 // @@ -18,8 +18,8 @@ // d2x checker generalized-unions // -#include -#include +import std; +import d2x.harness; union M { @@ -43,12 +43,11 @@ int main() { u1.a2 = {1, D2X_YOUR_ANSWER, 3}; // 2. 验证:正常访问 - d2x_assert_eq(u1.a2[1], 42); + d2x::check_eq(u1.a2[1], 42, "u1.a2[1] == 42"); // 3. 手动析构 u1.a2.~vector(); - D2X_WAIT - + d2x::wait(); return 0; } diff --git a/dslings/cpp11/16-generalized-unions-2.cpp b/cpp11/tests/16-generalized-unions/2.cpp similarity index 80% rename from dslings/cpp11/16-generalized-unions-2.cpp rename to cpp11/tests/16-generalized-unions/2.cpp index 57bd336..1f739aa 100644 --- a/dslings/cpp11/16-generalized-unions-2.cpp +++ b/cpp11/tests/16-generalized-unions/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/16-generalized-unions-2.cpp +// file: cpp11/tests/16-generalized-unions/2.cpp // // Exercise/练习: cpp11 | 16 - generalized unions | 带标签的鉴别联合体 // @@ -20,9 +20,8 @@ // d2x checker generalized-unions // -#include -#include -#include +import std; +import d2x.harness; // 标签类型 — 标记联合体中哪个成员是活跃的 enum class Tag { @@ -77,29 +76,28 @@ int main() { // 1. 构造 int 值并验证 Value v1(42); - d2x_assert(v1.tag == Tag::INTEGER); - d2x_assert_eq(v1.as_int(), 42); + d2x::check(v1.tag == Tag::INTEGER, "v1.tag == Tag::INTEGER"); + d2x::check_eq(v1.as_int(), 42, "v1.as_int() == 42"); // 2. 构造 string 值并验证 Value v2(std::string("hello")); - d2x_assert(v2.tag == Tag::STRING); - d2x_assert(v2.as_string() == "hello"); + d2x::check(v2.tag == Tag::STRING, "v2.tag == Tag::STRING"); + d2x::check(v2.as_string() == "hello", "v2.as_string() == \"hello\""); // 3. 从 string 切换到 int { Value v3(std::string("world")); - d2x_assert(v3.as_string() == "world"); + d2x::check(v3.as_string() == "world", "v3.as_string() == \"world\""); // 手动析构 string 成员, 切换到 int v3.data.s.~basic_string(); v3.tag = Tag::INTEGER; v3.data.i = 100; - d2x_assert_eq(v3.as_int(), D2X_YOUR_ANSWER); + d2x::check_eq(v3.as_int(), D2X_YOUR_ANSWER, "v3.as_int() == D2X_YOUR_ANSWER"); // 离开作用域时 ~Value() 发现 tag == INTEGER, 不析构 string } - D2X_WAIT - + d2x::wait(); return 0; } diff --git a/dslings/cpp11/17-pod-type-0.cpp b/cpp11/tests/17-pod-type/0.cpp similarity index 87% rename from dslings/cpp11/17-pod-type-0.cpp rename to cpp11/tests/17-pod-type/0.cpp index 718ded2..9149270 100644 --- a/dslings/cpp11/17-pod-type-0.cpp +++ b/cpp11/tests/17-pod-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/17-pod-type-0.cpp +// file: cpp11/tests/17-pod-type/0.cpp // // Exercise/练习: cpp11 | 17 - POD Type | 基本类型性质判断 // @@ -18,9 +18,9 @@ // // d2x checker pod-type -#include +import std; +import d2x.harness; -#include struct A { int x; @@ -53,19 +53,19 @@ int main() { // 3. 判断 A 是否是 trivial /standard_layout 类型,将结果写入 ok bool ok = D2X_YOUR_ANSWER; - d2x_assert(ok); + d2x::check(ok, "ok"); // 4. 判断 B 是否是 trivial /standard_layout 类型,将结果写入 ok ok = D2X_YOUR_ANSWER; - d2x_assert(ok); + d2x::check(ok, "ok"); // 5. 判断 C 是否是 trivial 类型,将结果写入 ok ok = D2X_YOUR_ANSWER; - d2x_assert(ok); + d2x::check(ok, "ok"); // 6. 判断 D 是否不是 standard_layout 类型,将结果写入 ok ok = D2X_YOUR_ANSWER; - d2x_assert(!ok); + d2x::check(!ok, "!ok"); - D2X_WAIT + d2x::wait(); } diff --git a/dslings/cpp11/17-pod-type-1.cpp b/cpp11/tests/17-pod-type/1.cpp similarity index 72% rename from dslings/cpp11/17-pod-type-1.cpp rename to cpp11/tests/17-pod-type/1.cpp index 37d417f..43b0452 100644 --- a/dslings/cpp11/17-pod-type-1.cpp +++ b/cpp11/tests/17-pod-type/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/17-pod-type-1.cpp +// file: cpp11/tests/17-pod-type/1.cpp // // Exercise/练习: cpp11 | 17 - POD Type | 按字节拷贝 POD 结构体 // @@ -18,10 +18,9 @@ // // d2x checker pod-type -#include +import std; +import d2x.harness; -#include -#include struct Packet { @@ -41,12 +40,11 @@ int main() { // TODO: 使用内存拷贝将 p1 的内容复制到 p2 D2X_YOUR_ANSWER; - d2x_assert_eq(p2.len, 42u); - d2x_assert_eq(p2.type, static_cast(1)); - d2x_assert_eq(p2.flags, static_cast(0xFF)); - - D2X_WAIT + d2x::check_eq(p2.len, 42u, "p2.len == 42u"); + d2x::check_eq(p2.type, static_cast(1), "p2.type == static_cast(1)"); + d2x::check_eq(p2.flags, static_cast(0xFF), "p2.flags == static_cast(0xFF)"); + d2x::wait(); return 0; } diff --git a/dslings/cpp11/17-pod-type-2.cpp b/cpp11/tests/17-pod-type/2.cpp similarity index 78% rename from dslings/cpp11/17-pod-type-2.cpp rename to cpp11/tests/17-pod-type/2.cpp index b3f5558..a5de1c2 100644 --- a/dslings/cpp11/17-pod-type-2.cpp +++ b/cpp11/tests/17-pod-type/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/17-pod-type-2.cpp +// file: cpp11/tests/17-pod-type/2.cpp // // Exercise/练习: cpp11 | 17 - POD Type | 适配 C 接口的 POD 头部 // @@ -18,10 +18,9 @@ // // d2x checker pod-type -#include +import std; +import d2x.harness; -#include -#include // C 风格的消息头部,仅包含 POD 成员,用于跨语言/跨模块传递 @@ -42,7 +41,7 @@ static MessageHeader g_last_header{}; extern "C" void c_send_header(const void* data, std::size_t size) { // 在真实场景中,这里可能是 C 库/系统调用,这里简化为记录收到的头部 - d2x_assert_eq(size, sizeof(MessageHeader)); + d2x::check_eq(size, sizeof(MessageHeader), "size == sizeof(MessageHeader)"); const auto* header = static_cast(data); g_last_header = *header; } @@ -63,12 +62,11 @@ int main() { send_message_to_c(msg); // 验证 C 接口收到的头部内容是否正确 - d2x_assert_eq(g_last_header.type, msg.type); - d2x_assert_eq(g_last_header.len, static_cast(msg.payload.size())); - d2x_assert_eq(g_last_header.flags, static_cast(0)); - - D2X_WAIT + d2x::check_eq(g_last_header.type, msg.type, "g_last_header.type == msg.type"); + d2x::check_eq(g_last_header.len, static_cast(msg.payload.size()), "g_last_header.len == static_cast(msg.payload.size())"); + d2x::check_eq(g_last_header.flags, static_cast(0), "g_last_header.flags == static_cast(0)"); + d2x::wait(); return 0; } diff --git a/dslings/cpp14/00-generic-lambdas-0.cpp b/dslings/cpp14/00-generic-lambdas-0.cpp deleted file mode 100644 index 31ccdd3..0000000 --- a/dslings/cpp14/00-generic-lambdas-0.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// d2mcpp: https://github.com/mcpp-community/d2mcpp -// license: Apache-2.0 -// file: dslings/cpp14/00-generic-lambdas-0.cpp -// -// Exercise/练习: cpp14 | 00 - generic lambdas | 泛型 lambda -// -// Tips/提示: -// - lambda 参数使用 auto, 编译器为 operator() 生成隐式模板 -// - 同一个泛型 lambda 可以接受不同类型的参数 -// -// Docs/文档: -// - https://en.cppreference.com/w/cpp/language/lambda -// - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/00-generic-lambdas.md -// -// 练习交流讨论: http://forum.d2learn.org/category/20 -// -// Auto-Checker/自动检测命令: -// -// d2x checker generic-lambdas -// - -#include -#include - -int main() { - - // 0. 简单泛型 lambda — identity - auto identity = [](D2X_YOUR_ANSWER x) { - return x; - }; - - d2x_assert_eq(identity(42), 42); - d2x_assert(identity(std::string("hello")) == "hello"); - d2x_assert_eq(identity(3.14), D2X_YOUR_ANSWER); - - // 1. 泛型 lambda 做比较 - auto greater = [](auto a, auto b) { - return D2X_YOUR_ANSWER; - }; - - d2x_assert(greater(5, 3)); - d2x_assert(greater(2.5, 1.2)); - d2x_assert(greater(std::string("z"), std::string("a"))); - - // 2. 推导类型确认 - auto get_type_size = [](auto x) { - return sizeof(D2X_YOUR_ANSWER); - }; - - d2x_assert_eq(get_type_size(42), sizeof(int)); - d2x_assert_eq(get_type_size('c'), sizeof(char)); - - D2X_WAIT - - return 0; -} diff --git a/dslings/cpp14/00-generic-lambdas-1.cpp b/dslings/cpp14/00-generic-lambdas-1.cpp deleted file mode 100644 index e3f0c5b..0000000 --- a/dslings/cpp14/00-generic-lambdas-1.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// d2mcpp: https://github.com/mcpp-community/d2mcpp -// license: Apache-2.0 -// file: dslings/cpp14/00-generic-lambdas-1.cpp -// -// Exercise/练习: cpp14 | 00 - generic lambdas | 泛型 lambda 与 STL 算法 -// -// Tips/提示: -// - 泛型 lambda 可复用于不同元素类型的容器, 同一个 lambda 传给多种 STL 算法 -// - 捕获的变量类型不变, 只有参数用 auto -// -// Docs/文档: -// - https://en.cppreference.com/w/cpp/language/lambda -// - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/00-generic-lambdas.md -// -// 练习交流讨论: http://forum.d2learn.org/category/20 -// -// Auto-Checker/自动检测命令: -// -// d2x checker generic-lambdas -// - -#include -#include -#include - -int main() { - - // 0. 同一个泛型 lambda 用于不同元素类型的容器排序 - std::vector v1 = {5, 1, 4, 2, 8}; - std::vector v2 = {3.1, 2.7, 8.5, 1.9}; - - auto desc = [](D2X_YOUR_ANSWER a, D2X_YOUR_ANSWER b) { - return a > b; - }; - - std::sort(v1.begin(), v1.end(), desc); - d2x_assert_eq(v1[0], 8); - d2x_assert_eq(v1[4], 1); - - std::sort(v2.begin(), v2.end(), desc); - d2x_assert_eq(v2[0], D2X_YOUR_ANSWER); - - // 1. 带捕获的泛型 lambda — find_if - int threshold = 3; - auto above = [threshold](auto x) { - return x > threshold; - }; - - auto it1 = std::find_if(v1.begin(), v1.end(), above); - d2x_assert(*it1 == D2X_YOUR_ANSWER); - - auto it2 = std::find_if(v2.begin(), v2.end(), above); - d2x_assert(*it2 == 8.5); - - // 2. 泛型 lambda 返回 lambda — 函数工厂 - auto make_multiplier = [](auto factor) { - return [factor](auto x) { return x * D2X_YOUR_ANSWER; }; - }; - - auto times2 = make_multiplier(2); - d2x_assert_eq(times2(10), 20); - d2x_assert_eq(times2(0.5), 1.0); - - D2X_WAIT - - return 0; -} diff --git a/dslings/cpp17/README.md b/dslings/cpp17/README.md deleted file mode 100644 index f87f5c1..0000000 --- a/dslings/cpp17/README.md +++ /dev/null @@ -1 +0,0 @@ -# TODO \ No newline at end of file diff --git a/dslings/cpp20/README.md b/dslings/cpp20/README.md deleted file mode 100644 index f87f5c1..0000000 --- a/dslings/cpp20/README.md +++ /dev/null @@ -1 +0,0 @@ -# TODO \ No newline at end of file diff --git a/dslings/cpp23/README.md b/dslings/cpp23/README.md deleted file mode 100644 index f87f5c1..0000000 --- a/dslings/cpp23/README.md +++ /dev/null @@ -1 +0,0 @@ -# TODO \ No newline at end of file diff --git a/dslings/harness/include/d2x/cpp/common.hpp b/dslings/harness/include/d2x/cpp/common.hpp deleted file mode 100644 index b420cbe..0000000 --- a/dslings/harness/include/d2x/cpp/common.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef COMMON_HPP_D2X -#define COMMON_HPP_D2X - -// d2mcpp 练习脚手架 —— 经典 #include 路径。 -// -// 这条路径提供宏,因为 D2X_YOUR_ANSWER 必须展开为空才能制造编译错误, -// 而宏无法跨模块导出。模块化的练习请改用 `import d2x.harness;`,它提供 -// d2x::check / check_eq / wait(靠 std::source_location 自动带 file/line), -// 但没有填空占位符的等价物。 -// -// 可见输出与旧版逐字节一致 —— ✅/❌ 的逐条对照是教学的一部分。新增的只是 -// 把同样的结果写进侧信道,让判定不再依赖扫 stdout 找 emoji(那会误判: -// 一个断言全过的正确解答,只要在说明文字里打了个 ❌ 就被判失败)。 - -#include -#include - -#include -#include - -namespace d2x::detail { - -// std::to_string 只接受算术类型,但断言里允许任意可比较类型。 -// 转得了的转,转不了的留空:侧信道少一点细节,好过整个 harness 编不过。 -template -inline std::string show(const T& v) { - if constexpr (requires { std::to_string(v); }) return std::to_string(v); - else if constexpr (requires { std::string(v); }) return std::string(v); - else return {}; -} - -} // namespace d2x::detail - -#define d2x_assert(expr) \ -{ \ - bool d2x_ok_ = static_cast(expr); \ - d2x::report::assertion(d2x_ok_, #expr, "true", d2x_ok_ ? "true" : "false", \ - __FILE__, __LINE__); \ - if (!d2x_ok_) { \ - HONLY_LOGW("❌(error) | %s", #expr); \ - } else { \ - HONLY_LOGI_P("✅ | %s", #expr); \ - } \ -} - -#define d2x_assert_eq(a, b) \ -{ \ - bool d2x_ok_ = ((a) == (b)); \ - d2x::report::assertion(d2x_ok_, #a " == " #b, \ - d2x::detail::show(b), d2x::detail::show(a), \ - __FILE__, __LINE__); \ - if (!d2x_ok_) {\ - HONLY_LOGW("❌ | %s == %s (%s == %s)", \ - #a, #b, d2x::detail::show(a).c_str(), d2x::detail::show(b).c_str()); \ - } else {\ - HONLY_LOGI_P("✅ | %s == %s (%s == %s)", \ - #a, #b, d2x::detail::show(a).c_str(), d2x::detail::show(b).c_str()); \ - } \ -} - -#define D2X_WAIT { \ - d2x::report::wait(__FILE__, __LINE__); \ - HONLY_LOGW("🥳 Delete the D2X_WAIT to continue..."); \ -} - -// 展开为空 —— 学员在这里填类型。留空会得到「未声明」错误, -// 比留一个占位标识符导致的语法错误更贴近意图。 -#define D2X_YOUR_ANSWER - -#define D2X_DONT_DELETE_THIS(x) x - -#endif // COMMON_HPP_D2X diff --git a/dslings/harness/include/d2x/cpp/honly_logger.hpp b/dslings/harness/include/d2x/cpp/honly_logger.hpp deleted file mode 100644 index 0edda51..0000000 --- a/dslings/harness/include/d2x/cpp/honly_logger.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef LOGGER_HPP__HONLY -#define LOGGER_HPP__HONLY - -#include - -#ifndef HONLY_LOGGER_TAG -#define HONLY_LOGGER_TAG "HONLY" -#endif - -#define LOG_ENABLE true -#define _HONLY_LOG(fd, ...) if (LOG_ENABLE) { fprintf (fd, __VA_ARGS__); fprintf (fd, "\033[0m\n"); fflush(stdout); } -#define HONLY_LOGI_P(...) { fprintf (stdout, "\033[32m[%s LOGI]: - ", HONLY_LOGGER_TAG); _HONLY_LOG(stdout, __VA_ARGS__); } -#define HONLY_LOGI(...) { fprintf (stdout, "\033[32m[%s LOGI]: %s: %s:%d - ", HONLY_LOGGER_TAG, __func__, __FILE__, __LINE__); _HONLY_LOG(stdout, __VA_ARGS__); } -#define HONLY_LOGD(...) { fprintf (stdout, "\033[37m[%s LOGD]: %s: %s:%d - ", HONLY_LOGGER_TAG, __func__, __FILE__, __LINE__); _HONLY_LOG(stdout, __VA_ARGS__); } -#define HONLY_LOGW(...) { fprintf (stdout, "\033[33m[%s LOGW]: %s: %s:%d - ", HONLY_LOGGER_TAG, __func__, __FILE__, __LINE__); _HONLY_LOG(stdout, __VA_ARGS__); } -#define HONLY_LOGE(...) { fprintf (stdout, "\033[31m[%s LOGE]: %s: %s:%d - ❌ | ", HONLY_LOGGER_TAG, __func__, __FILE__, __LINE__); _HONLY_LOG(stdout, __VA_ARGS__); } - -#endif \ No newline at end of file diff --git a/dslings/harness/include/d2x/cpp/report.hpp b/dslings/harness/include/d2x/cpp/report.hpp deleted file mode 100644 index 81f6f37..0000000 --- a/dslings/harness/include/d2x/cpp/report.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef REPORT_HPP_D2X -#define REPORT_HPP_D2X - -// 侧信道上报:把判定信号从 stdout 移到带外。 -// -// 为什么需要:断言失败并不改变退出码,所以判定只能靠扫 stdout 找 ❌ —— -// 这会误判。一个断言全过的正确解答,只要在说明文字里打了个 ❌ 就被判失败 -// (已复现)。反过来,输出被截断或 Windows 控制台代码页不对,判定同样失效。 -// -// 于是:stdout 回归「给人看」,判定走这个文件。 -// -// 逐条追加而非退出时统一写 —— 练习段错误时,崩溃之前的断言结果照样保留, -// 学员仍能看到前几条过了。 -// -// D2X_RESULT_FILE 未设置时这里什么都不做:学员直接跑二进制零摩擦。 - -#include -#include -#include - -namespace d2x::report { - -inline std::string escape(const std::string& s) { - std::string out; - out.reserve(s.size() + 8); - for (unsigned char c : s) { - switch (c) { - case '"': out += "\\\""; break; - case '\\': out += "\\\\"; break; - case '\n': out += "\\n"; break; - case '\r': out += "\\r"; break; - case '\t': out += "\\t"; break; - default: - if (c < 0x20) { - char buf[8]; - std::snprintf(buf, sizeof buf, "\\u%04x", static_cast(c)); - out += buf; - } else { - out += static_cast(c); - } - } - } - return out; -} - -// 每次都重新打开并追加。比持有 FILE* 慢,但换来两个性质: -// 进程异常终止不丢已写入的行;无需依赖静态析构顺序。 -inline void emit(const std::string& json_line) { - const char* path = std::getenv("D2X_RESULT_FILE"); - if (!path || !*path) return; // 学员直接运行:什么都不做 - - if (std::FILE* f = std::fopen(path, "a")) { - std::fputs(json_line.c_str(), f); - std::fputc('\n', f); - std::fclose(f); - } -} - -inline void assertion(bool ok, const std::string& expr, - const std::string& expected, const std::string& actual, - const char* file, int line) { - std::string out = "{\"kind\":\"assert\",\"ok\":"; - out += ok ? "true" : "false"; - out += ",\"expr\":\"" + escape(expr) + "\""; - out += ",\"expected\":\"" + escape(expected) + "\""; - out += ",\"actual\":\"" + escape(actual) + "\""; - out += ",\"file\":\"" + escape(file ? file : "") + "\""; - out += ",\"line\":" + std::to_string(line) + "}"; - emit(out); -} - -inline void wait(const char* file, int line) { - std::string out = "{\"kind\":\"wait\",\"file\":\""; - out += escape(file ? file : ""); - out += "\",\"line\":" + std::to_string(line) + "}"; - emit(out); -} - -} // namespace d2x::report - -#endif // REPORT_HPP_D2X diff --git a/dslings/harness/mcpp.toml b/dslings/harness/mcpp.toml deleted file mode 100644 index d1cea20..0000000 --- a/dslings/harness/mcpp.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "harness" -version = "0.1.0" -description = "d2mcpp 练习脚手架:断言、路障、填空占位符,以及判定用的侧信道上报" -license = "Apache-2.0" -standard = "c++23" - -# include/ 会传播给消费者,所以练习里 `#include ` -# 这个既有路径原样可用 —— 104 个练习和书本一行都不用改。 -# 路径命名以后可以单独重整,不必和这次机制改造捆在一起。 -[build] -include_dirs = ["include"] - -[targets.harness] -kind = "lib" diff --git a/dslings/harness/src/harness.cppm b/dslings/harness/src/harness.cppm deleted file mode 100644 index 9f3c882..0000000 --- a/dslings/harness/src/harness.cppm +++ /dev/null @@ -1,62 +0,0 @@ -module; - -// 上报核心以头文件形式共享给两条路径。放在全局模块片段里引入, -// 这样模块实现能用它,而它的宏(无)不会泄漏给消费者。 -#include -#include - -export module d2x.harness; - -import std; - -// 模块化练习的脚手架 —— import 路径。 -// -// 与 #include 路径的差异,写在这里免得踩坑: -// -// 宏无法跨模块导出。所以这里提供的是函数,不是 d2x_assert 那套宏。 -// 好处是 std::source_location 自动带上 file/line,比 __LINE__ 更准, -// 而且正好是 Provider 填 Verdict.diagnostics 需要的东西;代价是丢了 -// 表达式原文(宏版能打印 "a == b",函数版只能打印值)。 -// -// D2X_YOUR_ANSWER 没有模块等价物 —— 它必须展开为空才能制造编译错误, -// 本质就是宏。模块化章节需要另一套填空约定。 -export namespace d2x { - -// 与 #include 路径共用同一套可见输出格式,学员在两种练习里看到的是一致的。 -inline void check(bool ok, std::source_location loc = std::source_location::current()) { - d2x::report::assertion(ok, "check", "true", ok ? "true" : "false", - loc.file_name(), static_cast(loc.line())); - if (ok) std::print("\033[32m[HONLY LOGI]: - ✅ | check\033[0m\n"); - else std::print("\033[33m[HONLY LOGW]: {}:{} - ❌(error) | check\033[0m\n", - loc.file_name(), loc.line()); - std::fflush(stdout); -} - -template -inline void check_eq(const A& a, const B& b, - std::source_location loc = std::source_location::current()) { - const bool ok = (a == b); - - auto show = [](const auto& v) -> std::string { - if constexpr (requires { std::format("{}", v); }) return std::format("{}", v); - else return {}; - }; - - d2x::report::assertion(ok, "check_eq", show(b), show(a), - loc.file_name(), static_cast(loc.line())); - - if (ok) std::print("\033[32m[HONLY LOGI]: - ✅ | {} == {}\033[0m\n", show(a), show(b)); - else std::print("\033[33m[HONLY LOGW]: {}:{} - ❌ | {} == {}\033[0m\n", - loc.file_name(), loc.line(), show(a), show(b)); - std::fflush(stdout); -} - -// D2X_WAIT 的模块等价物:显式路障,学员删掉它才算真正完成这一题。 -inline void wait(std::source_location loc = std::source_location::current()) { - d2x::report::wait(loc.file_name(), static_cast(loc.line())); - std::print("\033[33m[HONLY LOGW]: {}:{} - 🥳 Delete the d2x::wait() to continue...\033[0m\n", - loc.file_name(), loc.line()); - std::fflush(stdout); -} - -} // namespace d2x diff --git a/en/cpp11/mcpp.toml b/en/cpp11/mcpp.toml new file mode 100644 index 0000000..7c2d85d --- /dev/null +++ b/en/cpp11/mcpp.toml @@ -0,0 +1,10 @@ +# cpp11 (en) 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: +# mcpp test -p en/cpp11 全部练习(进度表) +# mcpp test -p en/cpp11 <子串> 只跑匹配的练习 +[package] +name = "en-cpp11" +version = "0.1.0" +standard = "c++23" + +[dependencies] +harness = { path = "../../harness" } diff --git a/dslings/en/cpp11/00-auto-and-decltype-0.cpp b/en/cpp11/tests/00-auto-and-decltype/0.cpp similarity index 73% rename from dslings/en/cpp11/00-auto-and-decltype-0.cpp rename to en/cpp11/tests/00-auto-and-decltype/0.cpp index 8f2896d..6a871c2 100644 --- a/dslings/en/cpp11/00-auto-and-decltype-0.cpp +++ b/en/cpp11/tests/00-auto-and-decltype/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-0.cpp +// file: en/cpp11/tests/00-auto-and-decltype/0.cpp // // Exercise: cpp11 | 00 - auto and decltype | Automatic Type Deduction // @@ -18,7 +18,8 @@ // d2x checker auto-and-decltype // -#include +import std; +import d2x.harness; int main() { @@ -35,14 +36,13 @@ int main() { D2X_YOUR_ANSWER c1 = c; D2X_YOUR_ANSWER c2 = c; - d2x_assert_eq(a, a1); - d2x_assert_eq(a1, a2); - d2x_assert_eq(b, b1); - d2x_assert_eq(b1, b2); - d2x_assert_eq(c, c1); - d2x_assert_eq(c1, c2); - - D2X_WAIT + d2x::check_eq(a, a1, "a == a1"); + d2x::check_eq(a1, a2, "a1 == a2"); + d2x::check_eq(b, b1, "b == b1"); + d2x::check_eq(b1, b2, "b1 == b2"); + d2x::check_eq(c, c1, "c == c1"); + d2x::check_eq(c1, c2, "c1 == c2"); + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/00-auto-and-decltype-1.cpp b/en/cpp11/tests/00-auto-and-decltype/1.cpp similarity index 75% rename from dslings/en/cpp11/00-auto-and-decltype-1.cpp rename to en/cpp11/tests/00-auto-and-decltype/1.cpp index a3b9a01..bc9ce6d 100644 --- a/dslings/en/cpp11/00-auto-and-decltype-1.cpp +++ b/en/cpp11/tests/00-auto-and-decltype/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-1.cpp +// file: en/cpp11/tests/00-auto-and-decltype/1.cpp // // Exercise: cpp11 | 00 - auto and decltype | Expression Type Deduction // @@ -19,7 +19,8 @@ // -#include +import std; +import d2x.harness; int main() { @@ -36,12 +37,11 @@ int main() { D2X_YOUR_ANSWER c1 = 1 + c; D2X_YOUR_ANSWER c2 = 2 + 'a'; - d2x_assert_eq(a2, a + 2 + 1.1); - d2x_assert_eq(b1, a + 0.1); - d2x_assert_eq(c1, 1 + c); - d2x_assert_eq(c2, 2 + 'a'); - - D2X_WAIT + d2x::check_eq(a2, a + 2 + 1.1, "a2 == a + 2 + 1.1"); + d2x::check_eq(b1, a + 0.1, "b1 == a + 0.1"); + d2x::check_eq(c1, 1 + c, "c1 == 1 + c"); + d2x::check_eq(c2, 2 + 'a', "c2 == 2 + 'a'"); + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/00-auto-and-decltype-2.cpp b/en/cpp11/tests/00-auto-and-decltype/2.cpp similarity index 83% rename from dslings/en/cpp11/00-auto-and-decltype-2.cpp rename to en/cpp11/tests/00-auto-and-decltype/2.cpp index fb1cffd..251dbae 100644 --- a/dslings/en/cpp11/00-auto-and-decltype-2.cpp +++ b/en/cpp11/tests/00-auto-and-decltype/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-2.cpp +// file: en/cpp11/tests/00-auto-and-decltype/2.cpp // // Exercise: cpp11 | 00 - auto and decltype | Complex Type Deduction // @@ -18,11 +18,9 @@ // d2x checker auto-and-decltype-2 // -#include +import std; +import d2x.harness; -#include -#include -#include int add_func(int a, int b) { return a + b; @@ -53,10 +51,9 @@ int main() { minus_func }; - d2x_assert_eq(funcVec[0](1, 2), 3); - d2x_assert_eq(funcVec[1](1, 2), -1); - - D2X_WAIT + d2x::check_eq(funcVec[0](1, 2), 3, "funcVec[0](1, 2) == 3"); + d2x::check_eq(funcVec[1](1, 2), -1, "funcVec[1](1, 2) == -1"); + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/00-auto-and-decltype-3.cpp b/en/cpp11/tests/00-auto-and-decltype/3.cpp similarity index 73% rename from dslings/en/cpp11/00-auto-and-decltype-3.cpp rename to en/cpp11/tests/00-auto-and-decltype/3.cpp index fb7d426..71470e8 100644 --- a/dslings/en/cpp11/00-auto-and-decltype-3.cpp +++ b/en/cpp11/tests/00-auto-and-decltype/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-3.cpp +// file: en/cpp11/tests/00-auto-and-decltype/3.cpp // // Exercise: cpp11 | 00 - auto and decltype | Function Return Type Deduction // @@ -18,10 +18,9 @@ // d2x checker auto-and-decltype-3 // -#include +import std; +import d2x.harness; -#include -#include // 3. Function return types @@ -36,11 +35,10 @@ D2X_YOUR_ANSWER minus_func(T1 a, T2 b) -> D2X_YOUR_ANSWER { int main() { - d2x_assert_eq(minus_func(1, 2), -1); - d2x_assert_eq(minus_func(2, 1), 1); - d2x_assert_eq(minus_func(1, 2.1), -1.1); - - D2X_WAIT + d2x::check_eq(minus_func(1, 2), -1, "minus_func(1, 2) == -1"); + d2x::check_eq(minus_func(2, 1), 1, "minus_func(2, 1) == 1"); + d2x::check_eq(minus_func(1, 2.1), -1.1, "minus_func(1, 2.1) == -1.1"); + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/00-auto-and-decltype-4.cpp b/en/cpp11/tests/00-auto-and-decltype/4.cpp similarity index 69% rename from dslings/en/cpp11/00-auto-and-decltype-4.cpp rename to en/cpp11/tests/00-auto-and-decltype/4.cpp index f56a15e..65e24ed 100644 --- a/dslings/en/cpp11/00-auto-and-decltype-4.cpp +++ b/en/cpp11/tests/00-auto-and-decltype/4.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-4.cpp +// file: en/cpp11/tests/00-auto-and-decltype/4.cpp // // Exercise: cpp11 | 00 - auto and decltype | Class/Struct Member Type Deduction // @@ -18,9 +18,9 @@ // d2x checker auto-and-decltype-4 // -#include +import std; +import d2x.harness; -#include // 4. Class/Struct member type deduction @@ -38,23 +38,22 @@ int main() { // Type deduction for obj and (obj) type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // Type deduction for obj.a and (obj.a) type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // Type deduction for obj.b and (obj.b) type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line - - D2X_WAIT + d2x::check(type_check, "type_check"); type_check = false; // dont change this line + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/00-auto-and-decltype-5.cpp b/en/cpp11/tests/00-auto-and-decltype/5.cpp similarity index 76% rename from dslings/en/cpp11/00-auto-and-decltype-5.cpp rename to en/cpp11/tests/00-auto-and-decltype/5.cpp index 90d1664..c30f3ad 100644 --- a/dslings/en/cpp11/00-auto-and-decltype-5.cpp +++ b/en/cpp11/tests/00-auto-and-decltype/5.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/00-auto-and-decltype-5.cpp +// file: en/cpp11/tests/00-auto-and-decltype/5.cpp // // Exercise: cpp11 | 00 - auto and decltype | const & reference stripping and preservation // @@ -18,9 +18,9 @@ // d2x checker auto-and-decltype-5 // -#include +import std; +import d2x.harness; -#include // 5. const & reference stripping and preservation @@ -35,24 +35,23 @@ int main() { // auto strips top-level const: what does a deduce to? auto a = ci; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // auto strips the reference: what does b deduce to? (b is an independent copy of n) auto b = ri; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // To keep a const reference const int&: how should cr be declared? D2X_YOUR_ANSWER cr = ci; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // To preserve the declared type const int exactly: how should d be declared? D2X_YOUR_ANSWER d = ci; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line - - D2X_WAIT + d2x::check(type_check, "type_check"); type_check = false; // dont change this line + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/01-default-and-delete-0.cpp b/en/cpp11/tests/01-default-and-delete/0.cpp similarity index 88% rename from dslings/en/cpp11/01-default-and-delete-0.cpp rename to en/cpp11/tests/01-default-and-delete/0.cpp index 0d03144..2c9e868 100644 --- a/dslings/en/cpp11/01-default-and-delete-0.cpp +++ b/en/cpp11/tests/01-default-and-delete/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/01-default-and-delete-0.cpp +// file: en/cpp11/tests/01-default-and-delete/0.cpp // // Exercise: cpp11 | 01 - default and delete | Explicitly specifying constructor generation behavior // @@ -15,9 +15,9 @@ // d2x checker default-and-delete // -#include +import std; +import d2x.harness; -#include // default and delete explicitly control -> compiler default constructor generation behavior struct A { }; @@ -35,7 +35,6 @@ int main() { // Do not directly modify the code in the main function B b; C c(1); - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/01-default-and-delete-1.cpp b/en/cpp11/tests/01-default-and-delete/1.cpp similarity index 61% rename from dslings/en/cpp11/01-default-and-delete-1.cpp rename to en/cpp11/tests/01-default-and-delete/1.cpp index af05d61..600af67 100644 --- a/dslings/en/cpp11/01-default-and-delete-1.cpp +++ b/en/cpp11/tests/01-default-and-delete/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/01-default-and-delete-1.cpp +// file: en/cpp11/tests/01-default-and-delete/1.cpp // // Exercise: cpp11 | 01 - default and delete | Non-copyable objects // @@ -15,9 +15,9 @@ // d2x checker default-and-delete-1 // -#include +import std; +import d2x.harness; -#include // Implement std::unique_ptr property: not copyable but movable struct UniquePtr { @@ -32,17 +32,16 @@ int main() { // Do not directly modify the code in the main function // Object cannot be copied/duplicated // std::unique_ptr b = a; // error - d2x_assert(std::is_copy_constructible::value == false); + d2x::check(std::is_copy_constructible::value == false, "std::is_copy_constructible::value == false"); // a = b; // error - d2x_assert(std::is_copy_assignable::value == false); + d2x::check(std::is_copy_assignable::value == false, "std::is_copy_assignable::value == false"); // Object can be moved // std::unique_ptr c = std::move(a); // ok - d2x_assert(std::is_move_constructible::value == true); + d2x::check(std::is_move_constructible::value == true, "std::is_move_constructible::value == true"); // a = std::move(c); // ok - d2x_assert(std::is_move_assignable::value == true); - - D2X_WAIT + d2x::check(std::is_move_assignable::value == true, "std::is_move_assignable::value == true"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/01-default-and-delete-2.cpp b/en/cpp11/tests/01-default-and-delete/2.cpp similarity index 85% rename from dslings/en/cpp11/01-default-and-delete-2.cpp rename to en/cpp11/tests/01-default-and-delete/2.cpp index 49fc1ad..aa9ea5d 100644 --- a/dslings/en/cpp11/01-default-and-delete-2.cpp +++ b/en/cpp11/tests/01-default-and-delete/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/01-default-and-delete-2.cpp +// file: en/cpp11/tests/01-default-and-delete/2.cpp // // Exercise: cpp11 | 01 - default and delete | Disabling function overloading // @@ -15,9 +15,9 @@ // d2x checker default-and-delete-2 // -#include +import std; +import d2x.harness; -#include void func(int x) { std::cout << "x = " << x << std::endl; @@ -31,7 +31,6 @@ int main() { func(1); // int func(1.1f); // float - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/02-final-and-override-0.cpp b/en/cpp11/tests/02-final-and-override/0.cpp similarity index 88% rename from dslings/en/cpp11/02-final-and-override-0.cpp rename to en/cpp11/tests/02-final-and-override/0.cpp index a141dd1..db4c662 100644 --- a/dslings/en/cpp11/02-final-and-override-0.cpp +++ b/en/cpp11/tests/02-final-and-override/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/02-final-and-override-0.cpp +// file: en/cpp11/tests/02-final-and-override/0.cpp // // Exercise: cpp11 | 02 - final and override // @@ -15,10 +15,9 @@ // d2x checker final-and-override // -#include +import std; +import d2x.harness; -#include -#include struct A { virtual void func1() { @@ -51,7 +50,6 @@ int main() { a->func1(); // B::func1() a->func2(); // A::func2() - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/02-final-and-override-1.cpp b/en/cpp11/tests/02-final-and-override/1.cpp similarity index 65% rename from dslings/en/cpp11/02-final-and-override-1.cpp rename to en/cpp11/tests/02-final-and-override/1.cpp index bad62e2..7580f54 100644 --- a/dslings/en/cpp11/02-final-and-override-1.cpp +++ b/en/cpp11/tests/02-final-and-override/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/02-final-and-override-1.cpp +// file: en/cpp11/tests/02-final-and-override/1.cpp // // Exercise: cpp11 | 02 - final and override // @@ -15,10 +15,9 @@ // d2x checker final-and-override-1 // -#include +import std; +import d2x.harness; -#include -#include struct A { virtual int func1() { @@ -45,14 +44,13 @@ struct C : B { int main() { B final; // Do not directly modify the code in the main function - d2x_assert_eq(final.func1(), 3); // B::func1() - d2x_assert_eq(final.func2(), 4); // B::func2() + d2x::check_eq(final.func1(), 3, "final.func1() == 3"); // B::func1() + d2x::check_eq(final.func2(), 4, "final.func2() == 4"); // B::func2() A *a = &final; - d2x_assert_eq(a->func1(), 3); // B::func1() - d2x_assert_eq(a->func2(), 2); // A::func2() - - D2X_WAIT + d2x::check_eq(a->func1(), 3, "a->func1() == 3"); // B::func1() + d2x::check_eq(a->func2(), 2, "a->func2() == 2"); // A::func2() + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/02-final-and-override-2.cpp b/en/cpp11/tests/02-final-and-override/2.cpp similarity index 93% rename from dslings/en/cpp11/02-final-and-override-2.cpp rename to en/cpp11/tests/02-final-and-override/2.cpp index 609f684..4a61163 100644 --- a/dslings/en/cpp11/02-final-and-override-2.cpp +++ b/en/cpp11/tests/02-final-and-override/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/02-final-and-override-2.cpp +// file: en/cpp11/tests/02-final-and-override/2.cpp // // Exercise: cpp11 | 02 - final and override // @@ -15,10 +15,9 @@ // d2x checker final-and-override-2 // -#include +import std; +import d2x.harness; -#include -#include struct AudioPlayer { // Do not directly modify the AudioPlayer class virtual void play() final { @@ -77,7 +76,6 @@ int main() { // Do not directly modify the code in the main function delete player2; delete player3; - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/03-trailing-return-type.cpp b/en/cpp11/tests/03-trailing-return-type/0.cpp similarity index 63% rename from dslings/en/cpp11/03-trailing-return-type.cpp rename to en/cpp11/tests/03-trailing-return-type/0.cpp index 7c2d97a..20f36ff 100644 --- a/dslings/en/cpp11/03-trailing-return-type.cpp +++ b/en/cpp11/tests/03-trailing-return-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/03-trailing-return-type.cpp +// file: en/cpp11/tests/03-trailing-return-type/0.cpp // // Exercise: cpp11 | 03 - trailing return type // @@ -14,9 +14,9 @@ // d2x checker trailing-return-type // -#include +import std; +import d2x.harness; -#include int add0(double a, int b) { return a + b; @@ -37,13 +37,12 @@ auto add3 = [](double a, double b) -> D2X_YOUR_ANSWER { int main() { - d2x_assert_eq(add0(1.1, 2), 3); - d2x_assert_eq(add1(1.1, 2), 3); - d2x_assert_eq(add2(1.1, 2), 3.1); - d2x_assert_eq(add2(1, 2.1), 3.1); - d2x_assert_eq(add3(1.1, 2.1), 3); - - D2X_WAIT + d2x::check_eq(add0(1.1, 2), 3, "add0(1.1, 2) == 3"); + d2x::check_eq(add1(1.1, 2), 3, "add1(1.1, 2) == 3"); + d2x::check_eq(add2(1.1, 2), 3.1, "add2(1.1, 2) == 3.1"); + d2x::check_eq(add2(1, 2.1), 3.1, "add2(1, 2.1) == 3.1"); + d2x::check_eq(add3(1.1, 2.1), 3, "add3(1.1, 2.1) == 3"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/04-rvalue-references.cpp b/en/cpp11/tests/04-rvalue-references/0.cpp similarity index 89% rename from dslings/en/cpp11/04-rvalue-references.cpp rename to en/cpp11/tests/04-rvalue-references/0.cpp index b8ac8fd..871e4c1 100644 --- a/dslings/en/cpp11/04-rvalue-references.cpp +++ b/en/cpp11/tests/04-rvalue-references/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/04-rvalue-references.cpp +// file: en/cpp11/tests/04-rvalue-references/0.cpp // // Exercise: cpp11 | 04 - rvalue references // @@ -14,10 +14,9 @@ // d2x checker rvalue-references // -#include +import std; +import d2x.harness; -#include -#include struct Object; static Object * object_address = nullptr; @@ -56,13 +55,12 @@ int main() { // Disable compiler optimization objRef.data = 1; // Modify the value of the extended lifetime temporary object (do not directly modify this line) std::cout << "objRef.data = " << objRef.data << " - " << &objRef << std::endl; - d2x_assert((&objRef == object_address)); + d2x::check((&objRef == object_address), "(&objRef == object_address)"); // 钉住移动构造确实发生过。教学漂移之所以能静默发生, 正是因为 // 从前没有任何断言检查它 —— 输出少了一行, 没人发现。 - d2x_assert((move_ctor_calls >= 1)); + d2x::check((move_ctor_calls >= 1), "(move_ctor_calls >= 1)"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/05-move-semantics-0.cpp b/en/cpp11/tests/05-move-semantics/0.cpp similarity index 83% rename from dslings/en/cpp11/05-move-semantics-0.cpp rename to en/cpp11/tests/05-move-semantics/0.cpp index bd488d4..5461403 100644 --- a/dslings/en/cpp11/05-move-semantics-0.cpp +++ b/en/cpp11/tests/05-move-semantics/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/05-move-semantics-0.cpp +// file: en/cpp11/tests/05-move-semantics/0.cpp // // Exercise: cpp11 | 05 - move semantics | Move Construction and Trigger Timing // @@ -14,9 +14,9 @@ // d2x checker move-semantics // -#include +import std; +import d2x.harness; -#include struct Buffer { int *data; @@ -57,20 +57,19 @@ int main() { Buffer buff2(std::move(buff1)); auto buff2DataPtr = buff2.data_ptr(); - d2x_assert(buff1DataPtr == buff2DataPtr); + d2x::check(buff1DataPtr == buff2DataPtr, "buff1DataPtr == buff2DataPtr"); Buffer buff3 = buff2; auto buff3DataPtr = buff3.data_ptr(); - d2x_assert(buff2DataPtr == buff3DataPtr); + d2x::check(buff2DataPtr == buff3DataPtr, "buff2DataPtr == buff3DataPtr"); Buffer buff4 = process(buff3); auto buff4DataPtr = buff4.data_ptr(); - d2x_assert(buff3DataPtr == buff4DataPtr); + d2x::check(buff3DataPtr == buff4DataPtr, "buff3DataPtr == buff4DataPtr"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/05-move-semantics-1.cpp b/en/cpp11/tests/05-move-semantics/1.cpp similarity index 87% rename from dslings/en/cpp11/05-move-semantics-1.cpp rename to en/cpp11/tests/05-move-semantics/1.cpp index 0589a4c..a87c0f7 100644 --- a/dslings/en/cpp11/05-move-semantics-1.cpp +++ b/en/cpp11/tests/05-move-semantics/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/05-move-semantics-1.cpp +// file: en/cpp11/tests/05-move-semantics/1.cpp // // Exercise: cpp11 | 05 - move semantics | Move Assignment and Trigger Timing // @@ -14,9 +14,9 @@ // d2x checker move-semantics // -#include +import std; +import d2x.harness; -#include static int move_assignment_counter = 0; @@ -80,21 +80,20 @@ int main() { // No compiler optimization buff1 = Buffer(); // Case 1: Temporary object assignment - d2x_assert_eq(move_assignment_counter, 1); + d2x::check_eq(move_assignment_counter, 1, "move_assignment_counter == 1"); Buffer buff2; buff2 = process(buff1); // Case 2: Intermediate object assignment - d2x_assert_eq(move_assignment_counter, 2); + d2x::check_eq(move_assignment_counter, 2, "move_assignment_counter == 2"); buff2 = buff1; // Case 3: Explicit move assignment - d2x_assert_eq(move_assignment_counter, 3); + d2x::check_eq(move_assignment_counter, 3, "move_assignment_counter == 3"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/05-move-semantics-2.cpp b/en/cpp11/tests/05-move-semantics/2.cpp similarity index 85% rename from dslings/en/cpp11/05-move-semantics-2.cpp rename to en/cpp11/tests/05-move-semantics/2.cpp index cffbadb..c8224de 100644 --- a/dslings/en/cpp11/05-move-semantics-2.cpp +++ b/en/cpp11/tests/05-move-semantics/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/05-move-semantics-2.cpp +// file: en/cpp11/tests/05-move-semantics/2.cpp // // Exercise: cpp11 | 05 - move semantics | Moving resources not objects // @@ -14,9 +14,9 @@ // d2x checker move-semantics-2 // -#include +import std; +import d2x.harness; -#include struct Buffer { int *data; @@ -72,13 +72,12 @@ int main() { // Move semantics - Demonstrating moving resources not objects Buffer b2 = b1; // std::move(b1); - d2x_assert(&b1 != &b2); // b1 and b2 are different objects - d2x_assert(old_b1_data_ptr == b2.data_ptr()); - d2x_assert(b1.data_ptr() == nullptr); // b1's resources have been moved + d2x::check(&b1 != &b2, "&b1 != &b2"); // b1 and b2 are different objects + d2x::check(old_b1_data_ptr == b2.data_ptr(), "old_b1_data_ptr == b2.data_ptr()"); + d2x::check(b1.data_ptr() == nullptr, "b1.data_ptr() == nullptr"); // b1's resources have been moved } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/06-scoped-enums-0.cpp b/en/cpp11/tests/06-scoped-enums/0.cpp similarity index 78% rename from dslings/en/cpp11/06-scoped-enums-0.cpp rename to en/cpp11/tests/06-scoped-enums/0.cpp index c77c40a..ac5b251 100644 --- a/dslings/en/cpp11/06-scoped-enums-0.cpp +++ b/en/cpp11/tests/06-scoped-enums/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/06-scoped-enums-0.cpp +// file: en/cpp11/tests/06-scoped-enums/0.cpp // // Exercise: cpp11 | 06 - scoped enums | Traditional enum type potential issues // @@ -14,9 +14,9 @@ // d2x checker scoped-enums // -#include +import std; +import d2x.harness; -#include enum Color { RED, @@ -36,20 +36,19 @@ int main() { Color color = RED; Fruit fruit = Apple; - d2x_assert_eq(color, RED); - d2x_assert_eq(fruit, Apple); + d2x::check_eq(color, RED, "color == RED"); + d2x::check_eq(fruit, Apple, "fruit == Apple"); // 2. Syntactically correct, but logically wrong type matching if (color == Apple) { // Do not delete this line of code // Code will run here - D2X_WAIT + d2x::wait(); } if (fruit == RED) { - D2X_WAIT + d2x::wait(); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/06-scoped-enums-1.cpp b/en/cpp11/tests/06-scoped-enums/1.cpp similarity index 70% rename from dslings/en/cpp11/06-scoped-enums-1.cpp rename to en/cpp11/tests/06-scoped-enums/1.cpp index eddfdb6..dd44815 100644 --- a/dslings/en/cpp11/06-scoped-enums-1.cpp +++ b/en/cpp11/tests/06-scoped-enums/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/06-scoped-enums-1.cpp +// file: en/cpp11/tests/06-scoped-enums/1.cpp // // Exercise: cpp11 | 06 - scoped enums | Scoped enum type basic usage // @@ -14,9 +14,9 @@ // d2x checker scoped-enums-1 // -#include +import std; +import d2x.harness; -#include enum class Color { RED, @@ -37,12 +37,12 @@ int main() { Color color = Color::ORANGE; Fruit fruit = Fruit::ORANGE; - d2x_assert(color == Color::ORANGE); - d2x_assert(fruit == Fruit::ORANGE); + d2x::check(color == Color::ORANGE, "color == Color::ORANGE"); + d2x::check(fruit == Fruit::ORANGE, "fruit == Fruit::ORANGE"); // 2. Type safety: Prevent comparison between different enum type values if (color == Fruit::ORANGE) { // Use Color type to fix compilation error - d2x_assert(color == Color::ORANGE); + d2x::check(color == Color::ORANGE, "color == Color::ORANGE"); } // 3. Type checking: By default, scoped enum type values cannot be implicitly converted @@ -56,8 +56,8 @@ int main() { ORANGE // Orange color }; - d2x_assert_eq(sizeof(Color), sizeof(int)); // Default type is int - d2x_assert_eq(sizeof(Color8Bit), sizeof(int8_t)); // Can customize type int8_t + d2x::check_eq(sizeof(Color), sizeof(int), "sizeof(Color) == sizeof(int)"); // Default type is int + d2x::check_eq(sizeof(Color8Bit), sizeof(int8_t), "sizeof(Color8Bit) == sizeof(int8_t)"); // Can customize type int8_t // 5. Custom starting value: By default, scoped enum type values start from 0 and increment downward enum class ErrorCode : int { @@ -67,9 +67,8 @@ int main() { ERROR_3 }; - d2x_assert_eq(static_cast(ErrorCode::ERROR_3), 3); - - D2X_WAIT + d2x::check_eq(static_cast(ErrorCode::ERROR_3), 3, "static_cast(ErrorCode::ERROR_3) == 3"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/07-constexpr-0.cpp b/en/cpp11/tests/07-constexpr/0.cpp similarity index 84% rename from dslings/en/cpp11/07-constexpr-0.cpp rename to en/cpp11/tests/07-constexpr/0.cpp index b381c59..52e429e 100644 --- a/dslings/en/cpp11/07-constexpr-0.cpp +++ b/en/cpp11/tests/07-constexpr/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/07-constexpr-0.cpp +// file: en/cpp11/tests/07-constexpr/0.cpp // // Exercise: cpp11 | 07 - constexpr | Compile-time computation basics: Difference between constexpr and const // @@ -14,9 +14,9 @@ // d2x checker constexpr // -#include +import std; +import d2x.harness; -#include int sum_for_1_to(int n) { return n == 1 ? 1 : n + sum_for_1_to(n - 1); @@ -34,10 +34,9 @@ int main() { { // 2. Compile-time computation basics constexpr int s = sum_for_1_to(4); - d2x_assert_eq(s, 1 + 2 + 3 + 4); + d2x::check_eq(s, 1 + 2 + 3 + 4, "s == 1 + 2 + 3 + 4"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/07-constexpr-1.cpp b/en/cpp11/tests/07-constexpr/1.cpp similarity index 94% rename from dslings/en/cpp11/07-constexpr-1.cpp rename to en/cpp11/tests/07-constexpr/1.cpp index 47156ee..5f7aaf1 100644 --- a/dslings/en/cpp11/07-constexpr-1.cpp +++ b/en/cpp11/tests/07-constexpr/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/07-constexpr-1.cpp +// file: en/cpp11/tests/07-constexpr/1.cpp // // Exercise: cpp11 | 07 - constexpr | Compile-time computation application examples // @@ -14,9 +14,9 @@ // d2x checker constexpr // -#include +import std; +import d2x.harness; -#include template struct Sum { @@ -67,7 +67,6 @@ int main() { constexpr double sin30 = mysin(30.0); std::cout << "mysin(30): " << sin30 << " " << std::endl; - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/08-literal-type-0.cpp b/en/cpp11/tests/08-literal-type/0.cpp similarity index 91% rename from dslings/en/cpp11/08-literal-type-0.cpp rename to en/cpp11/tests/08-literal-type/0.cpp index a6c92e8..a99b103 100644 --- a/dslings/en/cpp11/08-literal-type-0.cpp +++ b/en/cpp11/tests/08-literal-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/08-literal-type-0.cpp +// file: en/cpp11/tests/08-literal-type/0.cpp // // Exercise: cpp11 | 08 - literal type | What are literal types // @@ -14,11 +14,9 @@ // d2x checker literal-type-0 // -#include +import std; +import d2x.harness; -#include -#include -#include constexpr char compile_time_compute(char c, int a) { return a + c; @@ -54,7 +52,6 @@ int main() { std::cout << "1 + 2 + 3 = " << sum << std::endl; - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/08-literal-type-1.cpp b/en/cpp11/tests/08-literal-type/1.cpp similarity index 87% rename from dslings/en/cpp11/08-literal-type-1.cpp rename to en/cpp11/tests/08-literal-type/1.cpp index 5f3d785..7d5db3f 100644 --- a/dslings/en/cpp11/08-literal-type-1.cpp +++ b/en/cpp11/tests/08-literal-type/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/08-literal-type-1.cpp +// file: en/cpp11/tests/08-literal-type/1.cpp // // Exercise: cpp11 | 08 - literal type | Custom literal types // @@ -14,9 +14,9 @@ // d2x checker literal-type-1 // -#include +import std; +import d2x.harness; -#include struct Vector { int x, y; @@ -34,7 +34,6 @@ int main() { std::cout << "[ " << v3.x << ", " << v3.y << " ]" << std::endl; - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/09-list-initialization-0.cpp b/en/cpp11/tests/09-list-initialization/0.cpp similarity index 61% rename from dslings/en/cpp11/09-list-initialization-0.cpp rename to en/cpp11/tests/09-list-initialization/0.cpp index 3fe3da5..dd8d73f 100644 --- a/dslings/en/cpp11/09-list-initialization-0.cpp +++ b/en/cpp11/tests/09-list-initialization/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/09-list-initialization-0.cpp +// file: en/cpp11/tests/09-list-initialization/0.cpp // // Exercise: cpp11 | 09 - list initialization | Narrowing checks // @@ -15,19 +15,19 @@ // d2x checker list-initialization // -#include +import std; +import d2x.harness; -#include int main() { int a1 = 1.1; - d2x_assert_eq(a1, 1); + d2x::check_eq(a1, 1, "a1 == 1"); int a2 = 1.1; int a3 = { 1.1 }; - d2x_assert_eq(a2, 1); - d2x_assert_eq(a3, 1); + d2x::check_eq(a2, 1, "a2 == 1"); + d2x::check_eq(a3, 1, "a3 == 1"); double b1 { 1.1 }; constexpr double c1 { 2.2 }; @@ -35,18 +35,17 @@ int main() { int b2 { b1 }; int c2 { c1 }; - d2x_assert_eq(b2, 1); - d2x_assert_eq(c2, 2); + d2x::check_eq(b2, 1, "b2 == 1"); + d2x::check_eq(c2, 2, "c2 == 2"); int arr1[] = { 1, 2.2, 3 }; - d2x_assert_eq(arr1[1], 2); + d2x::check_eq(arr1[1], 2, "arr1[1] == 2"); int arr2[4] { 1, b1, c1 }; - d2x_assert_eq(arr2[1], 1); - d2x_assert_eq(arr2[2], 2); - d2x_assert_eq(arr2[3], 0); - - D2X_WAIT + d2x::check_eq(arr2[1], 1, "arr2[1] == 1"); + d2x::check_eq(arr2[2], 2, "arr2[2] == 2"); + d2x::check_eq(arr2[3], 0, "arr2[3] == 0"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/09-list-initialization-1.cpp b/en/cpp11/tests/09-list-initialization/1.cpp similarity index 80% rename from dslings/en/cpp11/09-list-initialization-1.cpp rename to en/cpp11/tests/09-list-initialization/1.cpp index 1245e9b..52357b1 100644 --- a/dslings/en/cpp11/09-list-initialization-1.cpp +++ b/en/cpp11/tests/09-list-initialization/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/09-list-initialization-1.cpp +// file: en/cpp11/tests/09-list-initialization/1.cpp // // Exercise: cpp11 | 09 - list initialization | Default initialization syntax pitfalls // @@ -15,9 +15,9 @@ // d2x checker list-initialization // -#include +import std; +import d2x.harness; -#include struct Object { Object() { @@ -35,10 +35,9 @@ int main() { Object obj1(); Object obj2(2); - d2x_assert_eq(obj1.x, 0); - d2x_assert_eq(obj2.x, 2); - - D2X_WAIT + d2x::check_eq(obj1.x, 0, "obj1.x == 0"); + d2x::check_eq(obj2.x, 2, "obj2.x == 2"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/09-list-initialization-2.cpp b/en/cpp11/tests/09-list-initialization/2.cpp similarity index 74% rename from dslings/en/cpp11/09-list-initialization-2.cpp rename to en/cpp11/tests/09-list-initialization/2.cpp index 5914394..ca84a41 100644 --- a/dslings/en/cpp11/09-list-initialization-2.cpp +++ b/en/cpp11/tests/09-list-initialization/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/09-list-initialization-2.cpp +// file: en/cpp11/tests/09-list-initialization/2.cpp // // Exercise: cpp11 | 09 - list initialization | Container list initialization // @@ -16,11 +16,9 @@ // d2x checker list-initialization // -#include +import std; +import d2x.harness; -#include -#include -#include class MyVector { int mSize; @@ -36,16 +34,15 @@ class MyVector { int main() { std::vector vec1 = { 1, 2, 3 }; - d2x_assert_eq(vec1.size(), 3); + d2x::check_eq(vec1.size(), 3, "vec1.size() == 3"); std::vector vec2 { 1, 2, 3, 4, 5 }; - d2x_assert_eq(vec2.size(), 5); + d2x::check_eq(vec2.size(), 5, "vec2.size() == 5"); MyVector myVec1 = { 1, 2, 3 }; - d2x_assert_eq(myVec1.size(), 3); + d2x::check_eq(myVec1.size(), 3, "myVec1.size() == 3"); MyVector myVec2 { 1, 2, 3, 4, 5 }; - d2x_assert_eq(myVec2.size(), 5); - - D2X_WAIT + d2x::check_eq(myVec2.size(), 5, "myVec2.size() == 5"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/09-list-initialization-3.cpp b/en/cpp11/tests/09-list-initialization/3.cpp similarity index 82% rename from dslings/en/cpp11/09-list-initialization-3.cpp rename to en/cpp11/tests/09-list-initialization/3.cpp index 416c660..0b5cd94 100644 --- a/dslings/en/cpp11/09-list-initialization-3.cpp +++ b/en/cpp11/tests/09-list-initialization/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/09-list-initialization-3.cpp +// file: en/cpp11/tests/09-list-initialization/3.cpp // // Exercise: cpp11 | 09 - list initialization | Precautions // @@ -16,11 +16,9 @@ // d2x checker list-initialization // -#include +import std; +import d2x.harness; -#include -#include -#include class MyVector { int mSize; @@ -73,16 +71,15 @@ int main() { Point p2 {3, 4}; MyVector vec1(1); - d2x_assert_eq(vec1.size(), 1); + d2x::check_eq(vec1.size(), 1, "vec1.size() == 1"); MyVector vec2 { 1 }; - d2x_assert_eq(vec2.size(), 1); + d2x::check_eq(vec2.size(), 1, "vec2.size() == 1"); MyVector vec3(1, 10); - d2x_assert_eq(vec3.size(), 10); + d2x::check_eq(vec3.size(), 10, "vec3.size() == 10"); MyVector vec4 { 1, 10 }; - d2x_assert_eq(vec4.size(), 10); - - D2X_WAIT + d2x::check_eq(vec4.size(), 10, "vec4.size() == 10"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/10-delegating-constructors-0.cpp b/en/cpp11/tests/10-delegating-constructors/0.cpp similarity index 77% rename from dslings/en/cpp11/10-delegating-constructors-0.cpp rename to en/cpp11/tests/10-delegating-constructors/0.cpp index b79d40a..1549d18 100644 --- a/dslings/en/cpp11/10-delegating-constructors-0.cpp +++ b/en/cpp11/tests/10-delegating-constructors/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/10-delegating-constructors-0.cpp +// file: en/cpp11/tests/10-delegating-constructors/0.cpp // // Exercise: cpp11 | 10 - delegating constructors | Delegating Constructors // @@ -15,10 +15,9 @@ // d2x checker delegating-constructors // -#include +import std; +import d2x.harness; -#include -#include static int construction_counter { 0 }; @@ -60,26 +59,23 @@ class Account { int main() { // Do not modify the code in the main function Account a1 { "1111" }; - d2x_assert_eq(construction_counter, 3); + d2x::check_eq(construction_counter, 3, "construction_counter == 3"); std::cout << a1.to_string() << std::endl; Account a2 { "2222", "wukong" }; - d2x_assert_eq(construction_counter, 5); + d2x::check_eq(construction_counter, 5, "construction_counter == 5"); std::cout << a2.to_string() << std::endl; Account a3 { "3333", "mcpp", 100 }; - d2x_assert_eq(construction_counter, 6); + d2x::check_eq(construction_counter, 6, "construction_counter == 6"); std::cout << a3.to_string() << std::endl; Account gi { "0000", "GImpact", 648 }; std::cout << gi.to_string() << std::endl; - d2x_assert( - gi.to_string() == - "Account { id: 0000, name: GImpact, coin: 648原石 }" - ); - - D2X_WAIT + d2x::check(gi.to_string() == + "Account { id: 0000, name: GImpact, coin: 648原石 }", "gi.to_string() == \"Account { id: 0000, name: GImpact, coin: 648原石 }\""); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/10-delegating-constructors-1.cpp b/en/cpp11/tests/10-delegating-constructors/1.cpp similarity index 85% rename from dslings/en/cpp11/10-delegating-constructors-1.cpp rename to en/cpp11/tests/10-delegating-constructors/1.cpp index 4843fd2..5b15c61 100644 --- a/dslings/en/cpp11/10-delegating-constructors-1.cpp +++ b/en/cpp11/tests/10-delegating-constructors/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/10-delegating-constructors-1.cpp +// file: en/cpp11/tests/10-delegating-constructors/1.cpp // // Exercise: cpp11 | 10 - delegating constructors | Delegating Constructors Precautions // @@ -15,10 +15,9 @@ // d2x checker delegating-constructors // -#include +import std; +import d2x.harness; -#include -#include struct Object { // Do not modify the code of this class static int construction_counter; @@ -77,16 +76,15 @@ int main() { // Do not modify the code in the main function Account a1 { "1111", "hello" }; std::cout << a1.to_string() << std::endl; - d2x_assert(a1.get_id() == "1111"); + d2x::check(a1.get_id() == "1111", "a1.get_id() == \"1111\""); Object::construction_counter = 0; Account a2 { "2222", "d2learn", 100 }; std::cout << a2.to_string() << std::endl; - d2x_assert(a2.get_object_name() == "d2learn"); - d2x_assert_eq(Object::construction_counter, 1); - - D2X_WAIT + d2x::check(a2.get_object_name() == "d2learn", "a2.get_object_name() == \"d2learn\""); + d2x::check_eq(Object::construction_counter, 1, "Object::construction_counter == 1"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/11-inherited-constructors-0.cpp b/en/cpp11/tests/11-inherited-constructors/0.cpp similarity index 92% rename from dslings/en/cpp11/11-inherited-constructors-0.cpp rename to en/cpp11/tests/11-inherited-constructors/0.cpp index 4daf5f1..3af5a91 100644 --- a/dslings/en/cpp11/11-inherited-constructors-0.cpp +++ b/en/cpp11/tests/11-inherited-constructors/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/11-inherited-constructors-0.cpp +// file: en/cpp11/tests/11-inherited-constructors/0.cpp // // Exercise: cpp11 | 11 - inherited constructors | Inherited Constructors // @@ -15,10 +15,9 @@ // d2x checker inherited-constructors // -#include +import std; +import d2x.harness; -#include -#include class ObjectBase { public: @@ -66,7 +65,6 @@ int main() { // Do not directly modify the code in the main function a1.tips_a(); b1.tips_b(); - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/11-inherited-constructors-1.cpp b/en/cpp11/tests/11-inherited-constructors/1.cpp similarity index 62% rename from dslings/en/cpp11/11-inherited-constructors-1.cpp rename to en/cpp11/tests/11-inherited-constructors/1.cpp index 95c63e1..ff96b9b 100644 --- a/dslings/en/cpp11/11-inherited-constructors-1.cpp +++ b/en/cpp11/tests/11-inherited-constructors/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/11-inherited-constructors-1.cpp +// file: en/cpp11/tests/11-inherited-constructors/1.cpp // // Exercise: cpp11 | 11 - inherited constructors | Inherited Constructors for Function Extension // @@ -15,10 +15,9 @@ // d2x checker inherited-constructors // -#include +import std; +import d2x.harness; -#include -#include class Student { // Do not directly modify the code in the Student class protected: @@ -62,35 +61,34 @@ int main() { // Do not directly modify the code in the main function { // Basic test StudentTest studentTest; - d2x_assert(studentTest.age_valid() == true); - d2x_assert(studentTest.score_valid() == true); - d2x_assert(studentTest.to_string() == "{001, 张三, 18, 0}"); + d2x::check(studentTest.age_valid() == true, "studentTest.age_valid() == true"); + d2x::check(studentTest.score_valid() == true, "studentTest.score_valid() == true"); + d2x::check(studentTest.to_string() == "{001, 张三, 18, 0}", "studentTest.to_string() == \"{001, 张三, 18, 0}\""); } { // Boundary test StudentTest studentTest("002", "张三", 201); - d2x_assert(studentTest.score_valid() == true); - d2x_assert(studentTest.age_valid() == false); + d2x::check(studentTest.score_valid() == true, "studentTest.score_valid() == true"); + d2x::check(studentTest.age_valid() == false, "studentTest.age_valid() == false"); studentTest.set_score(101); studentTest.set_age(200); - d2x_assert(studentTest.score_valid() == false); - d2x_assert(studentTest.age_valid() == true); + d2x::check(studentTest.score_valid() == false, "studentTest.score_valid() == false"); + d2x::check(studentTest.age_valid() == true, "studentTest.age_valid() == true"); studentTest.set_score(0); studentTest.set_age(1); - d2x_assert(studentTest.score_valid() == true); - d2x_assert(studentTest.age_valid() == true); + d2x::check(studentTest.score_valid() == true, "studentTest.score_valid() == true"); + d2x::check(studentTest.age_valid() == true, "studentTest.age_valid() == true"); studentTest.set_score(-1); studentTest.set_age(0); - d2x_assert(studentTest.score_valid() == false); - d2x_assert(studentTest.age_valid() == false); + d2x::check(studentTest.score_valid() == false, "studentTest.score_valid() == false"); + d2x::check(studentTest.age_valid() == false, "studentTest.age_valid() == false"); } - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/11-inherited-constructors-2.cpp b/en/cpp11/tests/11-inherited-constructors/2.cpp similarity index 80% rename from dslings/en/cpp11/11-inherited-constructors-2.cpp rename to en/cpp11/tests/11-inherited-constructors/2.cpp index 48133a9..21ba7cf 100644 --- a/dslings/en/cpp11/11-inherited-constructors-2.cpp +++ b/en/cpp11/tests/11-inherited-constructors/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/11-inherited-constructors-2.cpp +// file: en/cpp11/tests/11-inherited-constructors/2.cpp // // Exercise: cpp11 | 11 - inherited constructors | Inherited Constructors in Generic Decorator Applications // @@ -16,10 +16,9 @@ // d2x checker inherited-constructors // -#include +import std; +import d2x.harness; -#include -#include struct Point { double mX, mY; @@ -54,13 +53,13 @@ int main() { auto p11 = p1; std::cout << "p11: " << p11.to_string() << std::endl; - d2x_assert_eq(p1.mX, p11.mX); - d2x_assert_eq(p1.mY, p11.mY); + d2x::check_eq(p1.mX, p11.mX, "p1.mX == p11.mX"); + d2x::check_eq(p1.mY, p11.mY, "p1.mY == p11.mY"); decltype(p2) p22 = p2; // by std::move? std::cout << "p22: " << p22.to_string() << std::endl; - d2x_assert_eq(p2.mX, p22.mX); - d2x_assert_eq(p2.mY, p22.mY); + d2x::check_eq(p2.mX, p22.mX, "p2.mX == p22.mX"); + d2x::check_eq(p2.mY, p22.mY, "p2.mY == p22.mY"); NoMove p3(3, 3); std::cout << "p3: " << p3.to_string() << std::endl; @@ -68,10 +67,9 @@ int main() { NoMove p33(0, 0); p33 = std::move(p3); // by copy? std::cout << "p33: " << p33.to_string() << std::endl; - d2x_assert_eq(p3.mX, p33.mX); - d2x_assert_eq(p3.mY, p33.mY); - - D2X_WAIT + d2x::check_eq(p3.mX, p33.mX, "p3.mX == p33.mX"); + d2x::check_eq(p3.mY, p33.mY, "p3.mY == p33.mY"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/12-nullptr-0.cpp b/en/cpp11/tests/12-nullptr/0.cpp similarity index 70% rename from dslings/en/cpp11/12-nullptr-0.cpp rename to en/cpp11/tests/12-nullptr/0.cpp index 7cf0132..857868b 100644 --- a/dslings/en/cpp11/12-nullptr-0.cpp +++ b/en/cpp11/tests/12-nullptr/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/12-nullptr-0.cpp +// file: en/cpp11/tests/12-nullptr/0.cpp // // Exercise: cpp11 | 12 - nullptr | Pointer Literal - Basic Usage // @@ -17,9 +17,9 @@ // d2x checker nullptr // -#include +import std; +import d2x.harness; -#include int main() { @@ -28,13 +28,13 @@ int main() { int* ptr2 = NULL; // Fix here, add correct type int* ptr3 = 0; // Not recommended traditional usage - d2x_assert(ptr1 == nullptr); - d2x_assert(ptr2 == nullptr); - d2x_assert(ptr3 == nullptr); + d2x::check(ptr1 == nullptr, "ptr1 == nullptr"); + d2x::check(ptr2 == nullptr, "ptr2 == nullptr"); + d2x::check(ptr3 == nullptr, "ptr3 == nullptr"); // 2. Type of nullptr bool ok = std::is_same::value; - d2x_assert(ok); + d2x::check(ok, "ok"); // 3. Use nullptr for pointer comparison int value = 42; @@ -42,7 +42,7 @@ int main() { if (ptr4 != nullptr) { *ptr4 = D2X_YOUR_ANSWER; - d2x_assert_eq(*ptr4, 2233); + d2x::check_eq(*ptr4, 2233, "*ptr4 == 2233"); } // 4. Different types of pointers can all use nullptr @@ -50,11 +50,10 @@ int main() { char* cptr = nullptr void* vptr = nullptr; - d2x_assert(dptr == nullptr); - d2x_assert(cptr == nullptr); - d2x_assert(vptr == nullptr); - - D2X_WAIT + d2x::check(dptr == nullptr, "dptr == nullptr"); + d2x::check(cptr == nullptr, "cptr == nullptr"); + d2x::check(vptr == nullptr, "vptr == nullptr"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/12-nullptr-1.cpp b/en/cpp11/tests/12-nullptr/1.cpp similarity index 75% rename from dslings/en/cpp11/12-nullptr-1.cpp rename to en/cpp11/tests/12-nullptr/1.cpp index 274820e..ee3f4f2 100644 --- a/dslings/en/cpp11/12-nullptr-1.cpp +++ b/en/cpp11/tests/12-nullptr/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/12-nullptr-1.cpp +// file: en/cpp11/tests/12-nullptr/1.cpp // // Exercise: cpp11 | 12 - nullptr | Pointer Literal - Function Overloading // @@ -17,7 +17,8 @@ // d2x checker nullptr // -#include +import std; +import d2x.harness; bool process_int_called = false; bool process_ptr_called = false; @@ -47,12 +48,11 @@ int main() { display(NULL); process(NULL); - d2x_assert(process_int_called); - d2x_assert(display_int_called); - d2x_assert(display_ptr_called); - d2x_assert(display_ptr_called); - - D2X_WAIT + d2x::check(process_int_called, "process_int_called"); + d2x::check(display_int_called, "display_int_called"); + d2x::check(display_ptr_called, "display_ptr_called"); + d2x::check(display_ptr_called, "display_ptr_called"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/12-nullptr-2.cpp b/en/cpp11/tests/12-nullptr/2.cpp similarity index 92% rename from dslings/en/cpp11/12-nullptr-2.cpp rename to en/cpp11/tests/12-nullptr/2.cpp index 05750bf..4c4b950 100644 --- a/dslings/en/cpp11/12-nullptr-2.cpp +++ b/en/cpp11/tests/12-nullptr/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/12-nullptr-2.cpp +// file: en/cpp11/tests/12-nullptr/2.cpp // // Exercise: cpp11 | 12 - nullptr | Pointer Literal - Template Programming // @@ -17,8 +17,8 @@ // d2x checker nullptr // -#include -#include +import std; +import d2x.harness; // Template function example template @@ -44,7 +44,6 @@ int main() { processPointer(clone(nullptr)); processPointer(clone(nullptr)); - D2X_WAIT - + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/13-long-long-0.cpp b/en/cpp11/tests/13-long-long/0.cpp similarity index 77% rename from dslings/en/cpp11/13-long-long-0.cpp rename to en/cpp11/tests/13-long-long/0.cpp index b433fc2..921a6a4 100644 --- a/dslings/en/cpp11/13-long-long-0.cpp +++ b/en/cpp11/tests/13-long-long/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/13-long-long-0.cpp +// file: en/cpp11/tests/13-long-long/0.cpp // // Exercise: cpp11 | 13 - long long | 64-bit Integer Type - Basic Usage // @@ -17,9 +17,9 @@ // d2x checker long-long // -#include +import std; +import d2x.harness; -#include int main() { @@ -29,7 +29,7 @@ int main() { // 2. Integer representation range unsigned int uVal1 = 18446744073709551615; - d2x_assert_eq(uVal1, 18446744073709551615ULL); + d2x::check_eq(uVal1, 18446744073709551615ULL, "uVal1 == 18446744073709551615ULL"); // 3. Type deduction and literals // Fix the declarations below to let auto correctly deduce the types @@ -39,10 +39,9 @@ int main() { bool is_longlong = std::is_same::value; bool is_ulonglong = std::is_same::value; - d2x_assert(is_longlong == true); - d2x_assert(is_ulonglong == true); - - D2X_WAIT + d2x::check(is_longlong == true, "is_longlong == true"); + d2x::check(is_ulonglong == true, "is_ulonglong == true"); + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/13-long-long-1.cpp b/en/cpp11/tests/13-long-long/1.cpp similarity index 65% rename from dslings/en/cpp11/13-long-long-1.cpp rename to en/cpp11/tests/13-long-long/1.cpp index 8bf569a..6286f62 100644 --- a/dslings/en/cpp11/13-long-long-1.cpp +++ b/en/cpp11/tests/13-long-long/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/13-long-long-1.cpp +// file: en/cpp11/tests/13-long-long/1.cpp // // Exercise: cpp11 | 13 - long long | 64-bit Integer Type - Large Number Applications and Boundary Values // @@ -17,9 +17,9 @@ // d2x checker long-long // -#include +import std; +import d2x.harness; -#include int main() { @@ -29,16 +29,15 @@ int main() { auto minLL = std::numeric_limits::min(); auto maxULL = std::numeric_limits::max(); - d2x_assert_eq(maxInt, 2147483647); - d2x_assert_eq(maxLL, 9223372036854775807LL); - d2x_assert_eq(minLL, -9223372036854775807LL - 1); - d2x_assert_eq(maxULL, 18446744073709551615ULL); + d2x::check_eq(maxInt, 2147483647, "maxInt == 2147483647"); + d2x::check_eq(maxLL, 9223372036854775807LL, "maxLL == 9223372036854775807LL"); + d2x::check_eq(minLL, -9223372036854775807LL - 1, "minLL == -9223372036854775807LL - 1"); + d2x::check_eq(maxULL, 18446744073709551615ULL, "maxULL == 18446744073709551615ULL"); // 2. Large integer application - representing world population int currentPopulation = 7800000000; - d2x_assert_eq(currentPopulation, 7800000000ULL); - - D2X_WAIT + d2x::check_eq(currentPopulation, 7800000000ULL, "currentPopulation == 7800000000ULL"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/14-type-alias-0.cpp b/en/cpp11/tests/14-type-alias/0.cpp similarity index 72% rename from dslings/en/cpp11/14-type-alias-0.cpp rename to en/cpp11/tests/14-type-alias/0.cpp index a011fb4..9d9e27f 100644 --- a/dslings/en/cpp11/14-type-alias-0.cpp +++ b/en/cpp11/tests/14-type-alias/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/14-type-alias-0.cpp +// file: en/cpp11/tests/14-type-alias/0.cpp // // Exercise: cpp11 | 14 - type alias | Basic Type Aliases // @@ -17,9 +17,9 @@ // d2x checker type-alias // -#include +import std; +import d2x.harness; -#include int main() { @@ -27,24 +27,23 @@ int main() { D2X_YOUR_ANSWER Integer = int; D2X_YOUR_ANSWER = int; - bool ok = std::is_same::value; d2x_assert(ok); - ok = std::is_same::value; d2x_assert(ok); + bool ok = std::is_same::value; d2x::check(ok, "ok"); + ok = std::is_same::value; d2x::check(ok, "ok"); // 2. Using type aliases Integer a = 42; Real b = 3.14; // 3. Verifying type aliases - d2x_assert_eq(a, 42); - d2x_assert_eq(b, 3.14); + d2x::check_eq(a, 42, "a == 42"); + d2x::check_eq(b, 3.14, "b == 3.14"); // 4. Type aliases are essentially the same int c = 100; Integer d = c; // Can assign because they are essentially both int - d2x_assert_eq(c, d); - - D2X_WAIT + d2x::check_eq(c, d, "c == d"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/14-type-alias-1.cpp b/en/cpp11/tests/14-type-alias/1.cpp similarity index 78% rename from dslings/en/cpp11/14-type-alias-1.cpp rename to en/cpp11/tests/14-type-alias/1.cpp index 9578d00..aa4a4b8 100644 --- a/dslings/en/cpp11/14-type-alias-1.cpp +++ b/en/cpp11/tests/14-type-alias/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/14-type-alias-1.cpp +// file: en/cpp11/tests/14-type-alias/1.cpp // // Exercise: cpp11 | 14 - type alias | Complex Types and Function Pointer Aliases // @@ -17,10 +17,9 @@ // d2x checker type-alias // -#include +import std; +import d2x.harness; -#include -#include static int func_called = 0; @@ -37,7 +36,7 @@ int main() { FuncPtr func = example_func; func(1, 2); - d2x_assert_eq(func_called, 3); + d2x::check_eq(func_called, 3, "func_called == 3"); // 2. Container type alias // Use using to define an alias for vector @@ -54,11 +53,10 @@ int main() { Container::ValueType value = 100; // 4. Verifying type aliases - d2x_assert(strings[0] == "hello"); - d2x_assert(strings[1] == "world"); - d2x_assert_eq(value, 100); - - D2X_WAIT + d2x::check(strings[0] == "hello", "strings[0] == \"hello\""); + d2x::check(strings[1] == "world", "strings[1] == \"world\""); + d2x::check_eq(value, 100, "value == 100"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/14-type-alias-2.cpp b/en/cpp11/tests/14-type-alias/2.cpp similarity index 70% rename from dslings/en/cpp11/14-type-alias-2.cpp rename to en/cpp11/tests/14-type-alias/2.cpp index 4226637..b45b155 100644 --- a/dslings/en/cpp11/14-type-alias-2.cpp +++ b/en/cpp11/tests/14-type-alias/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/14-type-alias-2.cpp +// file: en/cpp11/tests/14-type-alias/2.cpp // // Exercise: cpp11 | 14 - type alias | Alias Templates Basics // @@ -17,11 +17,9 @@ // d2x checker type-alias // -#include +import std; +import d2x.harness; -#include -#include -#include // 1. Basic alias template template @@ -41,15 +39,14 @@ int main() { Vec3 v3 = {1.0f, 2.0f, 3.0f}; Heap minHeap; - d2x_assert_eq(numbers[0], 1); - d2x_assert_eq(numbers[1], 2); - d2x_assert_eq(numbers[2], 3); + d2x::check_eq(numbers[0], 1, "numbers[0] == 1"); + d2x::check_eq(numbers[1], 2, "numbers[1] == 2"); + d2x::check_eq(numbers[2], 3, "numbers[2] == 3"); - d2x_assert_eq(v3[0], 1.0f); - d2x_assert_eq(v3[1], 2.0f); - d2x_assert_eq(v3[2], 3.0f); - - D2X_WAIT + d2x::check_eq(v3[0], 1.0f, "v3[0] == 1.0f"); + d2x::check_eq(v3[1], 2.0f, "v3[1] == 2.0f"); + d2x::check_eq(v3[2], 3.0f, "v3[2] == 3.0f"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/14-type-alias-3.cpp b/en/cpp11/tests/14-type-alias/3.cpp similarity index 82% rename from dslings/en/cpp11/14-type-alias-3.cpp rename to en/cpp11/tests/14-type-alias/3.cpp index 1d208ad..0ff4aae 100644 --- a/dslings/en/cpp11/14-type-alias-3.cpp +++ b/en/cpp11/tests/14-type-alias/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/14-type-alias-3.cpp +// file: en/cpp11/tests/14-type-alias/3.cpp // // Exercise: cpp11 | 14 - type alias | Standard Library Style Alias Templates // @@ -17,8 +17,8 @@ // d2x checker type-alias // -#include -#include +import std; +import d2x.harness; int main() { @@ -30,10 +30,9 @@ int main() { bool ok = std::is_same, int*>::value; - d2x_assert(ok); - d2x_assert_eq(*ptr, 20); - - D2X_WAIT + d2x::check(ok, "ok"); + d2x::check_eq(*ptr, 20, "*ptr == 20"); + d2x::wait(); return 0; } \ No newline at end of file diff --git a/dslings/en/cpp11/15-variadic-templates-0.cpp b/en/cpp11/tests/15-variadic-templates/0.cpp similarity index 86% rename from dslings/en/cpp11/15-variadic-templates-0.cpp rename to en/cpp11/tests/15-variadic-templates/0.cpp index 25a8339..73638c0 100644 --- a/dslings/en/cpp11/15-variadic-templates-0.cpp +++ b/en/cpp11/tests/15-variadic-templates/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/15-variadic-templates-0.cpp +// file: en/cpp11/tests/15-variadic-templates/0.cpp // // Exercise: cpp11 | 15 - variadic templates | Variadic templates basics // @@ -20,8 +20,8 @@ // d2x checker variadic-templates // -#include -#include +import std; +import d2x.harness; std::stringstream ss; @@ -41,9 +41,8 @@ int main() { print(1, "hello", 3.14); std::string result = ss.str(); - d2x_assert(result == "1 hello 3.14 "); - - D2X_WAIT + d2x::check(result == "1 hello 3.14 ", "result == \"1 hello 3.14 \""); + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/15-variadic-templates-1.cpp b/en/cpp11/tests/15-variadic-templates/1.cpp similarity index 77% rename from dslings/en/cpp11/15-variadic-templates-1.cpp rename to en/cpp11/tests/15-variadic-templates/1.cpp index 1eaa173..1221f74 100644 --- a/dslings/en/cpp11/15-variadic-templates-1.cpp +++ b/en/cpp11/tests/15-variadic-templates/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/cpp11/15-variadic-templates-1.cpp +// file: en/cpp11/tests/15-variadic-templates/1.cpp // // Exercise: cpp11 | 15 - variadic templates | Variadic template sum // @@ -13,7 +13,8 @@ // - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/15-variadic-templates.md // -#include +import std; +import d2x.harness; D2X_YOUR_ANSWER @@ -24,17 +25,16 @@ T sum(T first, D2X_YOUR_ANSWER) { int main() { int res1 = sum(1, 2, 3, 4, 5); - d2x_assert_eq(res1, 15); + d2x::check_eq(res1, 15, "res1 == 15"); double res2 = sum(1.5, 2.5, 3.0); - d2x_assert(res2 == 7.0); + d2x::check(res2 == 7.0, "res2 == 7.0"); // Mixed types // Note: the return type is determined by the first argument T int res3 = sum(10, 20.5); - d2x_assert_eq(res3, 30); - - D2X_WAIT + d2x::check_eq(res3, 30, "res3 == 30"); + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/16-generalized-unions-0.cpp b/en/cpp11/tests/16-generalized-unions/0.cpp similarity index 84% rename from dslings/en/cpp11/16-generalized-unions-0.cpp rename to en/cpp11/tests/16-generalized-unions/0.cpp index 3c1db5c..e9a4c7a 100644 --- a/dslings/en/cpp11/16-generalized-unions-0.cpp +++ b/en/cpp11/tests/16-generalized-unions/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/16-generalized-unions-0.cpp +// file: en/cpp11/tests/16-generalized-unions/0.cpp // // Exercise: cpp11 | 16 - generalized unions | generalized (non-trivial) unions // @@ -18,7 +18,8 @@ // d2x checker generalized-unions // -#include +import std; +import d2x.harness; union M @@ -34,7 +35,7 @@ int main() { M u1; //Initialize union members - d2x_assert(u1.a1 == 42); + d2x::check(u1.a1 == 42, "u1.a1 == 42"); u1.a2 = 21; double val = 3.14; @@ -42,7 +43,6 @@ int main() { u1.c = 'x'; - D2X_WAIT - + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/16-generalized-unions-1.cpp b/en/cpp11/tests/16-generalized-unions/1.cpp similarity index 85% rename from dslings/en/cpp11/16-generalized-unions-1.cpp rename to en/cpp11/tests/16-generalized-unions/1.cpp index 8a18f76..2e830b0 100644 --- a/dslings/en/cpp11/16-generalized-unions-1.cpp +++ b/en/cpp11/tests/16-generalized-unions/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/16-generalized-unions-1.cpp +// file: en/cpp11/tests/16-generalized-unions/1.cpp // // Exercise: cpp11 | 16 - generalized unions | generalized (non-trivial) unions // @@ -18,8 +18,8 @@ // d2x checker generalized-unions // -#include -#include +import std; +import d2x.harness; union M { @@ -43,12 +43,11 @@ int main() { u1.a2 = {1, D2X_YOUR_ANSWER, 3}; // 2. use assert to verify the contents - d2x_assert_eq(u1.a2[1], 42); + d2x::check_eq(u1.a2[1], 42, "u1.a2[1] == 42"); // 3. Manually call the destructor u1.a2.~vector(); - D2X_WAIT - + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/16-generalized-unions-2.cpp b/en/cpp11/tests/16-generalized-unions/2.cpp similarity index 81% rename from dslings/en/cpp11/16-generalized-unions-2.cpp rename to en/cpp11/tests/16-generalized-unions/2.cpp index 5cd28dc..c818da5 100644 --- a/dslings/en/cpp11/16-generalized-unions-2.cpp +++ b/en/cpp11/tests/16-generalized-unions/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/16-generalized-unions-2.cpp +// file: en/cpp11/tests/16-generalized-unions/2.cpp // // Exercise: cpp11 | 16 - generalized unions | tagged discriminated union // @@ -20,9 +20,8 @@ // d2x checker generalized-unions // -#include -#include -#include +import std; +import d2x.harness; // Tag type — marks which member of the union is active enum class Tag { @@ -77,29 +76,28 @@ int main() { // 1. Construct an int value and verify Value v1(42); - d2x_assert(v1.tag == Tag::INTEGER); - d2x_assert_eq(v1.as_int(), 42); + d2x::check(v1.tag == Tag::INTEGER, "v1.tag == Tag::INTEGER"); + d2x::check_eq(v1.as_int(), 42, "v1.as_int() == 42"); // 2. Construct a string value and verify Value v2(std::string("hello")); - d2x_assert(v2.tag == Tag::STRING); - d2x_assert(v2.as_string() == "hello"); + d2x::check(v2.tag == Tag::STRING, "v2.tag == Tag::STRING"); + d2x::check(v2.as_string() == "hello", "v2.as_string() == \"hello\""); // 3. Switch from string to int { Value v3(std::string("world")); - d2x_assert(v3.as_string() == "world"); + d2x::check(v3.as_string() == "world", "v3.as_string() == \"world\""); // Manually destroy the string member, then switch to int v3.data.s.~basic_string(); v3.tag = Tag::INTEGER; v3.data.i = 100; - d2x_assert_eq(v3.as_int(), D2X_YOUR_ANSWER); + d2x::check_eq(v3.as_int(), D2X_YOUR_ANSWER, "v3.as_int() == D2X_YOUR_ANSWER"); // When v3 goes out of scope, ~Value() sees tag == INTEGER, skips string destruction } - D2X_WAIT - + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/17-pod-type-0.cpp b/en/cpp11/tests/17-pod-type/0.cpp similarity index 87% rename from dslings/en/cpp11/17-pod-type-0.cpp rename to en/cpp11/tests/17-pod-type/0.cpp index 970992d..d1eec66 100644 --- a/dslings/en/cpp11/17-pod-type-0.cpp +++ b/en/cpp11/tests/17-pod-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/17-pod-type-0.cpp +// file: en/cpp11/tests/17-pod-type/0.cpp // // Exercise: cpp11 | 17 - POD Type | Basic type properties // @@ -18,9 +18,9 @@ // // d2x checker pod-type -#include +import std; +import d2x.harness; -#include struct A { int x; @@ -53,21 +53,21 @@ int main() { // 3. Check if A is trivial / standard_layout type, write the result to ok bool ok = D2X_YOUR_ANSWER; - d2x_assert(ok); + d2x::check(ok, "ok"); // 4. Check if B is trivial / standard_layout type, write the result to ok ok = D2X_YOUR_ANSWER; - d2x_assert(ok); + d2x::check(ok, "ok"); // 5. Check if C is trivial type, write the result to ok ok = D2X_YOUR_ANSWER; - d2x_assert(ok); + d2x::check(ok, "ok"); // 6. Check if D is not standard_layout type, write the result to ok ok = D2X_YOUR_ANSWER; - d2x_assert(!ok); + d2x::check(!ok, "!ok"); - D2X_WAIT + d2x::wait(); } diff --git a/dslings/en/cpp11/17-pod-type-1.cpp b/en/cpp11/tests/17-pod-type/1.cpp similarity index 72% rename from dslings/en/cpp11/17-pod-type-1.cpp rename to en/cpp11/tests/17-pod-type/1.cpp index 074f27f..004386b 100644 --- a/dslings/en/cpp11/17-pod-type-1.cpp +++ b/en/cpp11/tests/17-pod-type/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/17-pod-type-1.cpp +// file: en/cpp11/tests/17-pod-type/1.cpp // // Exercise: cpp11 | 17 - POD Type | Byte-wise copying of a POD struct // @@ -18,10 +18,9 @@ // // d2x checker pod-type -#include +import std; +import d2x.harness; -#include -#include struct Packet { std::uint32_t len; @@ -40,12 +39,11 @@ int main() { // TODO: use std::memcpy to copy the contents of p1 into p2. D2X_YOUR_ANSWER; - d2x_assert_eq(p2.len, 42u); - d2x_assert_eq(p2.type, static_cast(1)); - d2x_assert_eq(p2.flags, static_cast(0xFF)); - - D2X_WAIT + d2x::check_eq(p2.len, 42u, "p2.len == 42u"); + d2x::check_eq(p2.type, static_cast(1), "p2.type == static_cast(1)"); + d2x::check_eq(p2.flags, static_cast(0xFF), "p2.flags == static_cast(0xFF)"); + d2x::wait(); return 0; } diff --git a/dslings/en/cpp11/17-pod-type-2.cpp b/en/cpp11/tests/17-pod-type/2.cpp similarity index 77% rename from dslings/en/cpp11/17-pod-type-2.cpp rename to en/cpp11/tests/17-pod-type/2.cpp index ce31790..294b83c 100644 --- a/dslings/en/cpp11/17-pod-type-2.cpp +++ b/en/cpp11/tests/17-pod-type/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp11/17-pod-type-2.cpp +// file: en/cpp11/tests/17-pod-type/2.cpp // // Exercise: cpp11 | 17 - POD Type | Adapting a C++ message to a C POD header // @@ -18,10 +18,9 @@ // // d2x checker pod-type -#include +import std; +import d2x.harness; -#include -#include // C-style message header, POD-only, used for cross-language / cross-module transfer. struct MessageHeader { @@ -41,7 +40,7 @@ static MessageHeader g_last_header{}; extern "C" void c_send_header(const void* data, std::size_t size) { // In a real-world scenario, this might be a C library or system call. - d2x_assert_eq(size, sizeof(MessageHeader)); + d2x::check_eq(size, sizeof(MessageHeader), "size == sizeof(MessageHeader)"); const auto* header = static_cast(data); g_last_header = *header; } @@ -62,12 +61,11 @@ int main() { send_message_to_c(msg); // Verify that the C interface received the correct header contents. - d2x_assert_eq(g_last_header.type, msg.type); - d2x_assert_eq(g_last_header.len, static_cast(msg.payload.size())); - d2x_assert_eq(g_last_header.flags, static_cast(0)); - - D2X_WAIT + d2x::check_eq(g_last_header.type, msg.type, "g_last_header.type == msg.type"); + d2x::check_eq(g_last_header.len, static_cast(msg.payload.size()), "g_last_header.len == static_cast(msg.payload.size())"); + d2x::check_eq(g_last_header.flags, static_cast(0), "g_last_header.flags == static_cast(0)"); + d2x::wait(); return 0; } diff --git a/en/cpp14/mcpp.toml b/en/cpp14/mcpp.toml new file mode 100644 index 0000000..ff4f10e --- /dev/null +++ b/en/cpp14/mcpp.toml @@ -0,0 +1,10 @@ +# cpp14 (en) 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: +# mcpp test -p en/cpp14 全部练习(进度表) +# mcpp test -p en/cpp14 <子串> 只跑匹配的练习 +[package] +name = "en-cpp14" +version = "0.1.0" +standard = "c++23" + +[dependencies] +harness = { path = "../../harness" } diff --git a/dslings/en/cpp14/00-generic-lambdas-0.cpp b/en/cpp14/tests/00-generic-lambdas/0.cpp similarity index 57% rename from dslings/en/cpp14/00-generic-lambdas-0.cpp rename to en/cpp14/tests/00-generic-lambdas/0.cpp index 3d2c16e..a740c88 100644 --- a/dslings/en/cpp14/00-generic-lambdas-0.cpp +++ b/en/cpp14/tests/00-generic-lambdas/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp14/00-generic-lambdas-0.cpp +// file: en/cpp14/tests/00-generic-lambdas/0.cpp // // Exercise: cpp14 | 00 - generic lambdas | basic generic lambda // @@ -20,8 +20,8 @@ // d2x checker generic-lambdas // -#include -#include +import std; +import d2x.harness; int main() { @@ -30,28 +30,27 @@ int main() { return x; }; - d2x_assert_eq(identity(42), 42); - d2x_assert(identity(std::string("hello")) == "hello"); - d2x_assert_eq(identity(3.14), D2X_YOUR_ANSWER); + d2x::check_eq(identity(42), 42, "identity(42) == 42"); + d2x::check(identity(std::string("hello")) == "hello", "identity(std::string(\"hello\")) == \"hello\""); + d2x::check_eq(identity(3.14), D2X_YOUR_ANSWER, "identity(3.14) == D2X_YOUR_ANSWER"); // 1. Generic lambda as comparator auto greater = [](auto a, auto b) { return D2X_YOUR_ANSWER; }; - d2x_assert(greater(5, 3)); - d2x_assert(greater(2.5, 1.2)); - d2x_assert(greater(std::string("z"), std::string("a"))); + d2x::check(greater(5, 3), "greater(5, 3)"); + d2x::check(greater(2.5, 1.2), "greater(2.5, 1.2)"); + d2x::check(greater(std::string("z"), std::string("a")), "greater(std::string(\"z\"), std::string(\"a\"))"); // 2. Verify deduced types auto get_type_size = [](auto x) { return sizeof(D2X_YOUR_ANSWER); }; - d2x_assert_eq(get_type_size(42), sizeof(int)); - d2x_assert_eq(get_type_size('c'), sizeof(char)); - - D2X_WAIT + d2x::check_eq(get_type_size(42), sizeof(int), "get_type_size(42) == sizeof(int)"); + d2x::check_eq(get_type_size('c'), sizeof(char), "get_type_size('c') == sizeof(char)"); + d2x::wait(); return 0; } diff --git a/dslings/en/cpp14/00-generic-lambdas-1.cpp b/en/cpp14/tests/00-generic-lambdas/1.cpp similarity index 75% rename from dslings/en/cpp14/00-generic-lambdas-1.cpp rename to en/cpp14/tests/00-generic-lambdas/1.cpp index d23df54..88cf159 100644 --- a/dslings/en/cpp14/00-generic-lambdas-1.cpp +++ b/en/cpp14/tests/00-generic-lambdas/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/en/cpp14/00-generic-lambdas-1.cpp +// file: en/cpp14/tests/00-generic-lambdas/1.cpp // // Exercise: cpp14 | 00 - generic lambdas | generic lambda with STL algorithms // @@ -20,9 +20,8 @@ // d2x checker generic-lambdas // -#include -#include -#include +import std; +import d2x.harness; int main() { @@ -35,11 +34,11 @@ int main() { }; std::sort(v1.begin(), v1.end(), desc); - d2x_assert_eq(v1[0], 8); - d2x_assert_eq(v1[4], 1); + d2x::check_eq(v1[0], 8, "v1[0] == 8"); + d2x::check_eq(v1[4], 1, "v1[4] == 1"); std::sort(v2.begin(), v2.end(), desc); - d2x_assert_eq(v2[0], D2X_YOUR_ANSWER); + d2x::check_eq(v2[0], D2X_YOUR_ANSWER, "v2[0] == D2X_YOUR_ANSWER"); // 1. Generic lambda with capture — find_if int threshold = 3; @@ -48,10 +47,10 @@ int main() { }; auto it1 = std::find_if(v1.begin(), v1.end(), above); - d2x_assert(*it1 == D2X_YOUR_ANSWER); + d2x::check(*it1 == D2X_YOUR_ANSWER, "*it1 == D2X_YOUR_ANSWER"); auto it2 = std::find_if(v2.begin(), v2.end(), above); - d2x_assert(*it2 == 8.5); + d2x::check(*it2 == 8.5, "*it2 == 8.5"); // 2. Generic lambda returning lambda — factory function auto make_multiplier = [](auto factor) { @@ -59,10 +58,9 @@ int main() { }; auto times2 = make_multiplier(2); - d2x_assert_eq(times2(10), 20); - d2x_assert_eq(times2(0.5), 1.0); - - D2X_WAIT + d2x::check_eq(times2(10), 20, "times2(10) == 20"); + d2x::check_eq(times2(0.5), 1.0, "times2(0.5) == 1.0"); + d2x::wait(); return 0; } diff --git a/en/intro/mcpp.toml b/en/intro/mcpp.toml new file mode 100644 index 0000000..324bd6b --- /dev/null +++ b/en/intro/mcpp.toml @@ -0,0 +1,10 @@ +# intro (en) 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: +# mcpp test -p en/intro 全部练习(进度表) +# mcpp test -p en/intro <子串> 只跑匹配的练习 +[package] +name = "en-intro" +version = "0.1.0" +standard = "c++23" + +[dependencies] +harness = { path = "../../harness" } diff --git a/dslings/en/hello-mcpp.cpp b/en/intro/tests/hello-mcpp.cpp similarity index 90% rename from dslings/en/hello-mcpp.cpp rename to en/intro/tests/hello-mcpp.cpp index f9f33b6..e7767e2 100644 --- a/dslings/en/hello-mcpp.cpp +++ b/en/intro/tests/hello-mcpp.cpp @@ -1,6 +1,6 @@ // mcpp-standard: https://github.com/Sunrisepeak/mcpp-standard // license: Apache-2.0 -// file: dslings/hello-mcpp.cpp +// file: en/intro/tests/hello-mcpp.cpp // // Exercise: Automated Code Practice Usage Tutorial // @@ -28,7 +28,8 @@ // d2x checker hello-mcpp // -#include +import std; +import d2x.harness; // You can observe "real-time" changes in the console when modifying code @@ -38,11 +39,11 @@ int main() { int a = 1.1; // 1. Fix this runtime error, change int to double to pass the check - d2x_assert_eq(a, 1.1); // 2. Runtime checkpoint, need to fix code to pass all checkpoints (cannot directly delete checkpoint code) + d2x::check_eq(a, 1.1, "a == 1.1"); // 2. Runtime checkpoint, need to fix code to pass all checkpoints (cannot directly delete checkpoint code) D2X_YOUR_ANSWER b = a; // 3. Fix this compilation error, give b an appropriate type - d2x_assert_eq(b, 1); // 4. Runtime checkpoint 2 + d2x::check_eq(b, 1, "b == 1"); // 4. Runtime checkpoint 2 D2X_WAIT // 5. Delete or comment out this macro to proceed to the next exercise (project formal code practice) diff --git a/intro/mcpp.toml b/intro/mcpp.toml new file mode 100644 index 0000000..8065e72 --- /dev/null +++ b/intro/mcpp.toml @@ -0,0 +1,10 @@ +# intro 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: +# mcpp test -p intro 全部练习(进度表) +# mcpp test -p intro <子串> 只跑匹配的练习 +[package] +name = "intro" +version = "0.1.0" +standard = "c++23" + +[dependencies] +harness = { path = "../harness" } diff --git a/dslings/hello-mcpp.cpp b/intro/tests/hello-mcpp.cpp similarity index 90% rename from dslings/hello-mcpp.cpp rename to intro/tests/hello-mcpp.cpp index 9cf4da3..98321e6 100644 --- a/dslings/hello-mcpp.cpp +++ b/intro/tests/hello-mcpp.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/hello-mcpp.cpp +// file: intro/tests/hello-mcpp.cpp // // Exercise/练习: 自动化代码练习使用教学 // @@ -23,7 +23,8 @@ // d2x checker hello-mcpp // -#include +import std; +import d2x.harness; // 修改代码时可以观察到控制台"实时"的变化 @@ -33,11 +34,11 @@ int main() { int a = 1.1; // 1.修复这个运行时错误, 修改int为double, 通过检查 - d2x_assert_eq(a, 1.1); // 2.运行时检查点, 需要修复代码通过所有检查点(不能直接删除检查点代码) + d2x::check_eq(a, 1.1, "a == 1.1"); // 2.运行时检查点, 需要修复代码通过所有检查点(不能直接删除检查点代码) D2X_YOUR_ANSWER b = a; // 3.修复这个编译错误, 给b一个合适的类型 - d2x_assert_eq(b, 1); // 4.运行时检查点2 + d2x::check_eq(b, 1, "b == 1"); // 4.运行时检查点2 D2X_WAIT // 5.删除或注释掉这个宏, 进入下一个练习(项目正式代码练习) diff --git a/mcpp.toml b/mcpp.toml index 5b9a12b..a90e86e 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,4 +1,8 @@ # d2mcpp 根 workspace。练习即测试:每个 C++ 标准一个真实工程, # 练习是它的 tests/,mcpp test 直接就是进度表;d2x checker 驱动同一条链。 [workspace] -members = ["harness", "cpp14", "d2x/buildtools/mcpp", "dslings/harness"] +members = [ + "harness", "intro", "cpp11", "cpp14", + "en/intro", "en/cpp11", "en/cpp14", + "d2x/buildtools/mcpp", +] diff --git a/solutions/cpp11/00-auto-and-decltype-0.cpp b/solutions/cpp11/00-auto-and-decltype/0.cpp similarity index 70% rename from solutions/cpp11/00-auto-and-decltype-0.cpp rename to solutions/cpp11/00-auto-and-decltype/0.cpp index 43ab73d..49ab0a1 100644 --- a/solutions/cpp11/00-auto-and-decltype-0.cpp +++ b/solutions/cpp11/00-auto-and-decltype/0.cpp @@ -6,7 +6,8 @@ // 教程练习入口: dslings/cpp11/00-auto-and-decltype-0.cpp // -#include +import std; +import d2x.harness; int main() { @@ -23,12 +24,12 @@ int main() { auto c1 = c; // c1: char decltype(c) c2 = c; // c2: char - d2x_assert_eq(a, a1); - d2x_assert_eq(a1, a2); - d2x_assert_eq(b, b1); - d2x_assert_eq(b1, b2); - d2x_assert_eq(c, c1); - d2x_assert_eq(c1, c2); + d2x::check_eq(a, a1, "a == a1"); + d2x::check_eq(a1, a2, "a1 == a2"); + d2x::check_eq(b, b1, "b == b1"); + d2x::check_eq(b1, b2, "b1 == b2"); + d2x::check_eq(c, c1, "c == c1"); + d2x::check_eq(c1, c2, "c1 == c2"); return 0; } diff --git a/solutions/cpp11/00-auto-and-decltype-1.cpp b/solutions/cpp11/00-auto-and-decltype/1.cpp similarity index 73% rename from solutions/cpp11/00-auto-and-decltype-1.cpp rename to solutions/cpp11/00-auto-and-decltype/1.cpp index f301144..85c7bda 100644 --- a/solutions/cpp11/00-auto-and-decltype-1.cpp +++ b/solutions/cpp11/00-auto-and-decltype/1.cpp @@ -6,7 +6,8 @@ // 教程练习入口: dslings/cpp11/00-auto-and-decltype-1.cpp // -#include +import std; +import d2x.harness; int main() { @@ -24,10 +25,10 @@ int main() { auto c1 = 1 + c; // int decltype(2 + 'a') c2 = 2 + 'a'; - d2x_assert_eq(a2, a + 2 + 1.1); - d2x_assert_eq(b1, a + 0.1); - d2x_assert_eq(c1, 1 + c); - d2x_assert_eq(c2, 2 + 'a'); + d2x::check_eq(a2, a + 2 + 1.1, "a2 == a + 2 + 1.1"); + d2x::check_eq(b1, a + 0.1, "b1 == a + 0.1"); + d2x::check_eq(c1, 1 + c, "c1 == 1 + c"); + d2x::check_eq(c2, 2 + 'a', "c2 == 2 + 'a'"); return 0; } diff --git a/solutions/cpp11/00-auto-and-decltype-2.cpp b/solutions/cpp11/00-auto-and-decltype/2.cpp similarity index 83% rename from solutions/cpp11/00-auto-and-decltype-2.cpp rename to solutions/cpp11/00-auto-and-decltype/2.cpp index e3d7c5a..9e86b6a 100644 --- a/solutions/cpp11/00-auto-and-decltype-2.cpp +++ b/solutions/cpp11/00-auto-and-decltype/2.cpp @@ -6,11 +6,9 @@ // 教程练习入口: dslings/cpp11/00-auto-and-decltype-2.cpp // -#include +import std; +import d2x.harness; -#include -#include -#include int add_func(int a, int b) { return a + b; @@ -41,8 +39,8 @@ int main() { minus_func }; - d2x_assert_eq(funcVec[0](1, 2), 3); - d2x_assert_eq(funcVec[1](1, 2), -1); + d2x::check_eq(funcVec[0](1, 2), 3, "funcVec[0](1, 2) == 3"); + d2x::check_eq(funcVec[1](1, 2), -1, "funcVec[1](1, 2) == -1"); return 0; } diff --git a/solutions/cpp11/00-auto-and-decltype-3.cpp b/solutions/cpp11/00-auto-and-decltype/3.cpp similarity index 68% rename from solutions/cpp11/00-auto-and-decltype-3.cpp rename to solutions/cpp11/00-auto-and-decltype/3.cpp index 47c8b2e..2cbae82 100644 --- a/solutions/cpp11/00-auto-and-decltype-3.cpp +++ b/solutions/cpp11/00-auto-and-decltype/3.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/00-auto-and-decltype-3.cpp // -#include +import std; +import d2x.harness; -#include -#include // 3. 函数返回值类型 @@ -24,9 +23,9 @@ auto minus_func(T1 a, T2 b) -> decltype(a - b) { int main() { - d2x_assert_eq(minus_func(1, 2), -1); - d2x_assert_eq(minus_func(2, 1), 1); - d2x_assert_eq(minus_func(1, 2.1), -1.1); + d2x::check_eq(minus_func(1, 2), -1, "minus_func(1, 2) == -1"); + d2x::check_eq(minus_func(2, 1), 1, "minus_func(2, 1) == 1"); + d2x::check_eq(minus_func(1, 2.1), -1.1, "minus_func(1, 2.1) == -1.1"); return 0; } diff --git a/solutions/cpp11/00-auto-and-decltype-4.cpp b/solutions/cpp11/00-auto-and-decltype/4.cpp similarity index 65% rename from solutions/cpp11/00-auto-and-decltype-4.cpp rename to solutions/cpp11/00-auto-and-decltype/4.cpp index caddb29..cbd2a5a 100644 --- a/solutions/cpp11/00-auto-and-decltype-4.cpp +++ b/solutions/cpp11/00-auto-and-decltype/4.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/00-auto-and-decltype-4.cpp // -#include +import std; +import d2x.harness; -#include // 4. 类/结构体成员类型推导 @@ -26,21 +26,21 @@ int main() { // obj的类型推导 和 (obj) 的类型推导 type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // obj.a的类型推导 和 (obj.a) 的类型推导 type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // obj.b的类型推导 和 (obj.b) 的类型推导 type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line return 0; } diff --git a/solutions/cpp11/00-auto-and-decltype-5.cpp b/solutions/cpp11/00-auto-and-decltype/5.cpp similarity index 72% rename from solutions/cpp11/00-auto-and-decltype-5.cpp rename to solutions/cpp11/00-auto-and-decltype/5.cpp index fe6a5ec..f7524b2 100644 --- a/solutions/cpp11/00-auto-and-decltype-5.cpp +++ b/solutions/cpp11/00-auto-and-decltype/5.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/00-auto-and-decltype-5.cpp // -#include +import std; +import d2x.harness; -#include // 5. const 与引用的剥离和保留 @@ -23,22 +23,22 @@ int main() { // auto 剥离顶层 const: a 推导成 int auto a = ci; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // auto 剥离引用: b 推导成 int (b 是 n 的独立副本) auto b = ri; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // 用 const auto& 保留 const 引用 const int& const auto& cr = ci; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line // 用 decltype 精确保留声明类型 const int decltype(ci) d = ci; type_check = std::is_same::value; - d2x_assert(type_check); type_check = false; // dont change this line + d2x::check(type_check, "type_check"); type_check = false; // dont change this line return 0; } diff --git a/solutions/cpp11/01-default-and-delete-0.cpp b/solutions/cpp11/01-default-and-delete/0.cpp similarity index 93% rename from solutions/cpp11/01-default-and-delete-0.cpp rename to solutions/cpp11/01-default-and-delete/0.cpp index 9e17570..eae670e 100644 --- a/solutions/cpp11/01-default-and-delete-0.cpp +++ b/solutions/cpp11/01-default-and-delete/0.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/01-default-and-delete-0.cpp // -#include +import std; +import d2x.harness; -#include // default和delete显式控制 -> 编译器默认构造函数的生成行为 struct A { }; diff --git a/solutions/cpp11/01-default-and-delete-1.cpp b/solutions/cpp11/01-default-and-delete/1.cpp similarity index 57% rename from solutions/cpp11/01-default-and-delete-1.cpp rename to solutions/cpp11/01-default-and-delete/1.cpp index ea00b86..5b6bae0 100644 --- a/solutions/cpp11/01-default-and-delete-1.cpp +++ b/solutions/cpp11/01-default-and-delete/1.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/01-default-and-delete-1.cpp // -#include +import std; +import d2x.harness; -#include // 实现std::unique_ptr不可以拷贝, 但可以移动的属性 struct UniquePtr { @@ -26,10 +26,10 @@ int main() { // 不要直接修改main函数中的代码 UniquePtr a; - d2x_assert(std::is_copy_constructible::value == false); - d2x_assert(std::is_copy_assignable::value == false); - d2x_assert(std::is_move_constructible::value == true); - d2x_assert(std::is_move_assignable::value == true); + d2x::check(std::is_copy_constructible::value == false, "std::is_copy_constructible::value == false"); + d2x::check(std::is_copy_assignable::value == false, "std::is_copy_assignable::value == false"); + d2x::check(std::is_move_constructible::value == true, "std::is_move_constructible::value == true"); + d2x::check(std::is_move_assignable::value == true, "std::is_move_assignable::value == true"); (void)a; return 0; diff --git a/solutions/cpp11/01-default-and-delete-2.cpp b/solutions/cpp11/01-default-and-delete/2.cpp similarity index 91% rename from solutions/cpp11/01-default-and-delete-2.cpp rename to solutions/cpp11/01-default-and-delete/2.cpp index d4c5d9a..9b020f4 100644 --- a/solutions/cpp11/01-default-and-delete-2.cpp +++ b/solutions/cpp11/01-default-and-delete/2.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/01-default-and-delete-2.cpp // -#include +import std; +import d2x.harness; -#include void func(int x) { std::cout << "x = " << x << std::endl; diff --git a/solutions/cpp11/02-final-and-override-0.cpp b/solutions/cpp11/02-final-and-override/0.cpp similarity index 93% rename from solutions/cpp11/02-final-and-override-0.cpp rename to solutions/cpp11/02-final-and-override/0.cpp index d29bb08..e3568c8 100644 --- a/solutions/cpp11/02-final-and-override-0.cpp +++ b/solutions/cpp11/02-final-and-override/0.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/02-final-and-override-0.cpp // -#include +import std; +import d2x.harness; -#include -#include struct A { virtual void func1() { diff --git a/solutions/cpp11/02-final-and-override-1.cpp b/solutions/cpp11/02-final-and-override/1.cpp similarity index 69% rename from solutions/cpp11/02-final-and-override-1.cpp rename to solutions/cpp11/02-final-and-override/1.cpp index dae19ae..da0aaf4 100644 --- a/solutions/cpp11/02-final-and-override-1.cpp +++ b/solutions/cpp11/02-final-and-override/1.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/02-final-and-override-1.cpp // -#include +import std; +import d2x.harness; -#include -#include struct A { // 移除 final, 否则 B 不能 override @@ -38,12 +37,12 @@ struct C : B { int main() { B final; // 不要直接修改main函数中的代码 - d2x_assert_eq(final.func1(), 3); // B::func1() - d2x_assert_eq(final.func2(), 4); // B::func2() + d2x::check_eq(final.func1(), 3, "final.func1() == 3"); // B::func1() + d2x::check_eq(final.func2(), 4, "final.func2() == 4"); // B::func2() A *a = &final; - d2x_assert_eq(a->func1(), 3); // B::func1() - d2x_assert_eq(a->func2(), 2); // A::func2() + d2x::check_eq(a->func1(), 3, "a->func1() == 3"); // B::func1() + d2x::check_eq(a->func2(), 2, "a->func2() == 2"); // A::func2() return 0; } diff --git a/solutions/cpp11/02-final-and-override-2.cpp b/solutions/cpp11/02-final-and-override/2.cpp similarity index 96% rename from solutions/cpp11/02-final-and-override-2.cpp rename to solutions/cpp11/02-final-and-override/2.cpp index 360a335..5a85f62 100644 --- a/solutions/cpp11/02-final-and-override-2.cpp +++ b/solutions/cpp11/02-final-and-override/2.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/02-final-and-override-2.cpp // -#include +import std; +import d2x.harness; -#include -#include struct AudioPlayer { // 不要直接修改AudioPlayer类 virtual void play() final { diff --git a/solutions/cpp11/03-trailing-return-type.cpp b/solutions/cpp11/03-trailing-return-type/0.cpp similarity index 64% rename from solutions/cpp11/03-trailing-return-type.cpp rename to solutions/cpp11/03-trailing-return-type/0.cpp index bb78e98..33709e2 100644 --- a/solutions/cpp11/03-trailing-return-type.cpp +++ b/solutions/cpp11/03-trailing-return-type/0.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/03-trailing-return-type.cpp // -#include +import std; +import d2x.harness; -#include int add0(double a, int b) { return a + b; @@ -29,11 +29,11 @@ auto add3 = [](double a, double b) -> int { int main() { - d2x_assert_eq(add0(1.1, 2), 3); - d2x_assert_eq(add1(1.1, 2), 3); - d2x_assert_eq(add2(1.1, 2), 3.1); - d2x_assert_eq(add2(1, 2.1), 3.1); - d2x_assert_eq(add3(1.1, 2.1), 3); + d2x::check_eq(add0(1.1, 2), 3, "add0(1.1, 2) == 3"); + d2x::check_eq(add1(1.1, 2), 3, "add1(1.1, 2) == 3"); + d2x::check_eq(add2(1.1, 2), 3.1, "add2(1.1, 2) == 3.1"); + d2x::check_eq(add2(1, 2.1), 3.1, "add2(1, 2.1) == 3.1"); + d2x::check_eq(add3(1.1, 2.1), 3, "add3(1.1, 2.1) == 3"); return 0; } diff --git a/solutions/cpp11/04-rvalue-references.cpp b/solutions/cpp11/04-rvalue-references/0.cpp similarity index 92% rename from solutions/cpp11/04-rvalue-references.cpp rename to solutions/cpp11/04-rvalue-references/0.cpp index 8becf04..7a9dc5c 100644 --- a/solutions/cpp11/04-rvalue-references.cpp +++ b/solutions/cpp11/04-rvalue-references/0.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/04-rvalue-references.cpp // -#include +import std; +import d2x.harness; -#include -#include struct Object; static Object * object_address = nullptr; @@ -48,10 +47,10 @@ int main() { // 关闭编译器优化 objRef.data = 1; // 修改被延长生命周期的临时对象的值(不要直接改动这行代码) std::cout << "objRef.data = " << objRef.data << " - " << &objRef << std::endl; - d2x_assert((&objRef == object_address)); + d2x::check((&objRef == object_address), "(&objRef == object_address)"); // 钉住移动构造确实发生过。教学漂移之所以能静默发生, 正是因为 // 从前没有任何断言检查它 —— 输出少了一行, 没人发现。 - d2x_assert((move_ctor_calls >= 1)); + d2x::check((move_ctor_calls >= 1), "(move_ctor_calls >= 1)"); } return 0; diff --git a/solutions/cpp11/05-move-semantics-0.cpp b/solutions/cpp11/05-move-semantics/0.cpp similarity index 86% rename from solutions/cpp11/05-move-semantics-0.cpp rename to solutions/cpp11/05-move-semantics/0.cpp index efb91c6..3f64ce0 100644 --- a/solutions/cpp11/05-move-semantics-0.cpp +++ b/solutions/cpp11/05-move-semantics/0.cpp @@ -8,9 +8,9 @@ // 教学要点: 让 Buffer 在拷贝时也"转移"资源, 只做一次资源分配和释放, // 这样每个传递点最终都指向同一块缓冲区, 三个 d2x_assert 都通过。 -#include +import std; +import d2x.harness; -#include struct Buffer { int *data; @@ -51,17 +51,17 @@ int main() { Buffer buff2(std::move(buff1)); auto buff2DataPtr = buff2.data_ptr(); - d2x_assert(buff1DataPtr == buff2DataPtr); + d2x::check(buff1DataPtr == buff2DataPtr, "buff1DataPtr == buff2DataPtr"); Buffer buff3 = buff2; auto buff3DataPtr = buff3.data_ptr(); - d2x_assert(buff2DataPtr == buff3DataPtr); + d2x::check(buff2DataPtr == buff3DataPtr, "buff2DataPtr == buff3DataPtr"); Buffer buff4 = process(buff3); auto buff4DataPtr = buff4.data_ptr(); - d2x_assert(buff3DataPtr == buff4DataPtr); + d2x::check(buff3DataPtr == buff4DataPtr, "buff3DataPtr == buff4DataPtr"); } return 0; diff --git a/solutions/cpp11/05-move-semantics-1.cpp b/solutions/cpp11/05-move-semantics/1.cpp similarity index 88% rename from solutions/cpp11/05-move-semantics-1.cpp rename to solutions/cpp11/05-move-semantics/1.cpp index 25d8a25..36119ce 100644 --- a/solutions/cpp11/05-move-semantics-1.cpp +++ b/solutions/cpp11/05-move-semantics/1.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/05-move-semantics-1.cpp // -#include +import std; +import d2x.harness; -#include static int move_assignment_counter = 0; @@ -72,17 +72,17 @@ int main() { // 无编译器优化 buff1 = Buffer(); // 情况1: 临时对象赋值 - d2x_assert_eq(move_assignment_counter, 1); + d2x::check_eq(move_assignment_counter, 1, "move_assignment_counter == 1"); Buffer buff2; buff2 = process(buff1); // 情况2: 中间对象赋值 - d2x_assert_eq(move_assignment_counter, 2); + d2x::check_eq(move_assignment_counter, 2, "move_assignment_counter == 2"); buff2 = std::move(buff1); // 情况3: 显式移动赋值 - d2x_assert_eq(move_assignment_counter, 3); + d2x::check_eq(move_assignment_counter, 3, "move_assignment_counter == 3"); } diff --git a/solutions/cpp11/05-move-semantics-2.cpp b/solutions/cpp11/05-move-semantics/2.cpp similarity index 86% rename from solutions/cpp11/05-move-semantics-2.cpp rename to solutions/cpp11/05-move-semantics/2.cpp index 9521d33..1f22feb 100644 --- a/solutions/cpp11/05-move-semantics-2.cpp +++ b/solutions/cpp11/05-move-semantics/2.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/05-move-semantics-2.cpp // -#include +import std; +import d2x.harness; -#include struct Buffer { int *data; @@ -64,9 +64,9 @@ int main() { // 移动语义 - 移动的是资源而不是对象演示 Buffer b2 = std::move(b1); // 触发移动构造 - d2x_assert(&b1 != &b2); // b1 和 b2 是不同的对象 - d2x_assert(old_b1_data_ptr == b2.data_ptr()); - d2x_assert(b1.data_ptr() == nullptr); // b1 的资源被移动了 + d2x::check(&b1 != &b2, "&b1 != &b2"); // b1 和 b2 是不同的对象 + d2x::check(old_b1_data_ptr == b2.data_ptr(), "old_b1_data_ptr == b2.data_ptr()"); + d2x::check(b1.data_ptr() == nullptr, "b1.data_ptr() == nullptr"); // b1 的资源被移动了 } diff --git a/solutions/cpp11/06-scoped-enums-0.cpp b/solutions/cpp11/06-scoped-enums/0.cpp similarity index 89% rename from solutions/cpp11/06-scoped-enums-0.cpp rename to solutions/cpp11/06-scoped-enums/0.cpp index 1085a11..4becf10 100644 --- a/solutions/cpp11/06-scoped-enums-0.cpp +++ b/solutions/cpp11/06-scoped-enums/0.cpp @@ -8,9 +8,9 @@ // 教学要点: 传统 enum 会污染外层作用域并隐式转 int, 这里通过重命名解决符号冲突, // 保留并展示 "color == Apple" 这种本不该相等却被静默接受的逻辑错误。 -#include +import std; +import d2x.harness; -#include enum Color { RED, @@ -30,8 +30,8 @@ int main() { Color color = RED; Fruit fruit = Apple; - d2x_assert_eq(color, RED); - d2x_assert_eq(fruit, Apple); + d2x::check_eq(color, RED, "color == RED"); + d2x::check_eq(fruit, Apple, "fruit == Apple"); // 2.符合语法, 但逻辑错误的类型匹配 if (color == Apple) { // 不要删除这行代码 diff --git a/solutions/cpp11/06-scoped-enums-1.cpp b/solutions/cpp11/06-scoped-enums/1.cpp similarity index 69% rename from solutions/cpp11/06-scoped-enums-1.cpp rename to solutions/cpp11/06-scoped-enums/1.cpp index 6846e23..d6529a5 100644 --- a/solutions/cpp11/06-scoped-enums-1.cpp +++ b/solutions/cpp11/06-scoped-enums/1.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/06-scoped-enums-1.cpp // -#include +import std; +import d2x.harness; -#include enum class Color { RED, @@ -29,12 +29,12 @@ int main() { Color color = Color::ORANGE; Fruit fruit = Fruit::ORANGE; - d2x_assert(color == Color::ORANGE); - d2x_assert(fruit == Fruit::ORANGE); + d2x::check(color == Color::ORANGE, "color == Color::ORANGE"); + d2x::check(fruit == Fruit::ORANGE, "fruit == Fruit::ORANGE"); // 2.类型安全: 防止不同类型的枚举值之间的比较 if (color == Color::ORANGE) { // 使用Color类型修复编译错误 - d2x_assert(color == Color::ORANGE); + d2x::check(color == Color::ORANGE, "color == Color::ORANGE"); } // 3.类型检查: 默认情况下, 范围枚举类型的值是不可隐式转换 @@ -49,8 +49,8 @@ int main() { ORANGE // 橙色 }; - d2x_assert_eq(sizeof(Color), sizeof(int)); // 默认类型是int - d2x_assert_eq(sizeof(Color8Bit), sizeof(int8_t)); // 可自定义类型int8_t + d2x::check_eq(sizeof(Color), sizeof(int), "sizeof(Color) == sizeof(int)"); // 默认类型是int + d2x::check_eq(sizeof(Color8Bit), sizeof(int8_t), "sizeof(Color8Bit) == sizeof(int8_t)"); // 可自定义类型int8_t // 5.自定义起始值: 默认情况下, 范围枚举类型的值从0开始, 往下递增 enum class ErrorCode : int { @@ -60,7 +60,7 @@ int main() { ERROR_3 = 3 }; - d2x_assert_eq(static_cast(ErrorCode::ERROR_3), 3); + d2x::check_eq(static_cast(ErrorCode::ERROR_3), 3, "static_cast(ErrorCode::ERROR_3) == 3"); return 0; } diff --git a/solutions/cpp11/07-constexpr-0.cpp b/solutions/cpp11/07-constexpr/0.cpp similarity index 89% rename from solutions/cpp11/07-constexpr-0.cpp rename to solutions/cpp11/07-constexpr/0.cpp index 1085249..4677664 100644 --- a/solutions/cpp11/07-constexpr-0.cpp +++ b/solutions/cpp11/07-constexpr/0.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/07-constexpr-0.cpp // -#include +import std; +import d2x.harness; -#include constexpr int sum_for_1_to(int n) { return n == 1 ? 1 : n + sum_for_1_to(n - 1); @@ -29,7 +29,7 @@ int main() { { // 2. 编译期计算基础 constexpr int s = sum_for_1_to(4); - d2x_assert_eq(s, 1 + 2 + 3 + 4); + d2x::check_eq(s, 1 + 2 + 3 + 4, "s == 1 + 2 + 3 + 4"); } return 0; diff --git a/solutions/cpp11/07-constexpr-1.cpp b/solutions/cpp11/07-constexpr/1.cpp similarity index 97% rename from solutions/cpp11/07-constexpr-1.cpp rename to solutions/cpp11/07-constexpr/1.cpp index d63a153..4985098 100644 --- a/solutions/cpp11/07-constexpr-1.cpp +++ b/solutions/cpp11/07-constexpr/1.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/07-constexpr-1.cpp // -#include +import std; +import d2x.harness; -#include template struct Sum { diff --git a/solutions/cpp11/08-literal-type-0.cpp b/solutions/cpp11/08-literal-type/0.cpp similarity index 95% rename from solutions/cpp11/08-literal-type-0.cpp rename to solutions/cpp11/08-literal-type/0.cpp index 840d946..236c958 100644 --- a/solutions/cpp11/08-literal-type-0.cpp +++ b/solutions/cpp11/08-literal-type/0.cpp @@ -9,11 +9,9 @@ // 这里把涉及 std::string 的部分降级为运行时, 其余字面值类型 (char/int/std::array) // 仍可参与编译期计算。 -#include +import std; +import d2x.harness; -#include -#include -#include constexpr char compile_time_compute(char c, int a) { return a + c; diff --git a/solutions/cpp11/08-literal-type-1.cpp b/solutions/cpp11/08-literal-type/1.cpp similarity index 92% rename from solutions/cpp11/08-literal-type-1.cpp rename to solutions/cpp11/08-literal-type/1.cpp index 264f0cb..9b47643 100644 --- a/solutions/cpp11/08-literal-type-1.cpp +++ b/solutions/cpp11/08-literal-type/1.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/08-literal-type-1.cpp // -#include +import std; +import d2x.harness; -#include struct Vector { int x, y; diff --git a/solutions/cpp11/09-list-initialization-0.cpp b/solutions/cpp11/09-list-initialization/0.cpp similarity index 67% rename from solutions/cpp11/09-list-initialization-0.cpp rename to solutions/cpp11/09-list-initialization/0.cpp index c96bf57..2dd5642 100644 --- a/solutions/cpp11/09-list-initialization-0.cpp +++ b/solutions/cpp11/09-list-initialization/0.cpp @@ -7,19 +7,19 @@ // // 教学要点: 大括号(列表)初始化对窄化转换不允许, 需要使用显式 static_cast 修复。 -#include +import std; +import d2x.harness; -#include int main() { int a1 = 1.1; // 拷贝初始化允许窄化, 截断为 1 - d2x_assert_eq(a1, 1); + d2x::check_eq(a1, 1, "a1 == 1"); int a2 = 1.1; int a3 = { static_cast(1.1) }; // 大括号初始化要求显式转换 - d2x_assert_eq(a2, 1); - d2x_assert_eq(a3, 1); + d2x::check_eq(a2, 1, "a2 == 1"); + d2x::check_eq(a3, 1, "a3 == 1"); double b1 { 1.1 }; constexpr double c1 { 2.2 }; @@ -27,16 +27,16 @@ int main() { int b2 { static_cast(b1) }; int c2 { static_cast(c1) }; - d2x_assert_eq(b2, 1); - d2x_assert_eq(c2, 2); + d2x::check_eq(b2, 1, "b2 == 1"); + d2x::check_eq(c2, 2, "c2 == 2"); int arr1[] = { 1, static_cast(2.2), 3 }; - d2x_assert_eq(arr1[1], 2); + d2x::check_eq(arr1[1], 2, "arr1[1] == 2"); int arr2[4] { 1, static_cast(b1), static_cast(c1) }; - d2x_assert_eq(arr2[1], 1); - d2x_assert_eq(arr2[2], 2); - d2x_assert_eq(arr2[3], 0); + d2x::check_eq(arr2[1], 1, "arr2[1] == 1"); + d2x::check_eq(arr2[2], 2, "arr2[2] == 2"); + d2x::check_eq(arr2[3], 0, "arr2[3] == 0"); return 0; } diff --git a/solutions/cpp11/09-list-initialization-1.cpp b/solutions/cpp11/09-list-initialization/1.cpp similarity index 84% rename from solutions/cpp11/09-list-initialization-1.cpp rename to solutions/cpp11/09-list-initialization/1.cpp index d470863..a0db470 100644 --- a/solutions/cpp11/09-list-initialization-1.cpp +++ b/solutions/cpp11/09-list-initialization/1.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/09-list-initialization-1.cpp // -#include +import std; +import d2x.harness; -#include struct Object { Object() { @@ -28,8 +28,8 @@ int main() { Object obj1{}; Object obj2(2); - d2x_assert_eq(obj1.x, 0); - d2x_assert_eq(obj2.x, 2); + d2x::check_eq(obj1.x, 0, "obj1.x == 0"); + d2x::check_eq(obj2.x, 2, "obj2.x == 2"); return 0; } diff --git a/solutions/cpp11/09-list-initialization-2.cpp b/solutions/cpp11/09-list-initialization/2.cpp similarity index 71% rename from solutions/cpp11/09-list-initialization-2.cpp rename to solutions/cpp11/09-list-initialization/2.cpp index f87f9f1..2d88c1d 100644 --- a/solutions/cpp11/09-list-initialization-2.cpp +++ b/solutions/cpp11/09-list-initialization/2.cpp @@ -6,11 +6,9 @@ // 教程练习入口: dslings/cpp11/09-list-initialization-2.cpp // -#include +import std; +import d2x.harness; -#include -#include -#include class MyVector { int mSize; @@ -26,14 +24,14 @@ class MyVector { int main() { std::vector vec1 = { 1, 2, 3 }; - d2x_assert_eq(vec1.size(), 3); + d2x::check_eq(vec1.size(), 3, "vec1.size() == 3"); std::vector vec2 { 1, 2, 3, 4, 5 }; - d2x_assert_eq(vec2.size(), 5); + d2x::check_eq(vec2.size(), 5, "vec2.size() == 5"); MyVector myVec1 = { 1, 2, 3 }; - d2x_assert_eq(myVec1.size(), 3); + d2x::check_eq(myVec1.size(), 3, "myVec1.size() == 3"); MyVector myVec2 { 1, 2, 3, 4, 5 }; - d2x_assert_eq(myVec2.size(), 5); + d2x::check_eq(myVec2.size(), 5, "myVec2.size() == 5"); return 0; } diff --git a/solutions/cpp11/09-list-initialization-3.cpp b/solutions/cpp11/09-list-initialization/3.cpp similarity index 86% rename from solutions/cpp11/09-list-initialization-3.cpp rename to solutions/cpp11/09-list-initialization/3.cpp index 4872fde..661301c 100644 --- a/solutions/cpp11/09-list-initialization-3.cpp +++ b/solutions/cpp11/09-list-initialization/3.cpp @@ -11,10 +11,9 @@ // 2. 列表初始化优先选 std::initializer_list 重载, 这里去掉 initializer_list 重载, // 让 `MyVector vec4 { 1, 10 }` 命中 (int, int) 构造函数。 -#include +import std; +import d2x.harness; -#include -#include class MyVector { int mSize; @@ -63,14 +62,14 @@ int main() { (void)p1; (void)p2; MyVector vec1(1); - d2x_assert_eq(vec1.size(), 1); + d2x::check_eq(vec1.size(), 1, "vec1.size() == 1"); MyVector vec2 { 1 }; - d2x_assert_eq(vec2.size(), 1); + d2x::check_eq(vec2.size(), 1, "vec2.size() == 1"); MyVector vec3(1, 10); - d2x_assert_eq(vec3.size(), 10); + d2x::check_eq(vec3.size(), 10, "vec3.size() == 10"); MyVector vec4 { 1, 10 }; - d2x_assert_eq(vec4.size(), 10); + d2x::check_eq(vec4.size(), 10, "vec4.size() == 10"); return 0; } diff --git a/solutions/cpp11/10-delegating-constructors-0.cpp b/solutions/cpp11/10-delegating-constructors/0.cpp similarity index 83% rename from solutions/cpp11/10-delegating-constructors-0.cpp rename to solutions/cpp11/10-delegating-constructors/0.cpp index 81ae70a..e98ab3d 100644 --- a/solutions/cpp11/10-delegating-constructors-0.cpp +++ b/solutions/cpp11/10-delegating-constructors/0.cpp @@ -9,10 +9,9 @@ // 既复用初始化逻辑, 又使每次构造仅按链路上每个构造函数体各 ++ 一次, // 计数器 1->3->5->6 与断言匹配。 -#include +import std; +import d2x.harness; -#include -#include static int construction_counter { 0 }; @@ -58,24 +57,22 @@ class Account { int main() { // 不要修改main函数中的代码 Account a1 { "1111" }; - d2x_assert_eq(construction_counter, 3); + d2x::check_eq(construction_counter, 3, "construction_counter == 3"); std::cout << a1.to_string() << std::endl; Account a2 { "2222", "wukong" }; - d2x_assert_eq(construction_counter, 5); + d2x::check_eq(construction_counter, 5, "construction_counter == 5"); std::cout << a2.to_string() << std::endl; Account a3 { "3333", "mcpp", 100 }; - d2x_assert_eq(construction_counter, 6); + d2x::check_eq(construction_counter, 6, "construction_counter == 6"); std::cout << a3.to_string() << std::endl; Account gi { "0000", "GImpact", 648 }; std::cout << gi.to_string() << std::endl; - d2x_assert( - gi.to_string() == - "Account { id: 0000, name: GImpact, coin: 648原石 }" - ); + d2x::check(gi.to_string() == + "Account { id: 0000, name: GImpact, coin: 648原石 }", "gi.to_string() == \"Account { id: 0000, name: GImpact, coin: 648原石 }\""); return 0; } diff --git a/solutions/cpp11/10-delegating-constructors-1.cpp b/solutions/cpp11/10-delegating-constructors/1.cpp similarity index 89% rename from solutions/cpp11/10-delegating-constructors-1.cpp rename to solutions/cpp11/10-delegating-constructors/1.cpp index 21306c6..14467ca 100644 --- a/solutions/cpp11/10-delegating-constructors-1.cpp +++ b/solutions/cpp11/10-delegating-constructors/1.cpp @@ -11,10 +11,9 @@ // `: Account(id_, name_, 0) {}` 才是真正的委托。 // 3. 用成员初始化列表直接构造 obj, 避免多余的默认构造 + 移动赋值, 让计数 == 1. -#include +import std; +import d2x.harness; -#include -#include struct Object { // 不要修改这个类的代码 static int construction_counter; @@ -76,14 +75,14 @@ int main() { // 不要修改main函数中的代码 Account a1 { "1111", "hello" }; std::cout << a1.to_string() << std::endl; - d2x_assert(a1.get_id() == "1111"); + d2x::check(a1.get_id() == "1111", "a1.get_id() == \"1111\""); Object::construction_counter = 0; Account a2 { "2222", "d2learn", 100 }; std::cout << a2.to_string() << std::endl; - d2x_assert(a2.get_object_name() == "d2learn"); - d2x_assert_eq(Object::construction_counter, 1); + d2x::check(a2.get_object_name() == "d2learn", "a2.get_object_name() == \"d2learn\""); + d2x::check_eq(Object::construction_counter, 1, "Object::construction_counter == 1"); return 0; } diff --git a/solutions/cpp11/11-inherited-constructors-0.cpp b/solutions/cpp11/11-inherited-constructors/0.cpp similarity index 96% rename from solutions/cpp11/11-inherited-constructors-0.cpp rename to solutions/cpp11/11-inherited-constructors/0.cpp index 77d28e5..e76d33a 100644 --- a/solutions/cpp11/11-inherited-constructors-0.cpp +++ b/solutions/cpp11/11-inherited-constructors/0.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/11-inherited-constructors-0.cpp // -#include +import std; +import d2x.harness; -#include -#include class ObjectBase { public: diff --git a/solutions/cpp11/11-inherited-constructors-1.cpp b/solutions/cpp11/11-inherited-constructors/1.cpp similarity index 63% rename from solutions/cpp11/11-inherited-constructors-1.cpp rename to solutions/cpp11/11-inherited-constructors/1.cpp index 62cd51a..ef0261a 100644 --- a/solutions/cpp11/11-inherited-constructors-1.cpp +++ b/solutions/cpp11/11-inherited-constructors/1.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/11-inherited-constructors-1.cpp // -#include +import std; +import d2x.harness; -#include -#include class Student { // 不要直接修改 Student 类中的代码 protected: @@ -61,31 +60,31 @@ int main() { // 不要直接修改 main 函数中的代码 { // 基础测试 StudentTest studentTest; - d2x_assert(studentTest.age_valid() == true); - d2x_assert(studentTest.score_valid() == true); - d2x_assert(studentTest.to_string() == "{001, 张三, 18, 0}"); + d2x::check(studentTest.age_valid() == true, "studentTest.age_valid() == true"); + d2x::check(studentTest.score_valid() == true, "studentTest.score_valid() == true"); + d2x::check(studentTest.to_string() == "{001, 张三, 18, 0}", "studentTest.to_string() == \"{001, 张三, 18, 0}\""); } { // 边界测试 StudentTest studentTest("002", "张三", 201); - d2x_assert(studentTest.score_valid() == true); - d2x_assert(studentTest.age_valid() == false); + d2x::check(studentTest.score_valid() == true, "studentTest.score_valid() == true"); + d2x::check(studentTest.age_valid() == false, "studentTest.age_valid() == false"); studentTest.set_score(101); studentTest.set_age(200); - d2x_assert(studentTest.score_valid() == false); - d2x_assert(studentTest.age_valid() == true); + d2x::check(studentTest.score_valid() == false, "studentTest.score_valid() == false"); + d2x::check(studentTest.age_valid() == true, "studentTest.age_valid() == true"); studentTest.set_score(0); studentTest.set_age(1); - d2x_assert(studentTest.score_valid() == true); - d2x_assert(studentTest.age_valid() == true); + d2x::check(studentTest.score_valid() == true, "studentTest.score_valid() == true"); + d2x::check(studentTest.age_valid() == true, "studentTest.age_valid() == true"); studentTest.set_score(-1); studentTest.set_age(0); - d2x_assert(studentTest.score_valid() == false); - d2x_assert(studentTest.age_valid() == false); + d2x::check(studentTest.score_valid() == false, "studentTest.score_valid() == false"); + d2x::check(studentTest.age_valid() == false, "studentTest.age_valid() == false"); } diff --git a/solutions/cpp11/11-inherited-constructors-2.cpp b/solutions/cpp11/11-inherited-constructors/2.cpp similarity index 84% rename from solutions/cpp11/11-inherited-constructors-2.cpp rename to solutions/cpp11/11-inherited-constructors/2.cpp index 8af159a..ce8d4ea 100644 --- a/solutions/cpp11/11-inherited-constructors-2.cpp +++ b/solutions/cpp11/11-inherited-constructors/2.cpp @@ -8,10 +8,9 @@ // 教学要点: NoMove 的拷贝控制只声明 = default 的拷贝构造/拷贝赋值, 不显式声明 // 移动操作 -> 移动操作不会被隐式生成, `std::move(p3)` 会回退到拷贝. -#include +import std; +import d2x.harness; -#include -#include struct Point { double mX, mY; @@ -56,13 +55,13 @@ int main() { auto p11 = p1; std::cout << "p11: " << p11.to_string() << std::endl; - d2x_assert_eq(p1.mX, p11.mX); - d2x_assert_eq(p1.mY, p11.mY); + d2x::check_eq(p1.mX, p11.mX, "p1.mX == p11.mX"); + d2x::check_eq(p1.mY, p11.mY, "p1.mY == p11.mY"); decltype(p2) p22 = std::move(p2); // NoCopy 不能拷贝, 只能 move std::cout << "p22: " << p22.to_string() << std::endl; - d2x_assert_eq(p2.mX, p22.mX); - d2x_assert_eq(p2.mY, p22.mY); + d2x::check_eq(p2.mX, p22.mX, "p2.mX == p22.mX"); + d2x::check_eq(p2.mY, p22.mY, "p2.mY == p22.mY"); NoMove p3(3, 3); std::cout << "p3: " << p3.to_string() << std::endl; @@ -70,8 +69,8 @@ int main() { NoMove p33(0, 0); p33 = std::move(p3); // NoMove 没有 move 重载, 回退到拷贝赋值 std::cout << "p33: " << p33.to_string() << std::endl; - d2x_assert_eq(p3.mX, p33.mX); - d2x_assert_eq(p3.mY, p33.mY); + d2x::check_eq(p3.mX, p33.mX, "p3.mX == p33.mX"); + d2x::check_eq(p3.mY, p33.mY, "p3.mY == p33.mY"); return 0; } diff --git a/solutions/cpp11/12-nullptr-0.cpp b/solutions/cpp11/12-nullptr/0.cpp similarity index 66% rename from solutions/cpp11/12-nullptr-0.cpp rename to solutions/cpp11/12-nullptr/0.cpp index efd31ee..6c2e721 100644 --- a/solutions/cpp11/12-nullptr-0.cpp +++ b/solutions/cpp11/12-nullptr/0.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/12-nullptr-0.cpp // -#include +import std; +import d2x.harness; -#include -#include int main() { @@ -18,13 +17,13 @@ int main() { int* ptr2 = NULL; // 修复这里,添加正确类型 int* ptr3 = 0; // 不推荐的传统用法 - d2x_assert(ptr1 == nullptr); - d2x_assert(ptr2 == nullptr); - d2x_assert(ptr3 == nullptr); + d2x::check(ptr1 == nullptr, "ptr1 == nullptr"); + d2x::check(ptr2 == nullptr, "ptr2 == nullptr"); + d2x::check(ptr3 == nullptr, "ptr3 == nullptr"); // 2. nullptr的类型 bool ok = std::is_same::value; - d2x_assert(ok); + d2x::check(ok, "ok"); // 3. 使用 nullptr 进行指针比较 int value = 42; @@ -32,7 +31,7 @@ int main() { if (ptr4 != nullptr) { *ptr4 = 2233; - d2x_assert_eq(*ptr4, 2233); + d2x::check_eq(*ptr4, 2233, "*ptr4 == 2233"); } // 4. 不同类型的指针都可以使用 nullptr @@ -40,9 +39,9 @@ int main() { char* cptr = nullptr; void* vptr = nullptr; - d2x_assert(dptr == nullptr); - d2x_assert(cptr == nullptr); - d2x_assert(vptr == nullptr); + d2x::check(dptr == nullptr, "dptr == nullptr"); + d2x::check(cptr == nullptr, "cptr == nullptr"); + d2x::check(vptr == nullptr, "vptr == nullptr"); return 0; } diff --git a/solutions/cpp11/12-nullptr-1.cpp b/solutions/cpp11/12-nullptr/1.cpp similarity index 77% rename from solutions/cpp11/12-nullptr-1.cpp rename to solutions/cpp11/12-nullptr/1.cpp index b2d9bb9..1e6857b 100644 --- a/solutions/cpp11/12-nullptr-1.cpp +++ b/solutions/cpp11/12-nullptr/1.cpp @@ -6,7 +6,8 @@ // 教程练习入口: dslings/cpp11/12-nullptr-1.cpp // -#include +import std; +import d2x.harness; bool process_int_called = false; bool process_ptr_called = false; @@ -40,10 +41,10 @@ int main() { display(nullptr); // 用 nullptr 准确选中 display(int*) process(0); // -> process(int) - d2x_assert(process_int_called); - d2x_assert(display_int_called); - d2x_assert(display_ptr_called); - d2x_assert(display_ptr_called); + d2x::check(process_int_called, "process_int_called"); + d2x::check(display_int_called, "display_int_called"); + d2x::check(display_ptr_called, "display_ptr_called"); + d2x::check(display_ptr_called, "display_ptr_called"); return 0; } diff --git a/solutions/cpp11/12-nullptr-2.cpp b/solutions/cpp11/12-nullptr/2.cpp similarity index 94% rename from solutions/cpp11/12-nullptr-2.cpp rename to solutions/cpp11/12-nullptr/2.cpp index 4906bac..f5b100b 100644 --- a/solutions/cpp11/12-nullptr-2.cpp +++ b/solutions/cpp11/12-nullptr/2.cpp @@ -6,8 +6,8 @@ // 教程练习入口: dslings/cpp11/12-nullptr-2.cpp // -#include -#include +import std; +import d2x.harness; // 模板函数示例 template diff --git a/solutions/cpp11/13-long-long-0.cpp b/solutions/cpp11/13-long-long/0.cpp similarity index 77% rename from solutions/cpp11/13-long-long-0.cpp rename to solutions/cpp11/13-long-long/0.cpp index 0b55006..6c1cc9b 100644 --- a/solutions/cpp11/13-long-long-0.cpp +++ b/solutions/cpp11/13-long-long/0.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/13-long-long-0.cpp // -#include +import std; +import d2x.harness; -#include int main() { @@ -19,7 +19,7 @@ int main() { // 2. 整数表示范围(unsigned int 装不下,需要 unsigned long long) unsigned long long uVal1 = 18446744073709551615ULL; - d2x_assert_eq(uVal1, 18446744073709551615ULL); + d2x::check_eq(uVal1, 18446744073709551615ULL, "uVal1 == 18446744073709551615ULL"); // 3. 类型推导和字面量后缀 auto longlong = 1234567890LL; @@ -28,8 +28,8 @@ int main() { bool is_longlong = std::is_same::value; bool is_ulonglong = std::is_same::value; - d2x_assert(is_longlong == true); - d2x_assert(is_ulonglong == true); + d2x::check(is_longlong == true, "is_longlong == true"); + d2x::check(is_ulonglong == true, "is_ulonglong == true"); return 0; } diff --git a/solutions/cpp11/13-long-long-1.cpp b/solutions/cpp11/13-long-long/1.cpp similarity index 62% rename from solutions/cpp11/13-long-long-1.cpp rename to solutions/cpp11/13-long-long/1.cpp index ca95734..823f5f7 100644 --- a/solutions/cpp11/13-long-long-1.cpp +++ b/solutions/cpp11/13-long-long/1.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/13-long-long-1.cpp // -#include +import std; +import d2x.harness; -#include int main() { @@ -18,14 +18,14 @@ int main() { auto minLL = std::numeric_limits::min(); auto maxULL = std::numeric_limits::max(); - d2x_assert_eq(maxInt, 2147483647); - d2x_assert_eq(maxLL, 9223372036854775807LL); - d2x_assert_eq(minLL, -9223372036854775807LL - 1); - d2x_assert_eq(maxULL, 18446744073709551615ULL); + d2x::check_eq(maxInt, 2147483647, "maxInt == 2147483647"); + d2x::check_eq(maxLL, 9223372036854775807LL, "maxLL == 9223372036854775807LL"); + d2x::check_eq(minLL, -9223372036854775807LL - 1, "minLL == -9223372036854775807LL - 1"); + d2x::check_eq(maxULL, 18446744073709551615ULL, "maxULL == 18446744073709551615ULL"); // 2. 大整数应用 - 表示世界人口(int 装不下 7.8e9) unsigned long long currentPopulation = 7800000000ULL; - d2x_assert_eq(currentPopulation, 7800000000ULL); + d2x::check_eq(currentPopulation, 7800000000ULL, "currentPopulation == 7800000000ULL"); return 0; } diff --git a/solutions/cpp11/14-type-alias-0.cpp b/solutions/cpp11/14-type-alias/0.cpp similarity index 68% rename from solutions/cpp11/14-type-alias-0.cpp rename to solutions/cpp11/14-type-alias/0.cpp index 9c045c8..cb278c0 100644 --- a/solutions/cpp11/14-type-alias-0.cpp +++ b/solutions/cpp11/14-type-alias/0.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/14-type-alias-0.cpp // -#include +import std; +import d2x.harness; -#include int main() { @@ -16,22 +16,22 @@ int main() { using Integer = int; using Real = double; - bool ok = std::is_same::value; d2x_assert(ok); - ok = std::is_same::value; d2x_assert(ok); + bool ok = std::is_same::value; d2x::check(ok, "ok"); + ok = std::is_same::value; d2x::check(ok, "ok"); // 2. 使用类型别名 Integer a = 42; Real b = 3.14; // 3. 验证类型别名 - d2x_assert_eq(a, 42); - d2x_assert_eq(b, 3.14); + d2x::check_eq(a, 42, "a == 42"); + d2x::check_eq(b, 3.14, "b == 3.14"); // 4. 类型别名本质相同 int c = 100; Integer d = c; // 可以赋值,因为本质都是int - d2x_assert_eq(c, d); + d2x::check_eq(c, d, "c == d"); return 0; } diff --git a/solutions/cpp11/14-type-alias-1.cpp b/solutions/cpp11/14-type-alias/1.cpp similarity index 76% rename from solutions/cpp11/14-type-alias-1.cpp rename to solutions/cpp11/14-type-alias/1.cpp index 2082868..fe6a586 100644 --- a/solutions/cpp11/14-type-alias-1.cpp +++ b/solutions/cpp11/14-type-alias/1.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/14-type-alias-1.cpp // -#include +import std; +import d2x.harness; -#include -#include static int func_called = 0; @@ -26,7 +25,7 @@ int main() { FuncPtr func = example_func; func(1, 2); - d2x_assert_eq(func_called, 3); + d2x::check_eq(func_called, 3, "func_called == 3"); // 2. 容器类型别名 using StringVector = std::vector; @@ -42,9 +41,9 @@ int main() { Container::ValueType value = 100; // 4. 验证类型别名 - d2x_assert(strings[0] == "hello"); - d2x_assert(strings[1] == "world"); - d2x_assert_eq(value, 100); + d2x::check(strings[0] == "hello", "strings[0] == \"hello\""); + d2x::check(strings[1] == "world", "strings[1] == \"world\""); + d2x::check_eq(value, 100, "value == 100"); return 0; } diff --git a/solutions/cpp11/14-type-alias-2.cpp b/solutions/cpp11/14-type-alias/2.cpp similarity index 68% rename from solutions/cpp11/14-type-alias-2.cpp rename to solutions/cpp11/14-type-alias/2.cpp index 586db61..263d9bc 100644 --- a/solutions/cpp11/14-type-alias-2.cpp +++ b/solutions/cpp11/14-type-alias/2.cpp @@ -6,12 +6,9 @@ // 教程练习入口: dslings/cpp11/14-type-alias-2.cpp // -#include +import std; +import d2x.harness; -#include -#include -#include -#include // 1. 基本别名模板 template @@ -32,13 +29,13 @@ int main() { Heap minHeap; (void)minHeap; - d2x_assert_eq(numbers[0], 1); - d2x_assert_eq(numbers[1], 2); - d2x_assert_eq(numbers[2], 3); + d2x::check_eq(numbers[0], 1, "numbers[0] == 1"); + d2x::check_eq(numbers[1], 2, "numbers[1] == 2"); + d2x::check_eq(numbers[2], 3, "numbers[2] == 3"); - d2x_assert_eq(v3[0], 1.0f); - d2x_assert_eq(v3[1], 2.0f); - d2x_assert_eq(v3[2], 3.0f); + d2x::check_eq(v3[0], 1.0f, "v3[0] == 1.0f"); + d2x::check_eq(v3[1], 2.0f, "v3[1] == 2.0f"); + d2x::check_eq(v3[2], 3.0f, "v3[2] == 3.0f"); return 0; } diff --git a/solutions/cpp11/14-type-alias-3.cpp b/solutions/cpp11/14-type-alias/3.cpp similarity index 85% rename from solutions/cpp11/14-type-alias-3.cpp rename to solutions/cpp11/14-type-alias/3.cpp index dc83708..8cb4bde 100644 --- a/solutions/cpp11/14-type-alias-3.cpp +++ b/solutions/cpp11/14-type-alias/3.cpp @@ -6,8 +6,8 @@ // 教程练习入口: dslings/cpp11/14-type-alias-3.cpp // -#include -#include +import std; +import d2x.harness; // 别名模板必须定义在命名空间作用域(不能写在函数体内) template @@ -20,8 +20,8 @@ int main() { bool ok = std::is_same, int*>::value; - d2x_assert(ok); - d2x_assert_eq(*ptr, 20); + d2x::check(ok, "ok"); + d2x::check_eq(*ptr, 20, "*ptr == 20"); return 0; } diff --git a/solutions/cpp11/15-variadic-templates-0.cpp b/solutions/cpp11/15-variadic-templates/0.cpp similarity index 86% rename from solutions/cpp11/15-variadic-templates-0.cpp rename to solutions/cpp11/15-variadic-templates/0.cpp index fe8a8c1..c023f12 100644 --- a/solutions/cpp11/15-variadic-templates-0.cpp +++ b/solutions/cpp11/15-variadic-templates/0.cpp @@ -6,8 +6,8 @@ // 教程练习入口: dslings/cpp11/15-variadic-templates-0.cpp // -#include -#include +import std; +import d2x.harness; std::stringstream ss; @@ -26,7 +26,7 @@ int main() { print(1, "hello", 3.14); std::string result = ss.str(); - d2x_assert(result == "1 hello 3.14 "); + d2x::check(result == "1 hello 3.14 ", "result == \"1 hello 3.14 \""); return 0; } diff --git a/solutions/cpp11/15-variadic-templates-1.cpp b/solutions/cpp11/15-variadic-templates/1.cpp similarity index 81% rename from solutions/cpp11/15-variadic-templates-1.cpp rename to solutions/cpp11/15-variadic-templates/1.cpp index ab75026..cdf51d4 100644 --- a/solutions/cpp11/15-variadic-templates-1.cpp +++ b/solutions/cpp11/15-variadic-templates/1.cpp @@ -6,7 +6,8 @@ // 教程练习入口: dslings/cpp11/15-variadic-templates-1.cpp // -#include +import std; +import d2x.harness; // 递归终止: 只剩一个参数时直接返回 template @@ -19,15 +20,15 @@ T sum(T first, Args... args) { int main() { int res1 = sum(1, 2, 3, 4, 5); - d2x_assert_eq(res1, 15); + d2x::check_eq(res1, 15, "res1 == 15"); double res2 = sum(1.5, 2.5, 3.0); - d2x_assert(res2 == 7.0); + d2x::check(res2 == 7.0, "res2 == 7.0"); // 混合类型 // 注意: 返回类型由第一个参数 T 决定 int res3 = sum(10, 20.5); - d2x_assert_eq(res3, 30); + d2x::check_eq(res3, 30, "res3 == 30"); return 0; } diff --git a/solutions/cpp11/16-generalized-unions-0.cpp b/solutions/cpp11/16-generalized-unions/0.cpp similarity index 87% rename from solutions/cpp11/16-generalized-unions-0.cpp rename to solutions/cpp11/16-generalized-unions/0.cpp index 72f36b1..5be020c 100644 --- a/solutions/cpp11/16-generalized-unions-0.cpp +++ b/solutions/cpp11/16-generalized-unions/0.cpp @@ -6,7 +6,8 @@ // 教程练习入口: dslings/cpp11/16-generalized-unions-0.cpp // -#include +import std; +import d2x.harness; union M @@ -22,7 +23,7 @@ int main() { M u1; //初始化成员 - d2x_assert(u1.a1 == 42); + d2x::check(u1.a1 == 42, "u1.a1 == 42"); u1.a2 = 21; double val = 3.14; diff --git a/solutions/cpp11/16-generalized-unions-1.cpp b/solutions/cpp11/16-generalized-unions/1.cpp similarity index 88% rename from solutions/cpp11/16-generalized-unions-1.cpp rename to solutions/cpp11/16-generalized-unions/1.cpp index 7fc63e3..780ea91 100644 --- a/solutions/cpp11/16-generalized-unions-1.cpp +++ b/solutions/cpp11/16-generalized-unions/1.cpp @@ -6,8 +6,8 @@ // 教程练习入口: dslings/cpp11/16-generalized-unions-1.cpp // -#include -#include +import std; +import d2x.harness; union M { @@ -31,7 +31,7 @@ int main() { u1.a2 = {1, 42, 3}; // 2. 验证:正常访问 - d2x_assert_eq(u1.a2[1], 42); + d2x::check_eq(u1.a2[1], 42, "u1.a2[1] == 42"); // 3. 手动析构 u1.a2.~vector(); diff --git a/solutions/cpp11/16-generalized-unions-2.cpp b/solutions/cpp11/16-generalized-unions/2.cpp similarity index 75% rename from solutions/cpp11/16-generalized-unions-2.cpp rename to solutions/cpp11/16-generalized-unions/2.cpp index 4f4a067..6c19c73 100644 --- a/solutions/cpp11/16-generalized-unions-2.cpp +++ b/solutions/cpp11/16-generalized-unions/2.cpp @@ -6,9 +6,8 @@ // 教程练习入口: dslings/cpp11/16-generalized-unions-2.cpp // -#include -#include -#include +import std; +import d2x.harness; enum class Tag { INTEGER, @@ -55,25 +54,25 @@ int main() { // 1. 构造 int 值并验证 Value v1(42); - d2x_assert(v1.tag == Tag::INTEGER); - d2x_assert_eq(v1.as_int(), 42); + d2x::check(v1.tag == Tag::INTEGER, "v1.tag == Tag::INTEGER"); + d2x::check_eq(v1.as_int(), 42, "v1.as_int() == 42"); // 2. 构造 string 值并验证 Value v2(std::string("hello")); - d2x_assert(v2.tag == Tag::STRING); - d2x_assert(v2.as_string() == "hello"); + d2x::check(v2.tag == Tag::STRING, "v2.tag == Tag::STRING"); + d2x::check(v2.as_string() == "hello", "v2.as_string() == \"hello\""); // 3. 从 string 切换到 int { Value v3(std::string("world")); - d2x_assert(v3.as_string() == "world"); + d2x::check(v3.as_string() == "world", "v3.as_string() == \"world\""); // 手动析构 string 成员, 切换到 int v3.data.s.~basic_string(); v3.tag = Tag::INTEGER; v3.data.i = 100; - d2x_assert_eq(v3.as_int(), 100); + d2x::check_eq(v3.as_int(), 100, "v3.as_int() == 100"); // 离开作用域时 ~Value() 发现 tag == INTEGER, 不析构 string } diff --git a/solutions/cpp11/17-pod-type-0.cpp b/solutions/cpp11/17-pod-type/0.cpp similarity index 90% rename from solutions/cpp11/17-pod-type-0.cpp rename to solutions/cpp11/17-pod-type/0.cpp index 58330f4..f8b2fb2 100644 --- a/solutions/cpp11/17-pod-type-0.cpp +++ b/solutions/cpp11/17-pod-type/0.cpp @@ -6,9 +6,9 @@ // 教程练习入口: dslings/cpp11/17-pod-type-0.cpp // -#include +import std; +import d2x.harness; -#include struct A { int x; @@ -40,17 +40,17 @@ int main() { // 3. 判断 A 是否是 trivial /standard_layout 类型 bool ok = std::is_trivial::value && std::is_standard_layout::value; - d2x_assert(ok); + d2x::check(ok, "ok"); // 4. 判断 B 是否是 trivial /standard_layout 类型 ok = std::is_trivial::value && std::is_standard_layout::value; - d2x_assert(ok); + d2x::check(ok, "ok"); // 5. C 含虚函数 -> 不是 trivial 类型 (assert 期望条件成立, 因此用否定) ok = !std::is_trivial::value; - d2x_assert(ok); + d2x::check(ok, "ok"); // 6. D 同时含 public/private 数据 -> 不是 standard_layout ok = std::is_standard_layout::value; - d2x_assert(!ok); + d2x::check(!ok, "!ok"); } diff --git a/solutions/cpp11/17-pod-type-1.cpp b/solutions/cpp11/17-pod-type/1.cpp similarity index 66% rename from solutions/cpp11/17-pod-type-1.cpp rename to solutions/cpp11/17-pod-type/1.cpp index da5d9e3..070a834 100644 --- a/solutions/cpp11/17-pod-type-1.cpp +++ b/solutions/cpp11/17-pod-type/1.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/17-pod-type-1.cpp // -#include +import std; +import d2x.harness; -#include -#include struct Packet { @@ -29,9 +28,9 @@ int main() { // 使用 memcpy 在 POD 上做按字节拷贝 std::memcpy(&p2, &p1, sizeof(Packet)); - d2x_assert_eq(p2.len, 42u); - d2x_assert_eq(p2.type, static_cast(1)); - d2x_assert_eq(p2.flags, static_cast(0xFF)); + d2x::check_eq(p2.len, 42u, "p2.len == 42u"); + d2x::check_eq(p2.type, static_cast(1), "p2.type == static_cast(1)"); + d2x::check_eq(p2.flags, static_cast(0xFF), "p2.flags == static_cast(0xFF)"); return 0; } diff --git a/solutions/cpp11/17-pod-type-2.cpp b/solutions/cpp11/17-pod-type/2.cpp similarity index 73% rename from solutions/cpp11/17-pod-type-2.cpp rename to solutions/cpp11/17-pod-type/2.cpp index 457550d..6bc2868 100644 --- a/solutions/cpp11/17-pod-type-2.cpp +++ b/solutions/cpp11/17-pod-type/2.cpp @@ -6,10 +6,9 @@ // 教程练习入口: dslings/cpp11/17-pod-type-2.cpp // -#include +import std; +import d2x.harness; -#include -#include // C 风格的消息头部,仅包含 POD 成员,用于跨语言/跨模块传递 @@ -29,7 +28,7 @@ struct Message { static MessageHeader g_last_header{}; extern "C" void c_send_header(const void* data, std::size_t size) { - d2x_assert_eq(size, sizeof(MessageHeader)); + d2x::check_eq(size, sizeof(MessageHeader), "size == sizeof(MessageHeader)"); const auto* header = static_cast(data); g_last_header = *header; } @@ -50,9 +49,9 @@ int main() { send_message_to_c(msg); // 验证 C 接口收到的头部内容是否正确 - d2x_assert_eq(g_last_header.type, msg.type); - d2x_assert_eq(g_last_header.len, static_cast(msg.payload.size())); - d2x_assert_eq(g_last_header.flags, static_cast(0)); + d2x::check_eq(g_last_header.type, msg.type, "g_last_header.type == msg.type"); + d2x::check_eq(g_last_header.len, static_cast(msg.payload.size()), "g_last_header.len == static_cast(msg.payload.size())"); + d2x::check_eq(g_last_header.flags, static_cast(0), "g_last_header.flags == static_cast(0)"); return 0; } From ae8daea215b5a3db225a51568d247fba5fb94d9c Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 22:43:53 +0800 Subject: [PATCH 13/25] =?UTF-8?q?fix:=20=E8=BF=81=E7=A7=BB=E6=B8=85?= =?UTF-8?q?=E5=B0=BE=E2=80=94=E2=80=94hello-mcpp=20=E6=95=99=E5=AD=A6?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E9=87=8D=E5=86=99+=E7=AD=94=E6=A1=88?= =?UTF-8?q?=E3=80=81NULL/int8=5Ft/dont=5Fdelete=5Fthis=E3=80=81harness=20f?= =?UTF-8?q?ormattable=20=E6=8E=A2=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cpp11/tests/06-scoped-enums/1.cpp | 2 +- cpp11/tests/10-delegating-constructors/0.cpp | 6 +-- cpp11/tests/12-nullptr/0.cpp | 1 + cpp11/tests/12-nullptr/2.cpp | 1 + en/cpp11/tests/06-scoped-enums/1.cpp | 2 +- .../tests/10-delegating-constructors/0.cpp | 6 +-- en/cpp11/tests/12-nullptr/0.cpp | 1 + en/cpp11/tests/12-nullptr/1.cpp | 1 + en/cpp11/tests/12-nullptr/2.cpp | 1 + en/intro/tests/hello-mcpp.cpp | 35 +++++++++-------- harness/src/harness.cppm | 13 ++++++- intro/tests/hello-mcpp.cpp | 39 ++++++++++--------- solutions/cpp11/05-move-semantics/0.cpp | 2 +- solutions/cpp11/06-scoped-enums/1.cpp | 4 +- .../cpp11/10-delegating-constructors/0.cpp | 8 ++-- solutions/cpp11/12-nullptr/0.cpp | 1 + solutions/cpp11/12-nullptr/2.cpp | 1 + solutions/intro/hello-mcpp.cpp | 21 ++++++++++ 18 files changed, 93 insertions(+), 52 deletions(-) create mode 100644 solutions/intro/hello-mcpp.cpp diff --git a/cpp11/tests/06-scoped-enums/1.cpp b/cpp11/tests/06-scoped-enums/1.cpp index 4f3cacd..799cf2f 100644 --- a/cpp11/tests/06-scoped-enums/1.cpp +++ b/cpp11/tests/06-scoped-enums/1.cpp @@ -57,7 +57,7 @@ int main() { }; d2x::check_eq(sizeof(Color), sizeof(int), "sizeof(Color) == sizeof(int)"); // 默认类型是int - d2x::check_eq(sizeof(Color8Bit), sizeof(int8_t), "sizeof(Color8Bit) == sizeof(int8_t)"); // 可自定义类型int8_t + d2x::check_eq(sizeof(Color8Bit), sizeof(std::int8_t), "sizeof(Color8Bit) == sizeof(std::int8_t)"); // 可自定义类型int8_t // 5.自定义起始值: 默认情况下, 范围枚举类型的值从0开始, 往下递增 enum class ErrorCode : int { diff --git a/cpp11/tests/10-delegating-constructors/0.cpp b/cpp11/tests/10-delegating-constructors/0.cpp index edb0a56..53eea89 100644 --- a/cpp11/tests/10-delegating-constructors/0.cpp +++ b/cpp11/tests/10-delegating-constructors/0.cpp @@ -32,7 +32,7 @@ class Account { name = "momo"; coin = "0元"; - D2X_DONT_DELETE_THIS(construction_counter++); + d2x::dont_delete_this(construction_counter++); } Account(std::string id_, std::string name_) { @@ -40,7 +40,7 @@ class Account { name = name_; coin = "0元"; - D2X_DONT_DELETE_THIS(construction_counter++); + d2x::dont_delete_this(construction_counter++); } Account(std::string id_, std::string name_, int coin_) { @@ -48,7 +48,7 @@ class Account { name = name_; coin = std::to_string(coin_) + "元"; - D2X_DONT_DELETE_THIS(construction_counter++); + d2x::dont_delete_this(construction_counter++); } std::string to_string() const { diff --git a/cpp11/tests/12-nullptr/0.cpp b/cpp11/tests/12-nullptr/0.cpp index f0617ab..e4bf4ec 100644 --- a/cpp11/tests/12-nullptr/0.cpp +++ b/cpp11/tests/12-nullptr/0.cpp @@ -17,6 +17,7 @@ // d2x checker nullptr // +#include // NULL 宏是这一课的教具,import std 不带宏 import std; import d2x.harness; diff --git a/cpp11/tests/12-nullptr/2.cpp b/cpp11/tests/12-nullptr/2.cpp index bee59fb..b9e6c95 100644 --- a/cpp11/tests/12-nullptr/2.cpp +++ b/cpp11/tests/12-nullptr/2.cpp @@ -17,6 +17,7 @@ // d2x checker nullptr // +#include // NULL 宏是这一课的教具,import std 不带宏 import std; import d2x.harness; diff --git a/en/cpp11/tests/06-scoped-enums/1.cpp b/en/cpp11/tests/06-scoped-enums/1.cpp index dd44815..cfc347d 100644 --- a/en/cpp11/tests/06-scoped-enums/1.cpp +++ b/en/cpp11/tests/06-scoped-enums/1.cpp @@ -57,7 +57,7 @@ int main() { }; d2x::check_eq(sizeof(Color), sizeof(int), "sizeof(Color) == sizeof(int)"); // Default type is int - d2x::check_eq(sizeof(Color8Bit), sizeof(int8_t), "sizeof(Color8Bit) == sizeof(int8_t)"); // Can customize type int8_t + d2x::check_eq(sizeof(Color8Bit), sizeof(std::int8_t), "sizeof(Color8Bit) == sizeof(std::int8_t)"); // Can customize type std::int8_t // 5. Custom starting value: By default, scoped enum type values start from 0 and increment downward enum class ErrorCode : int { diff --git a/en/cpp11/tests/10-delegating-constructors/0.cpp b/en/cpp11/tests/10-delegating-constructors/0.cpp index 1549d18..991585d 100644 --- a/en/cpp11/tests/10-delegating-constructors/0.cpp +++ b/en/cpp11/tests/10-delegating-constructors/0.cpp @@ -32,7 +32,7 @@ class Account { name = "momo"; coin = "0元"; - D2X_DONT_DELETE_THIS(construction_counter++); + d2x::dont_delete_this(construction_counter++); } Account(std::string id_, std::string name_) { @@ -40,7 +40,7 @@ class Account { name = name_; coin = "0元"; - D2X_DONT_DELETE_THIS(construction_counter++); + d2x::dont_delete_this(construction_counter++); } Account(std::string id_, std::string name_, int coin_) { @@ -48,7 +48,7 @@ class Account { name = name_; coin = std::to_string(coin_) + "元"; - D2X_DONT_DELETE_THIS(construction_counter++); + d2x::dont_delete_this(construction_counter++); } std::string to_string() const { diff --git a/en/cpp11/tests/12-nullptr/0.cpp b/en/cpp11/tests/12-nullptr/0.cpp index 857868b..6da67eb 100644 --- a/en/cpp11/tests/12-nullptr/0.cpp +++ b/en/cpp11/tests/12-nullptr/0.cpp @@ -17,6 +17,7 @@ // d2x checker nullptr // +#include // NULL 宏是这一课的教具,import std 不带宏 import std; import d2x.harness; diff --git a/en/cpp11/tests/12-nullptr/1.cpp b/en/cpp11/tests/12-nullptr/1.cpp index ee3f4f2..d08ad29 100644 --- a/en/cpp11/tests/12-nullptr/1.cpp +++ b/en/cpp11/tests/12-nullptr/1.cpp @@ -17,6 +17,7 @@ // d2x checker nullptr // +#include // NULL 宏是这一课的教具,import std 不带宏 import std; import d2x.harness; diff --git a/en/cpp11/tests/12-nullptr/2.cpp b/en/cpp11/tests/12-nullptr/2.cpp index 4c4b950..457f05d 100644 --- a/en/cpp11/tests/12-nullptr/2.cpp +++ b/en/cpp11/tests/12-nullptr/2.cpp @@ -17,6 +17,7 @@ // d2x checker nullptr // +#include // NULL 宏是这一课的教具,import std 不带宏 import std; import d2x.harness; diff --git a/en/intro/tests/hello-mcpp.cpp b/en/intro/tests/hello-mcpp.cpp index e7767e2..0e48f6d 100644 --- a/en/intro/tests/hello-mcpp.cpp +++ b/en/intro/tests/hello-mcpp.cpp @@ -5,19 +5,20 @@ // Exercise: Automated Code Practice Usage Tutorial // // Tips: -// This project is an automated code practice project built using the xlings tool. -// By executing `d2x checker` in the project root directory, you enter the -// "compiler-driven development mode" for automatic code practice detection. -// You need to modify errors in the code based on console error messages and prompts. -// After fixing all compilation errors and runtime checkpoints, you can delete or -// comment out the D2X_WAIT macro in the code to automatically proceed to the next exercise. +// This is an "exercises are tests" automated practice project. Two ways to play: // -// - D2X_WAIT: This macro is used to isolate different exercises. You can delete or -// comment out this macro to proceed to the next exercise. -// - d2x_assert_eq: This macro is used for runtime checkpoints. You need to fix -// errors in the code so that all checkpoints pass. -// - D2X_YOUR_ANSWER: This macro indicates code that needs to be modified, generally -// used for code completion (replace this macro with correct code) +// d2x checker guided mode: auto-detects your edits, passing advances you +// mcpp test -p en/intro native mode: plain mcpp — the test report IS your progress +// +// Fix the code based on console error messages. There are only three conventions: +// +// - D2X_YOUR_ANSWER: the fill-in-the-blank placeholder — replace it with correct +// code. It is not a macro, just a name nobody defines, so the compiler error +// points exactly at the blank +// - d2x::check / d2x::check_eq: runtime checkpoints — fix the code so every +// check passes (do not delete the checkpoints) +// - d2x::wait(): the barrier between exercises — delete it once you have read +// the lesson to really finish it // // Docs: // - https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/chapter_1.md @@ -45,7 +46,7 @@ int main() { d2x::check_eq(b, 1, "b == 1"); // 4. Runtime checkpoint 2 - D2X_WAIT // 5. Delete or comment out this macro to proceed to the next exercise (project formal code practice) + d2x::wait(); // 5. Delete or comment out this line to proceed to the next exercise (project formal code practice) return 0; } @@ -60,19 +61,19 @@ int main() { [Target: 00-0-hello-mcpp] - normal -->> Current exercise name -❌ Error: Compilation/Running failed for dslings/hello-mcpp.cpp -->> Shows detection status +❌ Error: Compilation/Running failed for en/intro/tests/hello-mcpp.cpp -->> Shows detection status The code exist some error! ---------C-Output--------- - Compiler output information -[HONLY LOGW]: main: dslings/hello-mcpp.cpp:24 - ❌ | a == 1.1 (1 == 1.100000) -->> Error prompt and location (line 24) -[HONLY LOGW]: main: dslings/hello-mcpp.cpp:26 - 🥳 Delete the D2X_WAIT to continue... +[HONLY LOGW]: main: en/intro/tests/hello-mcpp.cpp:24 - ❌ | a == 1.1 (1 == 1.100000) -->> Error prompt and location (line 24) +[HONLY LOGW]: main: en/intro/tests/hello-mcpp.cpp:26 - 🥳 Delete the d2x::wait() to continue... AI-Tips-Config: https://xlings.d2learn.org/en/documents/d2x/intro.html -->> AI tips (requires configuring large model key, optional) ---------E-Files--------- -dslings/hello-mcpp.cpp -->> Current file being checked +en/intro/tests/hello-mcpp.cpp -->> Current file being checked ------------------------- Homepage: https://github.com/openxlings/xlings diff --git a/harness/src/harness.cppm b/harness/src/harness.cppm index 40678a6..6d87b9b 100644 --- a/harness/src/harness.cppm +++ b/harness/src/harness.cppm @@ -98,8 +98,12 @@ inline void report_wait(const char* file, int line) { emit(out); } +// std::formattable 是 SFINAE 安全的探测;直接在 requires 里写 +// std::format("{}", v) 会踩进未特化 std::formatter 的 static_assert +// 硬错误(scoped enum、自定义类型的练习实测中招)。 inline std::string show_impl(const auto& v) { - if constexpr (requires { std::format("{}", v); }) return std::format("{}", v); + using T = std::remove_cvref_t; + if constexpr (std::formattable) return std::format("{}", v); else return {}; } @@ -147,6 +151,13 @@ inline bool check_eq(const A& a, const B& b, std::string_view what = {}, return ok; } +// 恒等透传:标记「这一行是教学观测点,别删」。旧宏 D2X_DONT_DELETE_THIS +// 的函数化等价物——对表达式原样求值并返回。 +template +constexpr decltype(auto) dont_delete_this(T&& x) { + return std::forward(x); +} + // 显式路障:学员读完说明、删掉这一行才算真正完成这一题。 // 记录后照常返回(不中断程序)——后续断言还要跑;退出码由 exit_hook 收口, // Provider 再根据侧信道把「只剩 wait」区分成 blocked。 diff --git a/intro/tests/hello-mcpp.cpp b/intro/tests/hello-mcpp.cpp index 98321e6..a3c5e82 100644 --- a/intro/tests/hello-mcpp.cpp +++ b/intro/tests/hello-mcpp.cpp @@ -5,14 +5,18 @@ // Exercise/练习: 自动化代码练习使用教学 // // Tips/提示: -// 该项目是使用xlings工具搭建的自动化代码练习项目, 通过在项目根目录下 -// 执行 d2x checker 进入"编译器驱动开发模式"的练习代码自动检测. -// 你需要根据控制台的报错和提示信息, 修改代码中的错误. 当修复所有编译错误和 -// 运行时检查点后, 你可以删除或注释掉代码中的 D2X_WAIT 宏, 会自动进入下一个练习. +// 这是一个「练习即测试」的自动化代码练习项目。两种玩法任选: // -// - D2X_WAIT: 该宏用于隔离不同练习, 你可以删除或注释掉该宏, 进入下一个练习. -// - d2x_assert_eq: 该宏用于运行时检查点, 你需要修复代码中的错误, 使得所有 -// - D2X_YOUR_ANSWER: 该宏用于提示你需要修改的代码, 一般用于代码填空(即用正确的代码替换这个宏) +// d2x checker 闯关模式: 自动检测、通过即进入下一题 +// mcpp test -p intro 原生模式: 直接用 mcpp 跑, 测试结果就是进度表 +// +// 你需要根据控制台的报错和提示信息修改代码。约定只有三个: +// +// - D2X_YOUR_ANSWER: 填空占位符 —— 用正确的代码替换它。它不是宏, +// 只是一个没人定义的名字, 所以编译器会指着它报错, 那就是要填的地方 +// - d2x::check / d2x::check_eq: 运行时检查点, 修复代码让所有检查通过 +// (不能直接删除检查点) +// - d2x::wait(): 练习之间的路障 —— 读完这一课, 删掉它才算真正完成 // // Docs/文档: // - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/chapter_1.md @@ -40,7 +44,7 @@ int main() { d2x::check_eq(b, 1, "b == 1"); // 4.运行时检查点2 - D2X_WAIT // 5.删除或注释掉这个宏, 进入下一个练习(项目正式代码练习) + d2x::wait(); // 5.删除或注释掉这一行, 进入下一个练习(项目正式代码练习) return 0; } @@ -51,25 +55,22 @@ int main() { # [[ 控制台输出解读 ]] -🌏Progress: [>----------] 0/10 -->> 显示当前的练习进度 - -[Target: 00-0-hello-mcpp] - normal -->> 当前的练习名 - -❌ Error: Compilation/Running failed for dslings/hello-mcpp.cpp -->> 显示检测状态 +🌏Progress: [>----------] 0/52 -->> 显示当前的练习进度 - The code exist some error! +[Exercise: hello-mcpp] -->> 当前的练习名 ----------C-Output--------- - 编译器输出信息 -[HONLY LOGW]: main: dslings/hello-mcpp.cpp:24 - ❌ | a == 1.1 (1 == 1.100000) -->> 错误提示及位置(24行) -[HONLY LOGW]: main: dslings/hello-mcpp.cpp:26 - 🥳 Delete the D2X_WAIT to continue... +❌ Error: Compilation/Running failed for intro/tests/hello-mcpp.cpp -->> 显示检测状态 +---------Output--------- - 编译/运行输出信息 +[HONLY LOGW]: intro/tests/hello-mcpp.cpp:41 - ❌ | a == 1.1 (1 == 1.1) -->> 错误提示及位置(41行) +[HONLY LOGW]: intro/tests/hello-mcpp.cpp:47 - 🥳 Delete the d2x::wait() to continue... AI-Tips-Config: https://xlings.d2learn.org/documents/d2x/intro.html -->> AI提示(需要配置大模型的key, 可不使用) ---------E-Files--------- -dslings/hello-mcpp.cpp -->> 当前检测的文件 +intro/tests/hello-mcpp.cpp -->> 当前检测的文件 ------------------------- Homepage: https://github.com/openxlings/xlings -*/ \ No newline at end of file +*/ diff --git a/solutions/cpp11/05-move-semantics/0.cpp b/solutions/cpp11/05-move-semantics/0.cpp index 3f64ce0..7d65c68 100644 --- a/solutions/cpp11/05-move-semantics/0.cpp +++ b/solutions/cpp11/05-move-semantics/0.cpp @@ -6,7 +6,7 @@ // 教程练习入口: dslings/cpp11/05-move-semantics-0.cpp // // 教学要点: 让 Buffer 在拷贝时也"转移"资源, 只做一次资源分配和释放, -// 这样每个传递点最终都指向同一块缓冲区, 三个 d2x_assert 都通过。 +// 这样每个传递点最终都指向同一块缓冲区, 三个 d2x::check 都通过。 import std; import d2x.harness; diff --git a/solutions/cpp11/06-scoped-enums/1.cpp b/solutions/cpp11/06-scoped-enums/1.cpp index d6529a5..23735f8 100644 --- a/solutions/cpp11/06-scoped-enums/1.cpp +++ b/solutions/cpp11/06-scoped-enums/1.cpp @@ -42,7 +42,7 @@ int main() { (void)colorValue; // 4.可自定义底层类型, 控制内存布局 - enum class Color8Bit : int8_t { + enum class Color8Bit : std::int8_t { RED, GREEN, BLUE, @@ -50,7 +50,7 @@ int main() { }; d2x::check_eq(sizeof(Color), sizeof(int), "sizeof(Color) == sizeof(int)"); // 默认类型是int - d2x::check_eq(sizeof(Color8Bit), sizeof(int8_t), "sizeof(Color8Bit) == sizeof(int8_t)"); // 可自定义类型int8_t + d2x::check_eq(sizeof(Color8Bit), sizeof(std::int8_t), "sizeof(Color8Bit) == sizeof(std::int8_t)"); // 可自定义类型int8_t // 5.自定义起始值: 默认情况下, 范围枚举类型的值从0开始, 往下递增 enum class ErrorCode : int { diff --git a/solutions/cpp11/10-delegating-constructors/0.cpp b/solutions/cpp11/10-delegating-constructors/0.cpp index e98ab3d..22d6102 100644 --- a/solutions/cpp11/10-delegating-constructors/0.cpp +++ b/solutions/cpp11/10-delegating-constructors/0.cpp @@ -25,28 +25,28 @@ class Account { Account(std::string id_) : Account(id_, "momo") { - D2X_DONT_DELETE_THIS(construction_counter++); + d2x::dont_delete_this(construction_counter++); } // 2 参 -> 委托给 3 参版本, 自身体再增加一次计数 (a2: +2) Account(std::string id_, std::string name_) : Account(id_, name_, 0) { - D2X_DONT_DELETE_THIS(construction_counter++); + d2x::dont_delete_this(construction_counter++); } // 3 参 -> 真正初始化的"基础版本" (a3: +1) Account(std::string id_, std::string name_, int coin_) { id = id_; name = name_; - // 满足最后一个 d2x_assert: GImpact 角色 coin 后缀使用 "原石" + // 满足最后一个 d2x::check: GImpact 角色 coin 后缀使用 "原石" if (name == "GImpact") { coin = std::to_string(coin_) + "原石"; } else { coin = std::to_string(coin_) + "元"; } - D2X_DONT_DELETE_THIS(construction_counter++); + d2x::dont_delete_this(construction_counter++); } std::string to_string() const { diff --git a/solutions/cpp11/12-nullptr/0.cpp b/solutions/cpp11/12-nullptr/0.cpp index 6c2e721..a31d793 100644 --- a/solutions/cpp11/12-nullptr/0.cpp +++ b/solutions/cpp11/12-nullptr/0.cpp @@ -6,6 +6,7 @@ // 教程练习入口: dslings/cpp11/12-nullptr-0.cpp // +#include // NULL 宏是这一课的教具,import std 不带宏 import std; import d2x.harness; diff --git a/solutions/cpp11/12-nullptr/2.cpp b/solutions/cpp11/12-nullptr/2.cpp index f5b100b..62029ca 100644 --- a/solutions/cpp11/12-nullptr/2.cpp +++ b/solutions/cpp11/12-nullptr/2.cpp @@ -6,6 +6,7 @@ // 教程练习入口: dslings/cpp11/12-nullptr-2.cpp // +#include // NULL 宏是这一课的教具,import std 不带宏 import std; import d2x.harness; diff --git a/solutions/intro/hello-mcpp.cpp b/solutions/intro/hello-mcpp.cpp new file mode 100644 index 0000000..fbaaa71 --- /dev/null +++ b/solutions/intro/hello-mcpp.cpp @@ -0,0 +1,21 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// file: intro/tests/hello-mcpp.cpp (参考答案) + +import std; +import d2x.harness; + +int main() { + + std::cout << "hello, mcpp!" << std::endl; // 0.修复这个编译错误 + + double a = 1.1; // 1.修复这个运行时错误, 修改int为double, 通过检查 + + d2x::check_eq(a, 1.1, "a == 1.1"); // 2.运行时检查点, 需要修复代码通过所有检查点(不能直接删除检查点代码) + + int b = a; // 3.修复这个编译错误, 给b一个合适的类型(int 截断 1.1 -> 1) + + d2x::check_eq(b, 1, "b == 1"); // 4.运行时检查点2 + + return 0; +} From b0257fe079a5296ce9ebd71f1b95155ea4def281 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 22:47:33 +0800 Subject: [PATCH 14/25] =?UTF-8?q?test(e2e):=20=E9=87=8D=E5=86=99=E4=B8=BA?= =?UTF-8?q?=20mcpp=20test=20=E9=A9=B1=E5=8A=A8=E2=80=94=E2=80=94pristine?= =?UTF-8?q?=200-pass=20+=2052/52=20=E7=AD=94=E6=A1=88=E5=85=A8=E7=BB=BF(zh?= =?UTF-8?q?/en),=E9=98=B2=E7=BA=BF=E4=BF=9D=E7=95=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- d2x/buildtools/mcpp/tests/e2e.sh | 189 +++++++++++++++++-------------- 1 file changed, 104 insertions(+), 85 deletions(-) diff --git a/d2x/buildtools/mcpp/tests/e2e.sh b/d2x/buildtools/mcpp/tests/e2e.sh index f889843..dde2f26 100755 --- a/d2x/buildtools/mcpp/tests/e2e.sh +++ b/d2x/buildtools/mcpp/tests/e2e.sh @@ -1,99 +1,118 @@ #!/usr/bin/env bash -# Provider 端到端测试。 +# d2mcpp 端到端验证 —— 两条硬断言,缺一不可: # -# 断言两件事,缺一不可: -# 1. 每个练习「未完成时」不通过 —— 否则学员会被直接跳过,练习形同虚设 -# 2. 每个参考答案「放进去后」通过 —— 否则参考答案本身是错的 +# 1) 每个练习未完成时不通过 (pristine 全量 mcpp test 必须 0 passed) +# 2) 每份参考答案放进去后通过 (overlay 后全量 mcpp test 必须全绿) # -# 这是 rustlings `cargo dev check --require-solutions` 的等价物。d2mcpp 现有的 -# dslings-ref-ci.yml 因为 solutions/ 在 xmake.lua 里被注释掉,实际校验零个目标。 +# 这是 rustlings `cargo dev check --require-solutions` 的等价物。 +# +# 两道防线(各自都咬过人,见 .agents/docs 参考文档): +# - 脏树检查: 练习/答案目录有未提交改动时拒绝运行 —— 脚本要把答案覆盖到 +# 练习上再用 git 还原,未提交的改动会被吃掉 +# - 防空转: overlay 后 passed 总数为 0 时直接失败,绝不静默绿灯 +# +# 用法: bash d2x/buildtools/mcpp/tests/e2e.sh [zh|en|all] (默认 all) +# 依赖: PATH 里的 mcpp 需支持 mcpp test 逐测试隔离/过滤/--message-format json set -uo pipefail -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)" -cd "$REPO_ROOT" - -PROVIDER=(mcpp run -q -p d2x/buildtools/mcpp --) +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)" +cd "$ROOT" + +MODE="${1:-all}" + +run_lang() { + local prefix="$1" # "" (zh) 或 "en/" + local label="$2" + local members=("${prefix}intro" "${prefix}cpp11" "${prefix}cpp14") + local test_dirs=() + for m in "${members[@]}"; do test_dirs+=("$m/tests"); done + + # —— 防线 1: 脏树检查 —— + local dirty + dirty=$(git status --porcelain -- "${test_dirs[@]}" solutions/ | head -20) + if [[ -n "$dirty" ]]; then + echo "E2E($label) 拒绝运行: 练习/答案目录有未提交改动(脚本的覆盖-还原会吃掉它们):" + echo "$dirty" + return 1 + fi -# 任何退出路径都还原被改动的练习文件,绝不把仓库留在脏状态。 -# -# 只还原练习源文件,绝不整目录还原 dslings/ —— 脚手架库 dslings/harness/ -# 也在这个目录下,整目录还原会把开发中的改动一起抹掉(踩过一次)。 -EXERCISE_DIRS=(dslings/cpp* dslings/en dslings/hello-mcpp.cpp) -restore() { - for d in "${EXERCISE_DIRS[@]}"; do - [ -e "$d" ] && git checkout -- "$d" 2>/dev/null || true + restore() { git checkout -q -- "${test_dirs[@]}"; } + + # —— 断言 1: pristine 全量必须 0 passed —— + local m out passed + for m in "${members[@]}"; do + out=$(mcpp test -p "$m" 2>&1) + passed=$(echo "$out" | grep -oE '[0-9]+ passed' | grep -oE '^[0-9]+' | tail -1) + if [[ "${passed:-0}" != "0" ]]; then + echo "E2E($label) FAIL: $m 在未完成状态下有 $passed 个练习通过了 —— 练习失去了「默认不通过」性质" + echo "$out" | grep " ... ok" | head -5 + return 1 + fi done -} - -# 本脚本会把参考答案覆盖到练习上再还原,所以运行前练习必须是干净的 —— -# 否则未提交的改动会被 restore 悄悄丢掉(作者踩过两次:一次丢了脚手架, -# 一次丢了刚修好的练习)。宁可拒绝运行,也不能吃掉别人的工作。 -if ! git diff --quiet -- "${EXERCISE_DIRS[@]}" 2>/dev/null; then - echo "拒绝运行:练习目录有未提交的改动,本测试会在结束时还原它们。" - echo "请先提交或 stash:" - git diff --stat -- "${EXERCISE_DIRS[@]}" | sed 's/^/ /' - exit 2 -fi - -trap restore EXIT - -outcome_of() { # $1 = exercise id - "${PROVIDER[@]}" check "$1" 2>&1 \ - | grep -o '"outcome":"[a-z]*"' | head -1 | cut -d'"' -f4 -} - -echo "==> 枚举练习" -mapfile -t LINES < <("${PROVIDER[@]}" exercises 2>&1 | grep '"event":"exercise"') -if [ "${#LINES[@]}" -eq 0 ]; then - echo "FAIL: Provider 没有枚举出任何练习"; exit 1 -fi -echo " 共 ${#LINES[@]} 个" - -pass=0; fail=0; skipped=0 - -for line in "${LINES[@]}"; do - id=$(printf '%s' "$line" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) - file=$(printf '%s' "$line" | grep -o '"files":\["[^"]*"' | head -1 | cut -d'"' -f4) - - # en/ 必须先剥,再拼 solutions/ —— 顺序反了的话 sol 已经以 "solutions/" - # 开头,`${sol#en/}` 匹配不到任何东西,是个静默 no-op,结果所有英文练习 - # 都因为找不到 solutions/en/... 而被 SKIP,测试全绿却一个都没验。 - # 这正是本脚本头部注释里说要防的那种「空转」。 - rel="${file#"$REPO_ROOT"/}" # dslings[/en]/cpp11/xx.cpp - rel="${rel#dslings/en/}" # en 镜像共用同一份参考答案 - rel="${rel#dslings/}" # cpp11/xx.cpp - sol="solutions/${rel}" # solutions/cpp11/xx.cpp + echo "E2E($label) pristine: 全部练习保持未通过 ✓" + + # —— 覆盖参考答案 (zh/en 共用同一份 solutions) —— + local n_overlaid=0 sol rel std rest dst + while IFS= read -r sol; do + rel="${sol#solutions/}" # intro/hello-mcpp.cpp | cpp11/00-x/0.cpp + std="${rel%%/*}"; rest="${rel#*/}" + dst="${prefix}${std}/tests/${rest}" + if [[ ! -f "$dst" ]]; then + echo "E2E($label) FAIL: 答案 $sol 没有对应的练习文件 $dst" + restore + return 1 + fi + cp "$sol" "$dst" + n_overlaid=$((n_overlaid + 1)) + done < <(find solutions -name '*.cpp' | sort) + + # —— 断言 2 + 防线 2: overlay 后全绿,且 passed 总数与答案数一致 —— + local total_passed=0 ok=1 + for m in "${members[@]}"; do + out=$(mcpp test -p "$m" 2>&1) + if ! echo "$out" | grep -q "test result ok"; then + echo "E2E($label) FAIL: $m 覆盖答案后仍有失败:" + echo "$out" | grep -E "FAIL|failures" -A10 | head -15 + ok=0 + fi + passed=$(echo "$out" | grep -oE '[0-9]+ passed' | grep -oE '^[0-9]+' | tail -1) + total_passed=$((total_passed + ${passed:-0})) + done + restore - # 1) 未完成态必须不通过 - got=$(outcome_of "$id") - if [ "$got" = "pass" ]; then - echo "FAIL [$id] 练习未完成却判定通过"; fail=$((fail+1)); continue + if [[ "$ok" != 1 ]]; then return 1; fi + if [[ "$total_passed" -eq 0 ]]; then + echo "E2E($label) FAIL: 防空转 —— passed 总数为 0,脚本没有真正跑到任何练习" + return 1 fi - - # 2) 参考答案必须通过 - if [ ! -f "$sol" ]; then - echo "SKIP [$id] 无参考答案 ($sol)"; skipped=$((skipped+1)); continue + if [[ "$total_passed" -ne "$n_overlaid" ]]; then + echo "E2E($label) FAIL: 覆盖了 $n_overlaid 份答案但只有 $total_passed 个练习通过" + return 1 fi - cp "$sol" "$file" - got=$(outcome_of "$id") - git checkout -- "$file" + echo "E2E($label) solutions: $total_passed/$n_overlaid 参考答案全部通过 ✓" +} - if [ "$got" = "pass" ]; then - pass=$((pass+1)) - else - echo "FAIL [$id] 参考答案未通过 (outcome=$got)"; fail=$((fail+1)) +provider_smoke() { + # Provider 协议冒烟:枚举数量、check 的关键事件 + local n + n=$(mcpp run -q -p d2x/buildtools/mcpp -- exercises | grep -c '"event":"exercise"') + if [[ "$n" -lt 52 ]]; then + echo "E2E(provider) FAIL: 枚举到 $n 个练习(预期 >= 52)" + return 1 fi -done - -echo -echo "==> 参考答案通过 $pass · 失败 $fail · 跳过 $skipped" + local out + out=$(mcpp run -q -p d2x/buildtools/mcpp -- check hello-mcpp) + echo "$out" | grep -q '"event":"stage","name":"compile"' \ + && echo "$out" | grep -q '"event":"verdict"' \ + && echo "$out" | grep -q '"outcome":"fail"' \ + || { echo "E2E(provider) FAIL: check hello-mcpp 的事件流不完整:"; echo "$out" | head -3; return 1; } + echo "E2E(provider) 协议冒烟: $n 个练习,check 事件流完整 ✓" +} -# 防空转:一个参考答案都没验到时必须红,而不是「0 失败」蒙混过关。 -# 旧 CI 就是这么绿了很久的 —— 它只挑 -ref 目标,而 solutions/ 早被注释掉, -# 循环一次都没进,job 照样退出 0。 -if [ "$pass" -eq 0 ]; then - echo "FAIL: 没有验证到任何参考答案 —— 测试本身失效了,不是「全部通过」" - exit 1 -fi +rc=0 +provider_smoke || rc=1 +if [[ "$MODE" == "zh" || "$MODE" == "all" ]]; then run_lang "" zh || rc=1; fi +if [[ "$MODE" == "en" || "$MODE" == "all" ]]; then run_lang "en/" en || rc=1; fi -[ "$fail" -eq 0 ] +if [[ "$rc" == 0 ]]; then echo "E2E: ALL GREEN"; else echo "E2E: FAILED"; fi +exit "$rc" From 180c64f922071d7689341202c29137d07e2e55d3 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 22:50:58 +0800 Subject: [PATCH 15/25] =?UTF-8?q?docs:=20book/=E6=92=B0=E7=A8=BF=E6=8A=80?= =?UTF-8?q?=E8=83=BD/CI/gitignore=20=E5=85=A8=E9=9D=A2=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E3=80=8C=E7=BB=83=E4=B9=A0=E5=8D=B3=E6=B5=8B=E8=AF=95=E3=80=8D?= =?UTF-8?q?=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/d2mcpp-authoring/SKILL.md | 64 +++++++++-------- .../d2mcpp-authoring/assets/exercise.cpp | 16 ++--- .../d2mcpp-authoring/assets/solution.cpp | 17 +++-- .../d2mcpp-authoring/references/anatomy.md | 68 ++++++++++--------- .github/workflows/dslings-ref-ci.yml | 41 ++++++----- .gitignore | 5 +- book/src/README.md | 2 +- book/src/base/chapter_0.md | 2 +- book/src/base/chapter_1.md | 43 ++++++------ book/src/cpp11/00-auto-and-decltype.md | 14 ++-- book/src/cpp11/01-default-and-delete.md | 8 +-- book/src/cpp11/02-final-and-override.md | 8 +-- book/src/cpp11/03-trailing-return-type.md | 4 +- book/src/cpp11/04-rvalue-references.md | 4 +- book/src/cpp11/05-move-semantics.md | 8 +-- book/src/cpp11/06-scoped-enums.md | 6 +- book/src/cpp11/07-constexpr.md | 6 +- book/src/cpp11/08-literal-type.md | 6 +- book/src/cpp11/09-list-initialization.md | 2 +- book/src/cpp11/10-delegating-constructors.md | 2 +- book/src/cpp11/11-inherited-constructors.md | 8 +-- book/src/cpp11/12-nullptr.md | 8 +-- book/src/cpp11/13-long-long.md | 6 +- book/src/cpp11/14-type-alias.md | 10 +-- book/src/cpp11/15-variadic-templates.md | 6 +- book/src/cpp11/16-generalized-unions.md | 8 +-- book/src/cpp11/17-pod-type.md | 8 +-- book/src/cpp14/00-generic-lambdas.md | 6 +- 28 files changed, 206 insertions(+), 180 deletions(-) diff --git a/.agents/skills/d2mcpp-authoring/SKILL.md b/.agents/skills/d2mcpp-authoring/SKILL.md index 78162d3..79ef9c4 100644 --- a/.agents/skills/d2mcpp-authoring/SKILL.md +++ b/.agents/skills/d2mcpp-authoring/SKILL.md @@ -3,20 +3,20 @@ name: d2mcpp-authoring description: >- Authoring conventions, design principles, and file formats for the d2mcpp (D2X) Modern C++ tutorial project. Use this whenever you add or edit a C++ - language-feature lesson — a book chapter, a dslings exercise, a reference + language-feature lesson — a book chapter, an exercise, a reference solution, or when registering them in SUMMARY/changelog. Trigger it for - any request like "add a chapter for X", "write a dslings exercise for fold + any request like "add a chapter for X", "write an exercise for fold expressions", "add the constexpr lesson", "fill the cpp14 section", or - anything touching book/src, book/en/src, dslings/, or solutions/. Even small - edits should follow these conventions so the bilingual + book/code/checker - artifacts stay in sync. + anything touching book/src, book/en/src, cpp*/tests/, en/, or solutions/. + Even small edits should follow these conventions so the bilingual + + book/code/checker artifacts stay in sync. --- # d2mcpp (D2X) Authoring Conventions d2mcpp teaches **Modern C++ core language features** through a tightly coupled -quartet: **Book (mdBook) + Code (dslings exercises) + Solution + Auto-checker -(`d2x checker`)**, all of it **bilingual (zh + en)**. A "lesson" is never one +quartet: **Book (mdBook) + Code (exercises-as-tests) + Solution + Auto-checker +(`d2x checker` / native `mcpp test`)**, all of it **bilingual (zh + en)**. A "lesson" is never one file — it is a coordinated set across several directories. The cardinal sin here is producing a half-set (a chapter with no exercise, a zh file with no en counterpart, an exercise missing from solutions/). This skill exists so every @@ -46,10 +46,11 @@ Honor that axis: forward — they are not chapter-worthy on their own. Examples: `auto x{1}` deduction change, `std::is_pod` deprecation pointer, guaranteed copy elision remark, P0136 inheriting-constructor semantics fix. -- **Compile each chapter at its introduction standard.** Do not globally pin a - newer standard. If one exercise genuinely needs a later standard, make it an - **explicit, commented exception** via a `// d2x:cxxflags:` header directive - in the exercise file — never a bare `TODO` hack. +- **All exercises compile as c++23** — the `cppNN/` directory denotes when the + feature was *introduced*, not the compile flags. Teach the introduction-era + shape through content and assertions (an untested teaching point disappears + silently). If one exercise needs special flags, declare a per-glob entry in + the member's `/mcpp.toml` `[build].flags` — never a bare `TODO` hack. Background and the full per-feature evolution analysis live in `.agents/docs/2026-06-08-cpp11-feature-evolution-and-cxx20-baseline.md`. Consult @@ -68,10 +69,10 @@ When adding a feature numbered `NN` with slug `topic` (e.g. `06-scoped-enums`): 1. `book/src//NN-topic.md` — zh chapter 2. `book/en/src//NN-topic.md` — en chapter (translate prose, keep code identical) -3. `dslings//NN-topic-K.cpp` — one or more exercises (`K` = 0,1,2…), zh comments -4. `dslings/en//NN-topic-K.cpp` — en exercises (translate comments only) -5. `solutions//NN-topic-K.cpp` — reference solution per exercise -6. (nothing to register — exercises are discovered by directory convention) +3. `/tests/NN-topic/K.cpp` — one or more exercises (`K` = 0,1,2…), zh comments +4. `en//tests/NN-topic/K.cpp` — en exercises (translate comments only) +5. `solutions//NN-topic/K.cpp` — reference solution per exercise (zh/en 共用) +6. (nothing to register — exercises are tests, discovered by directory convention) 8. Add the chapter line to **both** `book/src/SUMMARY.md` and `book/en/src/SUMMARY.md` 9. Add a changelog entry to **both** `book/src/changelog.md` and `book/en/src/changelog.md` @@ -81,9 +82,9 @@ exact registration snippets and SUMMARY/changelog line formats. ## Naming and numbering - Chapter/exercise prefix is a **two-digit** sequence `NN` within the standard - section; slug is **kebab-case** and matches book + dslings + solutions. -- Exercises split into `-0`, `-1`, … by sub-topic; a single-exercise chapter has - no numeric suffix on the dslings target name but the file is still `NN-topic.cpp`. + section; slug is **kebab-case** and matches book + tests + solutions. +- Exercises split into `0.cpp`, `1.cpp`, … by sub-topic inside the chapter + directory `/tests/NN-topic/`; a single-exercise chapter is just `0.cpp`. - The **`d2x checker `** name is the slug (and `-1`, `-2` for later exercises) — it omits the `NN-` prefix. Keep checker names, file names, and book references mutually consistent. @@ -142,7 +143,7 @@ the pinned upstream commit and how to refresh it). Rules: - Every chapter should carry this section. If a feature genuinely has no representative usage in the STL implementation, omit it and state why in the PR. -## dslings exercise format +## Exercise format (exercises are tests) Use `assets/exercise.cpp`. Essentials: @@ -150,16 +151,23 @@ Use `assets/exercise.cpp`. Essentials: `Exercise/练习:` line (` | NN - topic | 中文小标题`), `Tips/提示:`, `Docs/文档:` (cppreference), and the `Auto-Checker/自动检测命令:` with `d2x checker `. -- `#include ` then any std headers. +- `import std;` + `import d2x.harness;` — no `#include` unless the lesson + needs a macro from a header (e.g. `NULL` teaching needs ``; put the + include *before* the imports with a comment saying why). - `main()` seeded with **intentional errors** the learner fixes, each flagged by a numbered inline comment (`// 1.…`, `// 2.…`) telling them what to do. -- Checkpoint macros from `d2x/cpp/common.hpp`: - - `d2x_assert(cond)` / `d2x_assert_eq(a, b)` — runtime checkpoints; learners - must make them pass by fixing code, **not** by deleting the checkpoint. - - `D2X_YOUR_ANSWER` — a fill-in placeholder the learner replaces. - - `D2X_WAIT` — separates exercises; learner deletes/comments it to advance. +- Conventions (harness is `import d2x.harness;`, no macros): + - `d2x::check(cond, "原文")` / `d2x::check_eq(a, b, "a == b")` — runtime + checkpoints; the third argument is the expression text shown to learners + (c++23 has no reflection to capture it automatically — always pass it). + Learners must make checks pass by fixing code, **not** by deleting them. + - `D2X_YOUR_ANSWER` — the fill-in placeholder. It is **not defined anywhere**; + the compiler error pointing at it *is* the feature. Never define it. + - `d2x::wait()` — separates exercises; learner deletes it to advance. Put exactly one at the end of each exercise's checkpoints. - - `D2X_DONT_DELETE_THIS` — guards lines that must not be removed. + - `d2x::dont_delete_this(expr)` — guards expressions that must not be removed. +- A pure observation exercise may be **zero-dependency plain C++** (no imports) — + judged by exit code alone, copy-pasteable into Compiler Explorer. - The **en exercise translates only the comment prose**; the code stays byte-for-byte identical to the zh exercise so checker behavior matches. @@ -170,9 +178,9 @@ Use `assets/solution.cpp`. It is the corrected exercise with: - A solution header: `reference solution for: `, plus the note that it is **for CI/maintainers only, not a tutorial entry**, and a pointer back to the exercise file. -- `D2X_WAIT` removed; all checkpoints passing; unused locals silenced with +- `d2x::wait()` removed; all checkpoints passing; unused locals silenced with `(void)var;` rather than left to warn. -- Same `#include`s and structure as the exercise. +- Same imports and structure as the exercise. ## Definition of done diff --git a/.agents/skills/d2mcpp-authoring/assets/exercise.cpp b/.agents/skills/d2mcpp-authoring/assets/exercise.cpp index 0ca3d10..aecfa9d 100644 --- a/.agents/skills/d2mcpp-authoring/assets/exercise.cpp +++ b/.agents/skills/d2mcpp-authoring/assets/exercise.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings//NN-topic-K.cpp +// file: /tests/NN-topic/K.cpp // // Exercise/练习: | NN - topic | <中文小标题> // @@ -14,23 +14,23 @@ // d2x checker // -#include - -#include +import std; +import d2x.harness; int main() { // 1.<要修复的点>: <说明,例如:修复这个编译错误 / 改成正确类型> int a = 1.1; // <提示> - d2x_assert_eq(a, 1.1); // 运行时检查点:需修复代码使其通过(不要删除检查点) + d2x::check_eq(a, 1.1, "a == 1.1"); // 运行时检查点:需修复代码使其通过(不要删除检查点) - // 2.<填空点>: 用正确的代码替换 D2X_YOUR_ANSWER + // 2.<填空点>: 用正确的代码替换 D2X_YOUR_ANSWER(它不是宏,只是个没人定义的名字, + // 编译器会指着它报错 —— 那就是要填的地方) D2X_YOUR_ANSWER b = a; // <提示:给 b 一个合适的类型> - d2x_assert_eq(b, 1); + d2x::check_eq(b, 1, "b == 1"); - D2X_WAIT // 删除或注释掉本宏进入下一个练习 + d2x::wait(); // 路障:读完本课删除这一行,进入下一个练习 return 0; } diff --git a/.agents/skills/d2mcpp-authoring/assets/solution.cpp b/.agents/skills/d2mcpp-authoring/assets/solution.cpp index 65857f3..acfaf31 100644 --- a/.agents/skills/d2mcpp-authoring/assets/solution.cpp +++ b/.agents/skills/d2mcpp-authoring/assets/solution.cpp @@ -1,29 +1,28 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// reference solution for: dslings//NN-topic-K.cpp +// reference solution for: /tests/NN-topic/K.cpp // // 用途: 仅给 CI 与维护者参考使用,不是教程入口。 -// 教程练习入口: dslings//NN-topic-K.cpp +// 教程练习入口: /tests/NN-topic/K.cpp // -#include - -#include +import std; +import d2x.harness; int main() { // 1.<已修复>: 说明保持与练习一致,但代码为正确版本 double a = 1.1; - d2x_assert_eq(a, 1.1); + d2x::check_eq(a, 1.1, "a == 1.1"); // 2.<已填空> - double b = a; + int b = a; (void)b; // 用 (void) 抑制未使用告警,不要留下会触发 warning 的代码 - d2x_assert_eq(b, 1.1); + d2x::check_eq(b, 1, "b == 1"); - // 注意: 参考答案中删除 D2X_WAIT,且所有检查点均通过 + // 注意: 参考答案中删除 d2x::wait(),且所有检查点均通过 return 0; } diff --git a/.agents/skills/d2mcpp-authoring/references/anatomy.md b/.agents/skills/d2mcpp-authoring/references/anatomy.md index da7eabf..0c31f66 100644 --- a/.agents/skills/d2mcpp-authoring/references/anatomy.md +++ b/.agents/skills/d2mcpp-authoring/references/anatomy.md @@ -16,10 +16,10 @@ book/en/src/SUMMARY.md # en TOC (register here) book/src/changelog.md # zh changelog (add entry) book/en/src/changelog.md # en changelog (add entry) -dslings//NN-topic-K.cpp # zh exercise(s) — no registration needed -dslings/en//NN-topic-K.cpp # en exercise(s) — no registration needed +/tests/NN-topic/K.cpp # zh exercise(s) — no registration needed +en//tests/NN-topic/K.cpp # en exercise(s) — no registration needed -solutions//NN-topic-K.cpp # reference solution(s) +solutions//NN-topic/K.cpp # reference solution(s), zh/en 共用 ``` Repo-level shared basis (NOT per-lesson, created once, never edited by hand): @@ -35,50 +35,53 @@ The `## 二、真实案例` section of every chapter links into `msvc-stl/` for verbatim STL excerpt; you do not add files here per lesson — only refresh the snapshot via `msvc-stl/SOURCE.md` when needed. -A new `` section that does not exist yet needs only: a `dslings//` -and `dslings/en//` directory, a `solutions//` directory, and the -section heading in both `SUMMARY.md` files (e.g. `# C++14核心语言特性` / -`# C++14 Core Language Features`). No build-file wiring — the Provider -discovers `dslings//` by directory convention. +A new `` section that does not exist yet needs: a `/mcpp.toml` (copy +`cpp14/mcpp.toml`, change the name), `/tests/` + `en//tests/` + +`solutions//` directories, the member added to the root `mcpp.toml` +workspace list (both `` and `en/`), and the section heading in both +`SUMMARY.md` files (e.g. `# C++14核心语言特性` / `# C++14 Core Language +Features`). ## Exercise registration — there is none -Exercises are discovered by **directory convention**, not registered anywhere. -Dropping `dslings//NN-topic-K.cpp` into place is the whole job: the d2x -Provider (`d2x/buildtools/mcpp/`) derives the exercise id, order and chapter -from the path, and generates the mcpp manifests under `.d2x/build/`. +**Exercises are tests.** Each `/` is a real mcpp project and exercises are +its `tests/`; dropping `/tests/NN-topic/K.cpp` into place is the whole job. +`mcpp test -p ` runs them natively (the report is the progress table), and +the d2x Provider (`d2x/buildtools/mcpp/`) derives the exercise id, order and +chapter from the same path — one chain, no generated manifests, nothing under +`.d2x/` but learner progress. This is deliberate. rustlings' most expensive lesson (PR #1355) was the Rust edition living in *two* places — the `rustc` args and `rust-project.json` — which drifted and caused bugs. Any standalone registration file is a second -source of truth. Here the only truths are the directory layout and the exercise -file's own header comment. +source of truth. Here the only truth is the directory layout. ### Per-exercise compile flags -When a lesson needs non-default flags, declare them in the exercise file's own -header — near the code they affect, impossible to drift: +When a lesson needs non-default flags, declare a per-glob entry in the member's +`/mcpp.toml` — `[build].flags` globs cover test TUs: -```cpp -// d2x:cxxflags: -O0 -fno-elide-constructors +```toml +[build] +flags = [ + { glob = "tests/NN-topic/*.cpp", cxxflags = ["-O0", "-fno-elide-constructors"] }, +] ``` -Real examples in the repo: `04-rvalue-references` (observe moves — but note -C++17 guaranteed elision means `-fno-elide-constructors` can no longer force -prvalue materialisation, so this lesson needs rewriting) and `07-constexpr-0` -(`-Wpedantic -Werror` to make the VLA an error). +(No current exercise needs this; the capability exists for lessons that teach +observation-sensitive behavior.) ### C++ standard -All exercises compile as **c++23**. mcpp currently rejects `c++11/14/17/20` -(`src/manifest/types.cppm` whitelist) and the project chose not to patch mcpp -upstream. The `cppNN/` directories therefore denote *when a feature was -introduced* — they no longer change compile flags. +All exercises compile as **c++23** (`standard` in each member's `mcpp.toml`). +The `cppNN/` directories denote *when a feature was introduced* — they do not +change compile flags. ## Solution registration — there is none either -Drop the file at `solutions//NN-topic-K.cpp`. CI swaps it over the exercise -and asserts it passes; see `d2x/buildtools/mcpp/tests/e2e.sh`. +Drop the file at `solutions//NN-topic/K.cpp` (zh/en share one solution). +CI swaps it over the exercise and asserts it passes; see +`d2x/buildtools/mcpp/tests/e2e.sh`. ## SUMMARY registration @@ -107,11 +110,14 @@ to an absolute `YYYY/MM/DD`. Run from the project root; report real output, do not assert success blind: ```bash -# check a single exercise through the same path `d2x checker` uses +# run the exercise natively (fastest loop while authoring) +mcpp test -p NN-topic + +# check it through the Provider path (exactly what `d2x checker` consumes) mcpp run -q -p d2x/buildtools/mcpp -- check cppNN-NN-topic-0 -# or validate every exercise + solution at once -bash d2x/buildtools/mcpp/tests/e2e.sh +# or validate every exercise + solution at once (zh + en) +bash d2x/buildtools/mcpp/tests/e2e.sh all # drive the auto-checker against the exercise (expects the unsolved exercise to fail, # the solution to pass) — name omits the NN- prefix diff --git a/.github/workflows/dslings-ref-ci.yml b/.github/workflows/dslings-ref-ci.yml index 20fcf35..4579be9 100644 --- a/.github/workflows/dslings-ref-ci.yml +++ b/.github/workflows/dslings-ref-ci.yml @@ -1,20 +1,24 @@ -name: Validate dslings exercises and reference solutions +name: Validate exercises and reference solutions # 断言两件事,缺一不可: # 1. 每个练习「未完成时」不通过 —— 否则学员会被直接跳过,练习形同虚设 # 2. 每个参考答案「放进去后」通过 —— 否则参考答案本身是错的 # -# 走的是 `d2x checker` 内部使用的同一条 Provider 路径(d2x/buildtools/mcpp), -# 所以 CI 绿灯等价于学员本地能跑通,而不是另一条平行的构建路径。 +# 练习即测试:e2e 走 `mcpp test`,与 `d2x checker` 内部的 Provider 是同一条 +# 链路,所以 CI 绿灯等价于学员本地能跑通,而不是另一条平行的构建路径。 # -# 旧版本只挑 `-ref` 结尾的 xmake target,而 solutions/ 在 xmake.lua 里被注释掉, -# grep 返回空、循环全跳过、job 退出 0 —— 实际校验零个目标。 +# ⚠️ 依赖 mcpp「测试能力批次」(逐测试隔离 / 过滤 / --message-format json / +# tests 吃 [build].flags,本地分支 feat/test-isolation-json)。在 mcpp 发布 +# 携带这些能力的版本并更新安装 pin 之前,本 workflow 会失败 —— 这是预期的。 on: push: branches: ["main"] paths: - - "dslings/**" + - "harness/**" + - "intro/**" + - "cpp*/**" + - "en/**" - "solutions/**" - "d2x/**" - "mcpp.toml" @@ -22,7 +26,10 @@ on: - ".github/workflows/dslings-ref-ci.yml" pull_request: paths: - - "dslings/**" + - "harness/**" + - "intro/**" + - "cpp*/**" + - "en/**" - "solutions/**" - "d2x/**" - "mcpp.toml" @@ -51,6 +58,7 @@ jobs: echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" # 按 .xlings.json 安装 mcpp。mcpp 自带工具链沙箱,无需另装 gcc。 + # 需要携带「测试能力批次」的 mcpp 版本(见文件头注释)。 - name: Install mcpp run: | set -eu @@ -60,21 +68,22 @@ jobs: - name: Build d2x Provider run: mcpp build -p d2x/buildtools/mcpp - - name: Validate exercises and solutions - run: bash d2x/buildtools/mcpp/tests/e2e.sh + - name: Validate exercises and solutions (zh + en) + run: bash d2x/buildtools/mcpp/tests/e2e.sh all - - name: Verify dslings ↔ solutions filename parity + - name: Verify exercises ↔ solutions filename parity run: | set -eu # en/ 镜像跳过 —— 参考答案与语言无关,两边共用同一份 missing=0 - while IFS= read -r f; do - rel="${f#dslings/}" - if [ ! -f "solutions/$rel" ]; then - echo "::warning::missing solutions/$rel (paired with dslings/$rel)" + for f in $(find intro/tests cpp*/tests -name '*.cpp'); do + std="${f%%/*}" + rel="${f#*/tests/}" + if [ ! -f "solutions/$std/$rel" ]; then + echo "::warning::missing solutions/$std/$rel (paired with $f)" missing=$((missing+1)) fi - done < <(find dslings -name '*.cpp' -not -path 'dslings/en/*') + done if [ "$missing" -gt 0 ]; then - echo "::warning::$missing 个练习尚无参考答案。铺开阶段仅作提示;cpp11 覆盖完整后收紧为错误。" + echo "::warning::$missing 个练习尚无参考答案。铺开阶段仅作提示;覆盖完整后收紧为错误。" fi diff --git a/.gitignore b/.gitignore index 3ec6cc6..fe0b345 100644 --- a/.gitignore +++ b/.gitignore @@ -44,10 +44,9 @@ __pycache__ llm.config.xlings media .cache/ -dslings/compile_commands.json -# d2x Provider 生成物(mcpp workspace 清单)与学习进度 -.d2x/build/ +# d2x 学习进度与判定侧信道 .d2x/state.json +.d2x/result.ndjson # mcpp 构建产物 target/ diff --git a/book/src/README.md b/book/src/README.md index 02c350e..831d3fd 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -17,7 +17,7 @@ [📚Book]: https://mcpp-community.github.io/d2mcpp [🎥Video]: https://www.bilibili.com/video/BV182MtzPEiX -[⌨️Code]: https://github.com/mcpp-community/d2mcpp/tree/main/dslings +[⌨️Code]: https://github.com/mcpp-community/d2mcpp/tree/main/cpp11/tests [👥X]: https://forum.d2learn.org/category/20 ## 目标 diff --git a/book/src/base/chapter_0.md b/book/src/base/chapter_0.md index a4b9d39..f925f63 100644 --- a/book/src/base/chapter_0.md +++ b/book/src/base/chapter_0.md @@ -12,7 +12,7 @@ d2mcpp是一个开源、强调代码实践的`现代C++核心语言特性`教程 - [Book: 在线电子书](https://mcpp-community.github.io/d2mcpp) - [Video: 讲解视频](https://space.bilibili.com/65858958/lists/5208246?type=season) -- [Code: 练习代码](https://github.com/mcpp-community/d2mcpp/tree/main/dslings) +- [Code: 练习代码](https://github.com/mcpp-community/d2mcpp/tree/main/cpp11/tests) - [X: mcpp论坛](https://forum.d2learn.org/category/20) ## 语言支持 diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index 81920aa..4f2ec6c 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -81,26 +81,31 @@ d2x update ```cpp // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/hello-mcpp.cpp +// file: intro/tests/hello-mcpp.cpp // // Exercise/练习: 自动化代码练习使用教学 // // Tips/提示: -// 该项目是使用xlings工具搭建的自动化代码练习项目, 通过在项目根目录下 -// 执行 d2x checker 进入"编译器驱动开发模式"的练习代码自动检测. -// 你需要根据控制台的报错和提示信息, 修改代码中的错误. 当修复所有编译错误和 -// 运行时检查点后, 你可以删除或注释掉代码中的 D2X_WAIT 宏, 会自动进入下一个练习. +// 这是一个「练习即测试」的自动化代码练习项目。两种玩法任选: // -// - D2X_WAIT: 该宏用于隔离不同练习, 你可以删除或注释掉该宏, 进入下一个练习. -// - d2x_assert_eq: 该宏用于运行时检查点, 你需要修复代码中的错误, 使得所有 -// - D2X_YOUR_ANSWER: 该宏用于提示你需要修改的代码, 一般用于代码填空(即用正确的代码替换这个宏) +// d2x checker 闯关模式: 自动检测、通过即进入下一题 +// mcpp test -p intro 原生模式: 直接用 mcpp 跑, 测试结果就是进度表 +// +// 你需要根据控制台的报错和提示信息修改代码。约定只有三个: +// +// - D2X_YOUR_ANSWER: 填空占位符 —— 用正确的代码替换它。它不是宏, +// 只是一个没人定义的名字, 所以编译器会指着它报错, 那就是要填的地方 +// - d2x::check / d2x::check_eq: 运行时检查点, 修复代码让所有检查通过 +// (不能直接删除检查点) +// - d2x::wait(): 练习之间的路障 —— 读完这一课, 删掉它才算真正完成 // // Auto-Checker/自动检测命令: // // d2x checker hello-mcpp // -#include +import std; +import d2x.harness; // 修改代码时可以观察到控制台"实时"的变化 @@ -110,13 +115,13 @@ int main() { int a = 1.1; // 1.修复这个运行时错误, 修改int为double, 通过检查 - d2x_assert_eq(a, 1.1); // 2.运行时检查点, 需要修复代码通过所有检查点(不能直接删除检查点代码) + d2x::check_eq(a, 1.1, "a == 1.1"); // 2.运行时检查点, 需要修复代码通过所有检查点(不能直接删除检查点代码) D2X_YOUR_ANSWER b = a; // 3.修复这个编译错误, 给b一个合适的类型 - d2x_assert_eq(b, 1); // 4.运行时检查点2 + d2x::check_eq(b, 1, "b == 1"); // 4.运行时检查点2 - D2X_WAIT // 5.删除或注释掉这个宏, 进入下一个练习(项目正式代码练习) + d2x::wait(); // 5.删除或注释掉这一行, 进入下一个练习(项目正式代码练习) return 0; } @@ -125,23 +130,23 @@ int main() { **控制台输出及解释** ```bash -🌏Progress: [>----------] 0/10 -->> 显示当前的练习进度 +🌏Progress: [>----------] 0/52 -->> 显示当前的练习进度 -[Target: 00-0-hello-mcpp] - normal -->> 当前的练习名 +[Exercise: hello-mcpp] -->> 当前的练习名 -❌ Error: Compilation/Running failed for dslings/hello-mcpp.cpp -->> 显示检测状态 +❌ Error: Compilation/Running failed for intro/tests/hello-mcpp.cpp -->> 显示检测状态 The code exist some error! ----------C-Output--------- - 编译器输出信息 -[HONLY LOGW]: main: dslings/hello-mcpp.cpp:24 - ❌ | a == 1.1 (1 == 1.100000) -->> 错误提示及位置(24行) -[HONLY LOGW]: main: dslings/hello-mcpp.cpp:26 - 🥳 Delete the D2X_WAIT to continue... +---------Output--------- - 编译/运行输出信息 +[HONLY LOGW]: intro/tests/hello-mcpp.cpp:41 - ❌ | a == 1.1 (1 == 1.1) -->> 错误提示及位置(41行) +[HONLY LOGW]: intro/tests/hello-mcpp.cpp:47 - 🥳 Delete the d2x::wait() to continue... AI-Tips-Config: https://xlings.d2learn.org/documents/d2x/intro.html -->> AI提示(需要配置大模型的key, 可不使用) ---------E-Files--------- -dslings/hello-mcpp.cpp -->> 当前检测的文件 +intro/tests/hello-mcpp.cpp -->> 当前检测的文件 ------------------------- Homepage: https://github.com/openxlings/xlings diff --git a/book/src/cpp11/00-auto-and-decltype.md b/book/src/cpp11/00-auto-and-decltype.md index 50b8586..0ab8719 100644 --- a/book/src/cpp11/00-auto-and-decltype.md +++ b/book/src/cpp11/00-auto-and-decltype.md @@ -12,7 +12,7 @@ auto 和 decltype 是C++11引入的强有力的**类型自动推导**工具. 不 | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/00-auto-and-decltype.md) | [视频解读](https://www.bilibili.com/video/BV1EzJs6HEf7) / [练习讲解](https://www.bilibili.com/video/BV1xkdYYUEyH) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp) | | +| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/00-auto-and-decltype.md) | [视频解读](https://www.bilibili.com/video/BV1EzJs6HEf7) / [练习讲解](https://www.bilibili.com/video/BV1xkdYYUEyH) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/0.cpp) | | **为什么引入?** @@ -252,12 +252,12 @@ decltype( (b) ) // 推导结果是 int & ### 练习代码主题 -- 0 - [声明定义](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp) -- 1 - [表达式类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-1.cpp) -- 2 - [复杂类型推导 - 迭代器 / 函数](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-2.cpp) -- 3 - [函数返回值类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-3.cpp) -- 4 - [类/结构体成员类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-4.cpp) -- 5 - [const 与引用的剥离和保留](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-5.cpp) +- 0 - [声明定义](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/0.cpp) +- 1 - [表达式类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/1.cpp) +- 2 - [复杂类型推导 - 迭代器 / 函数](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/2.cpp) +- 3 - [函数返回值类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/3.cpp) +- 4 - [类/结构体成员类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/4.cpp) +- 5 - [const 与引用的剥离和保留](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/5.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/01-default-and-delete.md b/book/src/cpp11/01-default-and-delete.md index 57b1a31..d49b2d2 100644 --- a/book/src/cpp11/01-default-and-delete.md +++ b/book/src/cpp11/01-default-and-delete.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/function#Deleted_functions) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/01-default-and-delete.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/01-default-and-delete-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/function#Deleted_functions) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/01-default-and-delete.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/01-default-and-delete/0.cpp) | | **为什么引入?** @@ -217,9 +217,9 @@ only_int(1.0); // 错误: 调用 deleted function ### 练习代码主题 -- 0 - [显式指定构造函数生成行为](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/01-default-and-delete-0.cpp) -- 1 - [实现不可拷贝但可移动的 UniquePtr](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/01-default-and-delete-1.cpp) -- 2 - [用 = delete 屏蔽特定参数类型的重载](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/01-default-and-delete-2.cpp) +- 0 - [显式指定构造函数生成行为](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/01-default-and-delete/0.cpp) +- 1 - [实现不可拷贝但可移动的 UniquePtr](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/01-default-and-delete/1.cpp) +- 2 - [用 = delete 屏蔽特定参数类型的重载](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/01-default-and-delete/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/02-final-and-override.md b/book/src/cpp11/02-final-and-override.md index fa40a7a..e6fe2ee 100644 --- a/book/src/cpp11/02-final-and-override.md +++ b/book/src/cpp11/02-final-and-override.md @@ -12,7 +12,7 @@ final 和 override 是 C++11 引入的两个 **上下文相关标识符**, 用 | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-final](https://en.cppreference.com/w/cpp/language/final) / [cppreference-override](https://en.cppreference.com/w/cpp/language/override) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/02-final-and-override.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/02-final-and-override-0.cpp) | | +| [cppreference-final](https://en.cppreference.com/w/cpp/language/final) / [cppreference-override](https://en.cppreference.com/w/cpp/language/override) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/02-final-and-override.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/02-final-and-override/0.cpp) | | **为什么引入?** @@ -174,9 +174,9 @@ struct B : A { ### 练习代码主题 -- 0 - [熟悉 override 的使用](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/02-final-and-override-0.cpp) -- 1 - [熟悉 final 的使用](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/02-final-and-override-1.cpp) -- 2 - [final + 模板方法 - AudioPlayer / WAV / MP3 / OGG](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/02-final-and-override-2.cpp) +- 0 - [熟悉 override 的使用](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/02-final-and-override/0.cpp) +- 1 - [熟悉 final 的使用](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/02-final-and-override/1.cpp) +- 2 - [final + 模板方法 - AudioPlayer / WAV / MP3 / OGG](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/02-final-and-override/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/03-trailing-return-type.md b/book/src/cpp11/03-trailing-return-type.md index 9fba383..08c0889 100644 --- a/book/src/cpp11/03-trailing-return-type.md +++ b/book/src/cpp11/03-trailing-return-type.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/function) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/03-trailing-return-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/03-trailing-return-type.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/function) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/03-trailing-return-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/03-trailing-return-type/0.cpp) | | **为什么引入?** @@ -166,7 +166,7 @@ auto func() -> int(*)[3]; // ok: 返回数组指针 ### 练习代码主题 -- 0 - [熟悉尾置返回类型 - 普通函数 / 模板 / lambda 三种形态](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/03-trailing-return-type.cpp) +- 0 - [熟悉尾置返回类型 - 普通函数 / 模板 / lambda 三种形态](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/03-trailing-return-type/0.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/04-rvalue-references.md b/book/src/cpp11/04-rvalue-references.md index 03d3b13..664b2fc 100644 --- a/book/src/cpp11/04-rvalue-references.md +++ b/book/src/cpp11/04-rvalue-references.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/reference) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/04-rvalue-references.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/04-rvalue-references.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/reference) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/04-rvalue-references.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/04-rvalue-references/0.cpp) | | **为什么引入?** @@ -156,7 +156,7 @@ template void h(T&&); // 万能引用, 左右值都接受 ### 练习代码主题 -- 0 - [使用右值引用延长临时对象生命周期并修改其值](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/04-rvalue-references.cpp) +- 0 - [使用右值引用延长临时对象生命周期并修改其值](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/04-rvalue-references/0.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/05-move-semantics.md b/book/src/cpp11/05-move-semantics.md index 6f18793..423c47e 100644 --- a/book/src/cpp11/05-move-semantics.md +++ b/book/src/cpp11/05-move-semantics.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-move](https://en.cppreference.com/w/cpp/utility/move) / [cppreference-move-ctor](https://en.cppreference.com/w/cpp/language/move_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/05-move-semantics.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/05-move-semantics-0.cpp) | | +| [cppreference-move](https://en.cppreference.com/w/cpp/utility/move) / [cppreference-move-ctor](https://en.cppreference.com/w/cpp/language/move_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/05-move-semantics.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/05-move-semantics/0.cpp) | | **为什么引入?** @@ -187,9 +187,9 @@ Buffer bad() { ### 练习代码主题 -- 0 - [移动构造与触发时机 - 让 buff 传递只做一次资源分配](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/05-move-semantics-0.cpp) -- 1 - [移动赋值与触发时机 - 临时对象 / 中间对象 / 显式 std::move 三种场景](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/05-move-semantics-1.cpp) -- 2 - [移动的是资源而不是对象 - 对比对象地址和 data 指针](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/05-move-semantics-2.cpp) +- 0 - [移动构造与触发时机 - 让 buff 传递只做一次资源分配](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/05-move-semantics/0.cpp) +- 1 - [移动赋值与触发时机 - 临时对象 / 中间对象 / 显式 std::move 三种场景](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/05-move-semantics/1.cpp) +- 2 - [移动的是资源而不是对象 - 对比对象地址和 data 指针](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/05-move-semantics/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/06-scoped-enums.md b/book/src/cpp11/06-scoped-enums.md index 99c9066..3e0bbc9 100644 --- a/book/src/cpp11/06-scoped-enums.md +++ b/book/src/cpp11/06-scoped-enums.md @@ -12,7 +12,7 @@ scoped enum (`enum class` / `enum struct`) 是 C++11 引入的强类型枚举, | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/enum) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/06-scoped-enums.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/06-scoped-enums-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/enum) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/06-scoped-enums.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/06-scoped-enums/0.cpp) | | **为什么引入?** @@ -204,8 +204,8 @@ constexpr Perm operator|(Perm a, Perm b) { ### 练习代码主题 -- 0 - [传统 enum 的潜在问题 - 名字冲突与隐式比较](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/06-scoped-enums-0.cpp) -- 1 - [scoped enum 基本用法 - 作用域 / 类型安全 / 底层类型](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/06-scoped-enums-1.cpp) +- 0 - [传统 enum 的潜在问题 - 名字冲突与隐式比较](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/06-scoped-enums/0.cpp) +- 1 - [scoped enum 基本用法 - 作用域 / 类型安全 / 底层类型](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/06-scoped-enums/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/07-constexpr.md b/book/src/cpp11/07-constexpr.md index beee2e8..8b70b32 100644 --- a/book/src/cpp11/07-constexpr.md +++ b/book/src/cpp11/07-constexpr.md @@ -12,7 +12,7 @@ constexpr 是 C++11 引入的关键字, 用于把"原本要等到运行期才能 | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/constexpr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/07-constexpr.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/07-constexpr-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/constexpr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/07-constexpr.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/07-constexpr/0.cpp) | | **为什么引入?** @@ -181,8 +181,8 @@ constexpr 把计算搬到编译期, 也意味着错误诊断、调试栈信息 ### 练习代码主题 -- 0 - [编译期计算基础: constexpr 和 const 的区别](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/07-constexpr-0.cpp) -- 1 - [编译期计算应用示例: factorial / Sum / mysin 自动打表](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/07-constexpr-1.cpp) +- 0 - [编译期计算基础: constexpr 和 const 的区别](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/07-constexpr/0.cpp) +- 1 - [编译期计算应用示例: factorial / Sum / mysin 自动打表](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/07-constexpr/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/08-literal-type.md b/book/src/cpp11/08-literal-type.md index d7da3ba..cc006ca 100644 --- a/book/src/cpp11/08-literal-type.md +++ b/book/src/cpp11/08-literal-type.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-LiteralType](https://en.cppreference.com/w/cpp/named_req/LiteralType) / [cppreference-user-literal](https://en.cppreference.com/w/cpp/language/user_literal) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/08-literal-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/08-literal-type-0.cpp) | | +| [cppreference-LiteralType](https://en.cppreference.com/w/cpp/named_req/LiteralType) / [cppreference-user-literal](https://en.cppreference.com/w/cpp/language/user_literal) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/08-literal-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/08-literal-type/0.cpp) | | **为什么引入?** @@ -178,8 +178,8 @@ LiteralType 要求至少有一个 **不是拷贝 / 移动** 的 constexpr 构造 ### 练习代码主题 -- 0 - [理解字面值类型的概念 - 哪些能 constexpr](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/08-literal-type-0.cpp) -- 1 - [自定义字面值类型 - 给 Vector 加 constexpr 构造](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/08-literal-type-1.cpp) +- 0 - [理解字面值类型的概念 - 哪些能 constexpr](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/08-literal-type/0.cpp) +- 1 - [自定义字面值类型 - 给 Vector 加 constexpr 构造](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/08-literal-type/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/09-list-initialization.md b/book/src/cpp11/09-list-initialization.md index 0ec452c..fbac26f 100644 --- a/book/src/cpp11/09-list-initialization.md +++ b/book/src/cpp11/09-list-initialization.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/list_initialization.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/09-list-initialization.md) | [视频解读](https://www.bilibili.com/video/BV1vKuQzkEo2) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/09-list-initialization-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/list_initialization.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/09-list-initialization.md) | [视频解读](https://www.bilibili.com/video/BV1vKuQzkEo2) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/09-list-initialization/0.cpp) | | **为什么引入?** diff --git a/book/src/cpp11/10-delegating-constructors.md b/book/src/cpp11/10-delegating-constructors.md index e4cb719..09aec06 100644 --- a/book/src/cpp11/10-delegating-constructors.md +++ b/book/src/cpp11/10-delegating-constructors.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/initializer_list.html#Delegating_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/10-delegating-constructors.md) | [视频解读](https://www.bilibili.com/video/BV1zft3zSEER) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/10-delegating-constructors-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/initializer_list.html#Delegating_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/10-delegating-constructors.md) | [视频解读](https://www.bilibili.com/video/BV1zft3zSEER) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/10-delegating-constructors/0.cpp) | | **为什么引入?** diff --git a/book/src/cpp11/11-inherited-constructors.md b/book/src/cpp11/11-inherited-constructors.md index 303e65a..0711dd7 100644 --- a/book/src/cpp11/11-inherited-constructors.md +++ b/book/src/cpp11/11-inherited-constructors.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/using_declaration.html#Inheriting_constructors) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/11-inherited-constructors.md) | [Bili](https://www.bilibili.com/video/BV1bspBzFEEC) / [Youtube](https://youtu.be/p7vbY8XUKnY?si=GZUn9GSW68aU94A6) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/11-inherited-constructors-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/using_declaration.html#Inheriting_constructors) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/11-inherited-constructors.md) | [Bili](https://www.bilibili.com/video/BV1bspBzFEEC) / [Youtube](https://youtu.be/p7vbY8XUKnY?si=GZUn9GSW68aU94A6) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/11-inherited-constructors/0.cpp) | | **为什么引入?** @@ -224,9 +224,9 @@ auto p4 = p2; // error (不能拷贝) ### 练习代码主题 -- 0 - [熟悉继承构造函数特性](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/11-inherited-constructors-0.cpp) -- 1 - [在功能扩展中的应用 - StudentTest](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/11-inherited-constructors-1.cpp) -- 2 - [在泛型模板中的应用 - NoCopy / NoMove 行为约束](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/11-inherited-constructors-2.cpp) +- 0 - [熟悉继承构造函数特性](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/11-inherited-constructors/0.cpp) +- 1 - [在功能扩展中的应用 - StudentTest](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/11-inherited-constructors/1.cpp) +- 2 - [在泛型模板中的应用 - NoCopy / NoMove 行为约束](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/11-inherited-constructors/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/12-nullptr.md b/book/src/cpp11/12-nullptr.md index 4827e5e..7a9c0aa 100644 --- a/book/src/cpp11/12-nullptr.md +++ b/book/src/cpp11/12-nullptr.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/nullptr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/12-nullptr.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/12-nullptr-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/nullptr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/12-nullptr.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/12-nullptr/0.cpp) | | **为什么引入?** @@ -193,9 +193,9 @@ bool isEmpty = (ptr == nullptr); // true ### 练习代码主题 -- 0 - [nullptr基础用法](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/12-nullptr-0.cpp) -- 1 - [nullptr的函数重载](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/12-nullptr-1.cpp) -- 2 - [nullptr在模板编程中的优势](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/12-nullptr-2.cpp) +- 0 - [nullptr基础用法](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/12-nullptr/0.cpp) +- 1 - [nullptr的函数重载](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/12-nullptr/1.cpp) +- 2 - [nullptr在模板编程中的优势](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/12-nullptr/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/13-long-long.md b/book/src/cpp11/13-long-long.md index cf47687..3854f86 100644 --- a/book/src/cpp11/13-long-long.md +++ b/book/src/cpp11/13-long-long.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/types) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/13-long-long.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/13-long-long-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/types) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/13-long-long.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/13-long-long/0.cpp) | | **为什么引入?** @@ -109,8 +109,8 @@ if (bigValue > std::numeric_limits::max() || bigValue < std::numeric_limits ### 练习代码主题 -- 0 - [long long基础用法](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/13-long-long-0.cpp) -- 1 - [long long大数应用和边界值](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/13-long-long-1.cpp) +- 0 - [long long基础用法](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/13-long-long/0.cpp) +- 1 - [long long大数应用和边界值](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/13-long-long/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/14-type-alias.md b/book/src/cpp11/14-type-alias.md index 5a78af6..5487a45 100644 --- a/book/src/cpp11/14-type-alias.md +++ b/book/src/cpp11/14-type-alias.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-type-alias](https://en.cppreference.com/w/cpp/language/type_alias) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/14-type-alias.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/14-type-alias-0.cpp) | | +| [cppreference-type-alias](https://en.cppreference.com/w/cpp/language/type_alias) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/14-type-alias.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/0.cpp) | | > 注: `using`关键字在C++11之前就已经存在, 但当时主要是作为命名空间和类成员声明来使用的 > - 声明命名空间: `using namespace std;` @@ -192,10 +192,10 @@ struct A { ### 练习代码主题 -- 0 - [基本类型别名](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/14-type-alias-0.cpp) -- 1 - [复杂类型和函数指针别名](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/14-type-alias-1.cpp) -- 2 - [别名模板基础](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/14-type-alias-2.cpp) -- 3 - [标准库中的别名模板应用](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/14-type-alias-3.cpp) +- 0 - [基本类型别名](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/0.cpp) +- 1 - [复杂类型和函数指针别名](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/1.cpp) +- 2 - [别名模板基础](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/2.cpp) +- 3 - [标准库中的别名模板应用](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/3.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/15-variadic-templates.md b/book/src/cpp11/15-variadic-templates.md index 9a259db..e33f441 100644 --- a/book/src/cpp11/15-variadic-templates.md +++ b/book/src/cpp11/15-variadic-templates.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/parameter_pack) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/15-variadic-templates.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/15-variadic-templates-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/parameter_pack) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/15-variadic-templates.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/15-variadic-templates/0.cpp) | | **为什么引入?** @@ -228,8 +228,8 @@ auto save(Args&&... args) { ### 练习代码主题 -- 0 - [可变参数模板基础 - 递归展开 print](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/15-variadic-templates-0.cpp) -- 1 - [可变参数模板求和 - sum](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/15-variadic-templates-1.cpp) +- 0 - [可变参数模板基础 - 递归展开 print](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/15-variadic-templates/0.cpp) +- 1 - [可变参数模板求和 - sum](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/15-variadic-templates/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/16-generalized-unions.md b/book/src/cpp11/16-generalized-unions.md index 3934262..a8c0fc3 100644 --- a/book/src/cpp11/16-generalized-unions.md +++ b/book/src/cpp11/16-generalized-unions.md @@ -14,7 +14,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp) | | +| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/16-generalized-unions/0.cpp) | | **为什么引入** @@ -222,9 +222,9 @@ double c = m.b; // 错误:未定义行为 ### 练习代码主题 -- 0 - [联合体默认成员初始化 - 最多一个变体成员可带默认初始化器](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp) -- 1 - [联合体包含非平凡类型及生命周期管理 - placement new 构造 / 显式析构](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-1.cpp) -- 2 - [带标签的鉴别联合体 - 用 enum + union 实现简易 variant](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-2.cpp) +- 0 - [联合体默认成员初始化 - 最多一个变体成员可带默认初始化器](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/16-generalized-unions/0.cpp) +- 1 - [联合体包含非平凡类型及生命周期管理 - placement new 构造 / 显式析构](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/16-generalized-unions/1.cpp) +- 2 - [带标签的鉴别联合体 - 用 enum + union 实现简易 variant](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/16-generalized-unions/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/17-pod-type.md b/book/src/cpp11/17-pod-type.md index 79732dc..ec582b3 100644 --- a/book/src/cpp11/17-pod-type.md +++ b/book/src/cpp11/17-pod-type.md @@ -13,7 +13,7 @@ C++ 早期标准为了描述这类“行为上接近 C struct 的类型”,引 | 书籍 | 视频 | 代码 | 交流 | | --- | --- | --- | --- | -| [cppreference-PODType](https://cppreference.com/w/cpp/named_req/PODType.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/17-pod-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/17-pod-type-0.cpp) | [论坛讨论](https://forum.d2learn.org/category/20) | +| [cppreference-PODType](https://cppreference.com/w/cpp/named_req/PODType.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/17-pod-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/17-pod-type/0.cpp) | [论坛讨论](https://forum.d2learn.org/category/20) | > 注意:从 C++20 开始,标准中的 “PODType” 概念已被标记为**弃用**。标准库更倾向于使用更细化的类别,如 `TrivialType`、`StandardLayoutType`、`ScalarType` 等来描述相关需求。 @@ -140,9 +140,9 @@ void pod_only_copy(const T& src, T& dst) { ### 练习代码主题 -- 0 - [使用类型特征判断 POD / trivial / standard layout](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/17-pod-type-0.cpp) -- 1 - [模拟按字节拷贝 POD 结构体,体会其行为](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/17-pod-type-1.cpp) -- 2 - [为 C 接口传入合适的 POD 类型数据](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/17-pod-type-2.cpp) +- 0 - [使用类型特征判断 POD / trivial / standard layout](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/17-pod-type/0.cpp) +- 1 - [模拟按字节拷贝 POD 结构体,体会其行为](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/17-pod-type/1.cpp) +- 2 - [为 C 接口传入合适的 POD 类型数据](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/17-pod-type/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp14/00-generic-lambdas.md b/book/src/cpp14/00-generic-lambdas.md index 892b39c..44d0b13 100644 --- a/book/src/cpp14/00-generic-lambdas.md +++ b/book/src/cpp14/00-generic-lambdas.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-lambda](https://en.cppreference.com/w/cpp/language/lambda) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/00-generic-lambdas.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/00-generic-lambdas-0.cpp) | | +| [cppreference-lambda](https://en.cppreference.com/w/cpp/language/lambda) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/00-generic-lambdas.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp14/tests/00-generic-lambdas/0.cpp) | | **为什么引入?** @@ -191,8 +191,8 @@ C++14 的泛型 lambda 支持参数包 — `[](auto... xs)` 可以接受任意 ### 练习代码主题 -- 0 - [泛型 lambda 基础 — auto 参数与类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/00-generic-lambdas-0.cpp) -- 1 - [泛型 lambda 与 STL 算法 — 排序、查找、函数工厂](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/00-generic-lambdas-1.cpp) +- 0 - [泛型 lambda 基础 — auto 参数与类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/cpp14/tests/00-generic-lambdas/0.cpp) +- 1 - [泛型 lambda 与 STL 算法 — 排序、查找、函数工厂](https://github.com/mcpp-community/d2mcpp/blob/main/cpp14/tests/00-generic-lambdas/1.cpp) ### 练习代码自动检测命令 From d6869fb2d67420a64c37ba63a4f95b5038633bb2 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 22:51:10 +0800 Subject: [PATCH 16/25] =?UTF-8?q?docs(skill):=20chapter=20=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E8=B7=AF=E5=BE=84=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/d2mcpp-authoring/assets/chapter.en.md | 8 ++++---- .agents/skills/d2mcpp-authoring/assets/chapter.zh.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.agents/skills/d2mcpp-authoring/assets/chapter.en.md b/.agents/skills/d2mcpp-authoring/assets/chapter.en.md index f12c11a..2bf0339 100644 --- a/.agents/skills/d2mcpp-authoring/assets/chapter.en.md +++ b/.agents/skills/d2mcpp-authoring/assets/chapter.en.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/...) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src//NN-topic.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings//NN-topic-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/...) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src//NN-topic.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main//tests/NN-topic/0.cpp) | | **Why was it introduced?** @@ -60,7 +60,7 @@ - @@ -67,8 +67,8 @@ ### 练习代码主题 -- 0 - [<练习 0 主题>](https://github.com/mcpp-community/d2mcpp/blob/main/dslings//NN-topic-0.cpp) -- 1 - [<练习 1 主题>](https://github.com/mcpp-community/d2mcpp/blob/main/dslings//NN-topic-1.cpp) +- 0 - [<练习 0 主题>](https://github.com/mcpp-community/d2mcpp/blob/main//tests/NN-topic/0.cpp) +- 1 - [<练习 1 主题>](https://github.com/mcpp-community/d2mcpp/blob/main//tests/NN-topic/1.cpp) ### 练习代码自动检测命令 From 82b2215c944d527b235a6afeb4d54cf22bc83eb8 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 22:55:54 +0800 Subject: [PATCH 17/25] =?UTF-8?q?docs:=20=E8=BF=81=E7=A7=BB=E5=90=8E?= =?UTF-8?q?=E5=8F=82=E8=80=83=E6=96=87=E6=A1=A3=20+=20=E6=97=A7=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E6=A0=87=E8=AE=B0=E5=8F=96=E4=BB=A3=20+=20=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E5=8B=BE=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-07-20-mcpp-provider-reference.md | 2 + ...2026-07-23-exercises-as-tests-reference.md | 80 +++++++++++++++++++ .agents/docs/2026-07-23-migration-plan.md | 14 ++-- 3 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 .agents/docs/2026-07-23-exercises-as-tests-reference.md diff --git a/.agents/docs/2026-07-20-mcpp-provider-reference.md b/.agents/docs/2026-07-20-mcpp-provider-reference.md index e41f029..b6820cb 100644 --- a/.agents/docs/2026-07-20-mcpp-provider-reference.md +++ b/.agents/docs/2026-07-20-mcpp-provider-reference.md @@ -6,6 +6,8 @@ - d2x 侧协议规范:d2x 仓库 `.agents/docs/2026-07-20-d2x-architecture-reference.md` - 本文定位:**当前实现的参考手册**——目录约定、生成物、判定机制、维护须知 + +> **⚠️ 2026-07-23 起本文的 §4(清单生成)与 §5(判定机制)已被「练习即测试」迁移取代,现状见 [`2026-07-23-exercises-as-tests-reference.md`](2026-07-23-exercises-as-tests-reference.md)。** --- ## 1. 全景 diff --git a/.agents/docs/2026-07-23-exercises-as-tests-reference.md b/.agents/docs/2026-07-23-exercises-as-tests-reference.md new file mode 100644 index 0000000..f1a6ba6 --- /dev/null +++ b/.agents/docs/2026-07-23-exercises-as-tests-reference.md @@ -0,0 +1,80 @@ +# 练习即测试 —— 迁移后参考 + +- 日期:2026-07-23 +- 分支:`feat/mcpp-provider` +- 设计稿:[`2026-07-23-exercises-as-tests-design.md`](2026-07-23-exercises-as-tests-design.md) +- 取代:[`2026-07-20-mcpp-provider-reference.md`](2026-07-20-mcpp-provider-reference.md) 中的清单生成(§4)与判定机制(§5)章节 +- 本文定位:**迁移完成后的现状参考** —— 布局、判定链路、验证结果、依赖版本 + +--- + +## 1. 布局 + +``` +mcpp.toml workspace = [harness, intro, cpp11, cpp14, en/*, d2x/buildtools/mcpp] +harness/ 练习脚手架包(纯模块 d2x.harness,零宏) +intro/tests/hello-mcpp.cpp +cpp11/tests//.cpp 49 题 +cpp14/tests/00-generic-lambdas/{0,1}.cpp +en/{intro,cpp11,cpp14}/ en 镜像工程(lang=en 时启用,与 zh 互斥) +solutions///.cpp 52 份(zh/en 共用,含 hello-mcpp) +d2x/buildtools/mcpp/ Provider(C++26) +``` + +- **零生成物**:`.d2x/build/` 及三套清单生成已删除;`.d2x/` 下只剩学习进度 + `state.json`(d2x 写)与判定侧信道 `result.ndjson`(harness 写)。 +- id 推导与旧布局**逐字节一致**(`cpp11-00-auto-and-decltype-0`),学员进度不丢。 + +## 2. 双入口 + +``` +mcpp test -p cpp11 原生:进度表 +mcpp test -p cpp11 00-auto 原生:单题(子串过滤) +d2x checker 闯关:文件监听、通过自动推进(已实测:改对 + hello-mcpp 后自动进入 cpp11-00-auto-and-decltype-0) +``` + +同一条链路:`d2x checker → Provider → mcpp test --message-format json → 判定`; +Provider 只是把 mcpp 的 JSON 记录 × 侧信道合并成协议 verdict。 + +## 3. harness(`import d2x.harness;`) + +| 设施 | 说明 | +|---|---| +| `d2x::check(cond, "原文")` / `check_eq(a, b, "a == b")` | 第三参是给学员看的表达式原文(迁移脚本自动从宏参数生成,教学输出零漂移);`std::formattable` 探测转印值(SFINAE 安全,requires 里直接写 std::format 会踩未特化 formatter 的 static_assert) | +| `D2X_YOUR_ANSWER` | **纯约定,无定义**。裸标识符的编译报错正好指着要填的位置 | +| `d2x::wait()` | 路障;记录后继续执行(后续断言照跑) | +| `d2x::dont_delete_this(expr)` | 恒等透传(旧 D2X_DONT_DELETE_THIS) | +| 退出码 | 首次调用注册 atexit:有失败断言或未拆 wait → `_Exit(1)`。裸 `mcpp test` 因此天然能判对错 | +| 侧信道 v2 | `{"v":2,"kind":"assert","ok":…,"what":…,"expected":…,"actual":…,"file":…,"line":…}` / `{"v":2,"kind":"wait",…}`;`D2X_RESULT_FILE` 未设置时不写文件 | + +**判定顺序(注意与设计稿 §5 的一处刻意调整)**:失败断言 → fail;`wait` → blocked; +退出码非 0 → fail;无侧信道 → 退出码 0 即 pass。`wait` 判定**先于**退出码—— +新 harness 里 wait 会把退出码顶成 1,按旧顺序 blocked 会全部误判成 fail。 + +**教学细节**:`import std` 不带宏——需要 `NULL` 的课(12-nullptr)显式 +`#include ` 并注释原因;全局 `int8_t` 等别名要写 `std::int8_t`。 + +## 4. 依赖的 mcpp 能力(本地分支 feat/test-isolation-json) + +逐测试编译隔离(Phase A 包级/Phase B 每测试独立 ninja goal)、`mcpp test `、 +`--message-format json`、tests 相对路径命名、`[build].flags` glob 覆盖测试 TU、 +`merged_environ` 剥离私有 glibc loader 路径(嵌套段错误根治,d2mcpp `runner.cppm` +的 `unsetenv` workaround 已删;d2x `platform.cppm` 的保留——它还保护 editor 等 +非 mcpp 子进程)。**mcpp 发版并更新 xlings pin 前,CI 会失败,属预期。** + +## 5. 验证结果(2026-07-23,musl 静态 mcpp) + +| 项 | 结果 | +|---|---| +| e2e(`d2x/buildtools/mcpp/tests/e2e.sh all`) | zh 52/52、en 52/52 答案全绿;pristine 双向 0-pass;Provider 冒烟 ✓ | +| d2x 端到端 | checker 显示 0/52 → 改对自动推进 → state.json 兼容旧 id ✓ | +| d2x 仓库改动 | **零**(协议边界承诺兑现) | +| hello-mcpp | 答案已补,进入 e2e,不再 SKIP | + +## 6. 已知缺口(本轮后仍开) + +- macOS / Windows 未实测(Provider 的 `_popen` 分支、mcpp test 三能力的 Windows 路径) +- 编译错误未结构化成协议 diagnostics(原始文本透传,独立增量) +- Provider 无单测(discovery 推导/JSON 解析仅被 e2e 间接覆盖) +- cpp17/20/23 目录尚无练习(撰稿侧工作,脚手架已就绪) diff --git a/.agents/docs/2026-07-23-migration-plan.md b/.agents/docs/2026-07-23-migration-plan.md index d55bfb6..0b654a4 100644 --- a/.agents/docs/2026-07-23-migration-plan.md +++ b/.agents/docs/2026-07-23-migration-plan.md @@ -20,10 +20,10 @@ ## Tasks -- [ ] **T1 harness/ 新包**:顶层 `harness/`,纯模块 `d2x.harness`(check/check_eq/wait,source_location,`what` 可选参),atexit 退出码(失败或 wait 未拆→`_Exit(1)`),侧信道 v2 内联进模块(report.hpp 内容并入,include 路径退役)。单测:临时 fixture 直接 mcpp test。 -- [ ] **T2 试点 cpp14**:`cpp14/mcpp.toml`(dep harness path)+ `cpp14/tests/00-generic-lambdas/{0,1}.cpp`(内容转换:include→import,宏→函数)+ solutions 镜像;根 workspace 更新;`mcpp test -p cpp14` 原生验证(未完成→fail,答案→pass)。 -- [ ] **T3 Provider 瘦身**:discovery 扫 `cpp*/tests`+`intro/tests`(en 前缀 `en/`);check = spawn `mcpp test --message-format json -p ` + 读侧信道 + 合并判定;manifest.cppm 删除;runner.cppm 换成 mcpp-test 解析器;unsetenv workaround 删(mcpp c1bf311 已根治)。 -- [ ] **T4 批量迁移**:脚本迁 cpp11×49 + en/×52 + intro(hello-mcpp)+ solutions;两道特殊 cxxflags 题改用 `[build] flags = [{glob=...}]`(先验证 glob 对 tests/ 生效,不生效则保留头部指令+Provider 透传······不,tests 由 mcpp 驱动,必须 toml 承载,验证是硬前提);删 dslings/。 -- [ ] **T5 e2e.sh 重写**:两次全量 `mcpp test`(pristine 全 fail + overlay 全 pass)×zh/en + Provider 协议冒烟(枚举 52、check 单题事件流);hello-mcpp 补答案入 e2e。 -- [ ] **T6 d2x 端到端**:构建 d2x(feat/exercise-framework-protocol,musl mcpp on PATH),`d2x checker` 跑通新链路;.d2x.json 不变。 -- [ ] **T7 文档收尾**:book 路径 sed、authoring skill、CI workflow(注明需 mcpp 下一发版)、参考文档更新记录、CHANGELOG 式提交。 +- [x] **T1 harness/ 新包**:顶层 `harness/`,纯模块 `d2x.harness`(check/check_eq/wait,source_location,`what` 可选参),atexit 退出码(失败或 wait 未拆→`_Exit(1)`),侧信道 v2 内联进模块(report.hpp 内容并入,include 路径退役)。单测:临时 fixture 直接 mcpp test。 +- [x] **T2 试点 cpp14**:`cpp14/mcpp.toml`(dep harness path)+ `cpp14/tests/00-generic-lambdas/{0,1}.cpp`(内容转换:include→import,宏→函数)+ solutions 镜像;根 workspace 更新;`mcpp test -p cpp14` 原生验证(未完成→fail,答案→pass)。 +- [x] **T3 Provider 瘦身**:discovery 扫 `cpp*/tests`+`intro/tests`(en 前缀 `en/`);check = spawn `mcpp test --message-format json -p ` + 读侧信道 + 合并判定;manifest.cppm 删除;runner.cppm 换成 mcpp-test 解析器;unsetenv workaround 删(mcpp c1bf311 已根治)。 +- [x] **T4 批量迁移**:脚本迁 cpp11×49 + en/×52 + intro(hello-mcpp)+ solutions;两道特殊 cxxflags 题改用 `[build] flags = [{glob=...}]`(先验证 glob 对 tests/ 生效,不生效则保留头部指令+Provider 透传······不,tests 由 mcpp 驱动,必须 toml 承载,验证是硬前提);删 dslings/。 +- [x] **T5 e2e.sh 重写**:两次全量 `mcpp test`(pristine 全 fail + overlay 全 pass)×zh/en + Provider 协议冒烟(枚举 52、check 单题事件流);hello-mcpp 补答案入 e2e。 +- [x] **T6 d2x 端到端**:构建 d2x(feat/exercise-framework-protocol,musl mcpp on PATH),`d2x checker` 跑通新链路;.d2x.json 不变。 +- [x] **T7 文档收尾**:book 路径 sed、authoring skill、CI workflow(注明需 mcpp 下一发版)、参考文档更新记录、CHANGELOG 式提交。 From f2261d843b5069540be91fcf1a68c3cd99c9b55a Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 23:33:18 +0800 Subject: [PATCH 18/25] =?UTF-8?q?refactor:=20harness=E2=86=92d2x=20?= =?UTF-8?q?=E5=BA=93(import=20d2x)=E3=80=81=E8=AF=BE=E7=A8=8B=E5=B7=A5?= =?UTF-8?q?=E7=A8=8B=E6=94=B6=E8=BF=9B=20src/=E3=80=81=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=A0=87=E8=AF=86=E5=8E=BB=20HONLY=20=E9=87=8D=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/d2mcpp-authoring/SKILL.md | 14 ++-- .../d2mcpp-authoring/assets/exercise.cpp | 4 +- .../d2mcpp-authoring/assets/solution.cpp | 6 +- .../d2mcpp-authoring/references/anatomy.md | 14 ++-- .github/workflows/dslings-ref-ci.yml | 10 +-- book/en/src/README.md | 2 +- book/en/src/base/chapter_0.md | 2 +- book/en/src/base/chapter_1.md | 63 +++++++++------- book/en/src/cpp11/00-auto-and-decltype.md | 14 ++-- book/en/src/cpp11/01-default-and-delete.md | 8 +- book/en/src/cpp11/02-final-and-override.md | 8 +- book/en/src/cpp11/03-trailing-return-type.md | 4 +- book/en/src/cpp11/04-rvalue-references.md | 4 +- book/en/src/cpp11/05-move-semantics.md | 8 +- book/en/src/cpp11/06-scoped-enums.md | 6 +- book/en/src/cpp11/07-constexpr.md | 6 +- book/en/src/cpp11/08-literal-type.md | 6 +- book/en/src/cpp11/09-list-initialization.md | 2 +- .../src/cpp11/10-delegating-constructors.md | 2 +- .../en/src/cpp11/11-inherited-constructors.md | 8 +- book/en/src/cpp11/12-nullptr.md | 8 +- book/en/src/cpp11/13-long-long.md | 6 +- book/en/src/cpp11/14-type-alias.md | 10 +-- book/en/src/cpp11/15-variadic-templates.md | 6 +- book/en/src/cpp11/16-generalized-unions.md | 8 +- book/en/src/cpp11/17-pod-type.md | 8 +- book/en/src/cpp14/00-generic-lambdas.md | 6 +- book/src/base/chapter_1.md | 23 +++--- book/src/cpp11/00-auto-and-decltype.md | 14 ++-- book/src/cpp11/01-default-and-delete.md | 8 +- book/src/cpp11/02-final-and-override.md | 8 +- book/src/cpp11/03-trailing-return-type.md | 4 +- book/src/cpp11/04-rvalue-references.md | 4 +- book/src/cpp11/05-move-semantics.md | 8 +- book/src/cpp11/06-scoped-enums.md | 6 +- book/src/cpp11/07-constexpr.md | 6 +- book/src/cpp11/08-literal-type.md | 6 +- book/src/cpp11/09-list-initialization.md | 2 +- book/src/cpp11/10-delegating-constructors.md | 2 +- book/src/cpp11/11-inherited-constructors.md | 8 +- book/src/cpp11/12-nullptr.md | 8 +- book/src/cpp11/13-long-long.md | 6 +- book/src/cpp11/14-type-alias.md | 10 +-- book/src/cpp11/15-variadic-templates.md | 6 +- book/src/cpp11/16-generalized-unions.md | 8 +- book/src/cpp11/17-pod-type.md | 8 +- book/src/cpp14/00-generic-lambdas.md | 6 +- cpp14/mcpp.toml | 11 --- d2x/buildtools/mcpp/src/discovery.cppm | 12 +-- d2x/buildtools/mcpp/tests/e2e.sh | 6 +- d2x/mcpp.toml | 9 +++ harness/src/harness.cppm => d2x/src/d2x.cppm | 75 +++++++++++++------ harness/mcpp.toml | 9 --- mcpp.toml | 8 +- solutions/cpp11/00-auto-and-decltype/0.cpp | 2 +- solutions/cpp11/00-auto-and-decltype/1.cpp | 2 +- solutions/cpp11/00-auto-and-decltype/2.cpp | 2 +- solutions/cpp11/00-auto-and-decltype/3.cpp | 2 +- solutions/cpp11/00-auto-and-decltype/4.cpp | 2 +- solutions/cpp11/00-auto-and-decltype/5.cpp | 2 +- solutions/cpp11/01-default-and-delete/0.cpp | 2 +- solutions/cpp11/01-default-and-delete/1.cpp | 2 +- solutions/cpp11/01-default-and-delete/2.cpp | 2 +- solutions/cpp11/02-final-and-override/0.cpp | 2 +- solutions/cpp11/02-final-and-override/1.cpp | 2 +- solutions/cpp11/02-final-and-override/2.cpp | 2 +- solutions/cpp11/03-trailing-return-type/0.cpp | 2 +- solutions/cpp11/04-rvalue-references/0.cpp | 2 +- solutions/cpp11/05-move-semantics/0.cpp | 2 +- solutions/cpp11/05-move-semantics/1.cpp | 2 +- solutions/cpp11/05-move-semantics/2.cpp | 2 +- solutions/cpp11/06-scoped-enums/0.cpp | 2 +- solutions/cpp11/06-scoped-enums/1.cpp | 2 +- solutions/cpp11/07-constexpr/0.cpp | 2 +- solutions/cpp11/07-constexpr/1.cpp | 2 +- solutions/cpp11/08-literal-type/0.cpp | 2 +- solutions/cpp11/08-literal-type/1.cpp | 2 +- solutions/cpp11/09-list-initialization/0.cpp | 2 +- solutions/cpp11/09-list-initialization/1.cpp | 2 +- solutions/cpp11/09-list-initialization/2.cpp | 2 +- solutions/cpp11/09-list-initialization/3.cpp | 2 +- .../cpp11/10-delegating-constructors/0.cpp | 2 +- .../cpp11/10-delegating-constructors/1.cpp | 2 +- .../cpp11/11-inherited-constructors/0.cpp | 2 +- .../cpp11/11-inherited-constructors/1.cpp | 2 +- .../cpp11/11-inherited-constructors/2.cpp | 2 +- solutions/cpp11/12-nullptr/0.cpp | 2 +- solutions/cpp11/12-nullptr/1.cpp | 2 +- solutions/cpp11/12-nullptr/2.cpp | 2 +- solutions/cpp11/13-long-long/0.cpp | 2 +- solutions/cpp11/13-long-long/1.cpp | 2 +- solutions/cpp11/14-type-alias/0.cpp | 2 +- solutions/cpp11/14-type-alias/1.cpp | 2 +- solutions/cpp11/14-type-alias/2.cpp | 2 +- solutions/cpp11/14-type-alias/3.cpp | 2 +- solutions/cpp11/15-variadic-templates/0.cpp | 2 +- solutions/cpp11/15-variadic-templates/1.cpp | 2 +- solutions/cpp11/16-generalized-unions/0.cpp | 2 +- solutions/cpp11/16-generalized-unions/1.cpp | 2 +- solutions/cpp11/16-generalized-unions/2.cpp | 2 +- solutions/cpp11/17-pod-type/0.cpp | 2 +- solutions/cpp11/17-pod-type/1.cpp | 2 +- solutions/cpp11/17-pod-type/2.cpp | 2 +- solutions/cpp14/00-generic-lambdas/0.cpp | 2 +- solutions/cpp14/00-generic-lambdas/1.cpp | 2 +- solutions/intro/hello-mcpp.cpp | 4 +- {cpp11 => src/cpp11}/mcpp.toml | 6 +- .../cpp11}/tests/00-auto-and-decltype/0.cpp | 4 +- .../cpp11}/tests/00-auto-and-decltype/1.cpp | 4 +- .../cpp11}/tests/00-auto-and-decltype/2.cpp | 4 +- .../cpp11}/tests/00-auto-and-decltype/3.cpp | 4 +- .../cpp11}/tests/00-auto-and-decltype/4.cpp | 4 +- .../cpp11}/tests/00-auto-and-decltype/5.cpp | 4 +- .../cpp11}/tests/01-default-and-delete/0.cpp | 4 +- .../cpp11}/tests/01-default-and-delete/1.cpp | 4 +- .../cpp11}/tests/01-default-and-delete/2.cpp | 4 +- .../cpp11}/tests/02-final-and-override/0.cpp | 4 +- .../cpp11}/tests/02-final-and-override/1.cpp | 4 +- .../cpp11}/tests/02-final-and-override/2.cpp | 4 +- .../tests/03-trailing-return-type/0.cpp | 4 +- .../cpp11}/tests/04-rvalue-references/0.cpp | 4 +- .../cpp11}/tests/05-move-semantics/0.cpp | 4 +- .../cpp11}/tests/05-move-semantics/1.cpp | 4 +- .../cpp11}/tests/05-move-semantics/2.cpp | 4 +- .../cpp11}/tests/06-scoped-enums/0.cpp | 4 +- .../cpp11}/tests/06-scoped-enums/1.cpp | 4 +- {cpp11 => src/cpp11}/tests/07-constexpr/0.cpp | 4 +- {cpp11 => src/cpp11}/tests/07-constexpr/1.cpp | 4 +- .../cpp11}/tests/08-literal-type/0.cpp | 4 +- .../cpp11}/tests/08-literal-type/1.cpp | 4 +- .../cpp11}/tests/09-list-initialization/0.cpp | 4 +- .../cpp11}/tests/09-list-initialization/1.cpp | 4 +- .../cpp11}/tests/09-list-initialization/2.cpp | 4 +- .../cpp11}/tests/09-list-initialization/3.cpp | 4 +- .../tests/10-delegating-constructors/0.cpp | 4 +- .../tests/10-delegating-constructors/1.cpp | 4 +- .../tests/11-inherited-constructors/0.cpp | 4 +- .../tests/11-inherited-constructors/1.cpp | 4 +- .../tests/11-inherited-constructors/2.cpp | 4 +- {cpp11 => src/cpp11}/tests/12-nullptr/0.cpp | 4 +- {cpp11 => src/cpp11}/tests/12-nullptr/1.cpp | 4 +- {cpp11 => src/cpp11}/tests/12-nullptr/2.cpp | 4 +- {cpp11 => src/cpp11}/tests/13-long-long/0.cpp | 4 +- {cpp11 => src/cpp11}/tests/13-long-long/1.cpp | 4 +- .../cpp11}/tests/14-type-alias/0.cpp | 4 +- .../cpp11}/tests/14-type-alias/1.cpp | 4 +- .../cpp11}/tests/14-type-alias/2.cpp | 4 +- .../cpp11}/tests/14-type-alias/3.cpp | 4 +- .../cpp11}/tests/15-variadic-templates/0.cpp | 4 +- .../cpp11}/tests/15-variadic-templates/1.cpp | 4 +- .../cpp11}/tests/16-generalized-unions/0.cpp | 4 +- .../cpp11}/tests/16-generalized-unions/1.cpp | 4 +- .../cpp11}/tests/16-generalized-unions/2.cpp | 4 +- {cpp11 => src/cpp11}/tests/17-pod-type/0.cpp | 4 +- {cpp11 => src/cpp11}/tests/17-pod-type/1.cpp | 4 +- {cpp11 => src/cpp11}/tests/17-pod-type/2.cpp | 4 +- src/cpp14/mcpp.toml | 10 +++ .../cpp14}/tests/00-generic-lambdas/0.cpp | 2 +- .../cpp14}/tests/00-generic-lambdas/1.cpp | 4 +- {en => src/en}/cpp11/mcpp.toml | 6 +- .../cpp11/tests/00-auto-and-decltype/0.cpp | 4 +- .../cpp11/tests/00-auto-and-decltype/1.cpp | 4 +- .../cpp11/tests/00-auto-and-decltype/2.cpp | 4 +- .../cpp11/tests/00-auto-and-decltype/3.cpp | 4 +- .../cpp11/tests/00-auto-and-decltype/4.cpp | 4 +- .../cpp11/tests/00-auto-and-decltype/5.cpp | 4 +- .../cpp11/tests/01-default-and-delete/0.cpp | 4 +- .../cpp11/tests/01-default-and-delete/1.cpp | 4 +- .../cpp11/tests/01-default-and-delete/2.cpp | 4 +- .../cpp11/tests/02-final-and-override/0.cpp | 4 +- .../cpp11/tests/02-final-and-override/1.cpp | 4 +- .../cpp11/tests/02-final-and-override/2.cpp | 4 +- .../cpp11/tests/03-trailing-return-type/0.cpp | 4 +- .../cpp11/tests/04-rvalue-references/0.cpp | 4 +- .../en}/cpp11/tests/05-move-semantics/0.cpp | 4 +- .../en}/cpp11/tests/05-move-semantics/1.cpp | 4 +- .../en}/cpp11/tests/05-move-semantics/2.cpp | 4 +- .../en}/cpp11/tests/06-scoped-enums/0.cpp | 4 +- .../en}/cpp11/tests/06-scoped-enums/1.cpp | 4 +- {en => src/en}/cpp11/tests/07-constexpr/0.cpp | 4 +- {en => src/en}/cpp11/tests/07-constexpr/1.cpp | 4 +- .../en}/cpp11/tests/08-literal-type/0.cpp | 4 +- .../en}/cpp11/tests/08-literal-type/1.cpp | 4 +- .../cpp11/tests/09-list-initialization/0.cpp | 4 +- .../cpp11/tests/09-list-initialization/1.cpp | 4 +- .../cpp11/tests/09-list-initialization/2.cpp | 4 +- .../cpp11/tests/09-list-initialization/3.cpp | 4 +- .../tests/10-delegating-constructors/0.cpp | 4 +- .../tests/10-delegating-constructors/1.cpp | 4 +- .../tests/11-inherited-constructors/0.cpp | 4 +- .../tests/11-inherited-constructors/1.cpp | 4 +- .../tests/11-inherited-constructors/2.cpp | 4 +- {en => src/en}/cpp11/tests/12-nullptr/0.cpp | 4 +- {en => src/en}/cpp11/tests/12-nullptr/1.cpp | 4 +- {en => src/en}/cpp11/tests/12-nullptr/2.cpp | 4 +- {en => src/en}/cpp11/tests/13-long-long/0.cpp | 4 +- {en => src/en}/cpp11/tests/13-long-long/1.cpp | 4 +- .../en}/cpp11/tests/14-type-alias/0.cpp | 4 +- .../en}/cpp11/tests/14-type-alias/1.cpp | 4 +- .../en}/cpp11/tests/14-type-alias/2.cpp | 4 +- .../en}/cpp11/tests/14-type-alias/3.cpp | 4 +- .../cpp11/tests/15-variadic-templates/0.cpp | 4 +- .../cpp11/tests/15-variadic-templates/1.cpp | 4 +- .../cpp11/tests/16-generalized-unions/0.cpp | 4 +- .../cpp11/tests/16-generalized-unions/1.cpp | 4 +- .../cpp11/tests/16-generalized-unions/2.cpp | 4 +- {en => src/en}/cpp11/tests/17-pod-type/0.cpp | 4 +- {en => src/en}/cpp11/tests/17-pod-type/1.cpp | 4 +- {en => src/en}/cpp11/tests/17-pod-type/2.cpp | 4 +- {en => src/en}/cpp14/mcpp.toml | 6 +- .../en}/cpp14/tests/00-generic-lambdas/0.cpp | 4 +- .../en}/cpp14/tests/00-generic-lambdas/1.cpp | 4 +- {en => src/en}/intro/mcpp.toml | 6 +- {en => src/en}/intro/tests/hello-mcpp.cpp | 20 ++--- {intro => src/intro}/mcpp.toml | 6 +- {intro => src/intro}/tests/hello-mcpp.cpp | 22 +++--- 216 files changed, 574 insertions(+), 544 deletions(-) delete mode 100644 cpp14/mcpp.toml create mode 100644 d2x/mcpp.toml rename harness/src/harness.cppm => d2x/src/d2x.cppm (64%) delete mode 100644 harness/mcpp.toml rename {cpp11 => src/cpp11}/mcpp.toml (52%) rename {cpp11 => src/cpp11}/tests/00-auto-and-decltype/0.cpp (94%) rename {cpp11 => src/cpp11}/tests/00-auto-and-decltype/1.cpp (94%) rename {cpp11 => src/cpp11}/tests/00-auto-and-decltype/2.cpp (95%) rename {cpp11 => src/cpp11}/tests/00-auto-and-decltype/3.cpp (94%) rename {cpp11 => src/cpp11}/tests/00-auto-and-decltype/4.cpp (96%) rename {cpp11 => src/cpp11}/tests/00-auto-and-decltype/5.cpp (96%) rename {cpp11 => src/cpp11}/tests/01-default-and-delete/0.cpp (92%) rename {cpp11 => src/cpp11}/tests/01-default-and-delete/1.cpp (95%) rename {cpp11 => src/cpp11}/tests/01-default-and-delete/2.cpp (91%) rename {cpp11 => src/cpp11}/tests/02-final-and-override/0.cpp (93%) rename {cpp11 => src/cpp11}/tests/02-final-and-override/1.cpp (93%) rename {cpp11 => src/cpp11}/tests/02-final-and-override/2.cpp (96%) rename {cpp11 => src/cpp11}/tests/03-trailing-return-type/0.cpp (93%) rename {cpp11 => src/cpp11}/tests/04-rvalue-references/0.cpp (97%) rename {cpp11 => src/cpp11}/tests/05-move-semantics/0.cpp (96%) rename {cpp11 => src/cpp11}/tests/05-move-semantics/1.cpp (97%) rename {cpp11 => src/cpp11}/tests/05-move-semantics/2.cpp (97%) rename {cpp11 => src/cpp11}/tests/06-scoped-enums/0.cpp (94%) rename {cpp11 => src/cpp11}/tests/06-scoped-enums/1.cpp (96%) rename {cpp11 => src/cpp11}/tests/07-constexpr/0.cpp (93%) rename {cpp11 => src/cpp11}/tests/07-constexpr/1.cpp (96%) rename {cpp11 => src/cpp11}/tests/08-literal-type/0.cpp (95%) rename {cpp11 => src/cpp11}/tests/08-literal-type/1.cpp (92%) rename {cpp11 => src/cpp11}/tests/09-list-initialization/0.cpp (94%) rename {cpp11 => src/cpp11}/tests/09-list-initialization/1.cpp (93%) rename {cpp11 => src/cpp11}/tests/09-list-initialization/2.cpp (94%) rename {cpp11 => src/cpp11}/tests/09-list-initialization/3.cpp (96%) rename {cpp11 => src/cpp11}/tests/10-delegating-constructors/0.cpp (96%) rename {cpp11 => src/cpp11}/tests/10-delegating-constructors/1.cpp (96%) rename {cpp11 => src/cpp11}/tests/11-inherited-constructors/0.cpp (96%) rename {cpp11 => src/cpp11}/tests/11-inherited-constructors/1.cpp (97%) rename {cpp11 => src/cpp11}/tests/11-inherited-constructors/2.cpp (96%) rename {cpp11 => src/cpp11}/tests/12-nullptr/0.cpp (96%) rename {cpp11 => src/cpp11}/tests/12-nullptr/1.cpp (95%) rename {cpp11 => src/cpp11}/tests/12-nullptr/2.cpp (95%) rename {cpp11 => src/cpp11}/tests/13-long-long/0.cpp (95%) rename {cpp11 => src/cpp11}/tests/13-long-long/1.cpp (95%) rename {cpp11 => src/cpp11}/tests/14-type-alias/0.cpp (94%) rename {cpp11 => src/cpp11}/tests/14-type-alias/1.cpp (95%) rename {cpp11 => src/cpp11}/tests/14-type-alias/2.cpp (95%) rename {cpp11 => src/cpp11}/tests/14-type-alias/3.cpp (93%) rename {cpp11 => src/cpp11}/tests/15-variadic-templates/0.cpp (94%) rename {cpp11 => src/cpp11}/tests/15-variadic-templates/1.cpp (93%) rename {cpp11 => src/cpp11}/tests/16-generalized-unions/0.cpp (92%) rename {cpp11 => src/cpp11}/tests/16-generalized-unions/1.cpp (93%) rename {cpp11 => src/cpp11}/tests/16-generalized-unions/2.cpp (97%) rename {cpp11 => src/cpp11}/tests/17-pod-type/0.cpp (96%) rename {cpp11 => src/cpp11}/tests/17-pod-type/1.cpp (95%) rename {cpp11 => src/cpp11}/tests/17-pod-type/2.cpp (97%) create mode 100644 src/cpp14/mcpp.toml rename {cpp14 => src/cpp14}/tests/00-generic-lambdas/0.cpp (97%) rename {cpp14 => src/cpp14}/tests/00-generic-lambdas/1.cpp (96%) rename {en => src/en}/cpp11/mcpp.toml (52%) rename {en => src/en}/cpp11/tests/00-auto-and-decltype/0.cpp (94%) rename {en => src/en}/cpp11/tests/00-auto-and-decltype/1.cpp (94%) rename {en => src/en}/cpp11/tests/00-auto-and-decltype/2.cpp (95%) rename {en => src/en}/cpp11/tests/00-auto-and-decltype/3.cpp (93%) rename {en => src/en}/cpp11/tests/00-auto-and-decltype/4.cpp (96%) rename {en => src/en}/cpp11/tests/00-auto-and-decltype/5.cpp (96%) rename {en => src/en}/cpp11/tests/01-default-and-delete/0.cpp (92%) rename {en => src/en}/cpp11/tests/01-default-and-delete/1.cpp (95%) rename {en => src/en}/cpp11/tests/01-default-and-delete/2.cpp (90%) rename {en => src/en}/cpp11/tests/02-final-and-override/0.cpp (93%) rename {en => src/en}/cpp11/tests/02-final-and-override/1.cpp (93%) rename {en => src/en}/cpp11/tests/02-final-and-override/2.cpp (96%) rename {en => src/en}/cpp11/tests/03-trailing-return-type/0.cpp (93%) rename {en => src/en}/cpp11/tests/04-rvalue-references/0.cpp (97%) rename {en => src/en}/cpp11/tests/05-move-semantics/0.cpp (96%) rename {en => src/en}/cpp11/tests/05-move-semantics/1.cpp (97%) rename {en => src/en}/cpp11/tests/05-move-semantics/2.cpp (97%) rename {en => src/en}/cpp11/tests/06-scoped-enums/0.cpp (93%) rename {en => src/en}/cpp11/tests/06-scoped-enums/1.cpp (96%) rename {en => src/en}/cpp11/tests/07-constexpr/0.cpp (93%) rename {en => src/en}/cpp11/tests/07-constexpr/1.cpp (96%) rename {en => src/en}/cpp11/tests/08-literal-type/0.cpp (95%) rename {en => src/en}/cpp11/tests/08-literal-type/1.cpp (92%) rename {en => src/en}/cpp11/tests/09-list-initialization/0.cpp (94%) rename {en => src/en}/cpp11/tests/09-list-initialization/1.cpp (92%) rename {en => src/en}/cpp11/tests/09-list-initialization/2.cpp (94%) rename {en => src/en}/cpp11/tests/09-list-initialization/3.cpp (96%) rename {en => src/en}/cpp11/tests/10-delegating-constructors/0.cpp (96%) rename {en => src/en}/cpp11/tests/10-delegating-constructors/1.cpp (96%) rename {en => src/en}/cpp11/tests/11-inherited-constructors/0.cpp (95%) rename {en => src/en}/cpp11/tests/11-inherited-constructors/1.cpp (97%) rename {en => src/en}/cpp11/tests/11-inherited-constructors/2.cpp (96%) rename {en => src/en}/cpp11/tests/12-nullptr/0.cpp (96%) rename {en => src/en}/cpp11/tests/12-nullptr/1.cpp (95%) rename {en => src/en}/cpp11/tests/12-nullptr/2.cpp (95%) rename {en => src/en}/cpp11/tests/13-long-long/0.cpp (95%) rename {en => src/en}/cpp11/tests/13-long-long/1.cpp (95%) rename {en => src/en}/cpp11/tests/14-type-alias/0.cpp (94%) rename {en => src/en}/cpp11/tests/14-type-alias/1.cpp (95%) rename {en => src/en}/cpp11/tests/14-type-alias/2.cpp (95%) rename {en => src/en}/cpp11/tests/14-type-alias/3.cpp (93%) rename {en => src/en}/cpp11/tests/15-variadic-templates/0.cpp (94%) rename {en => src/en}/cpp11/tests/15-variadic-templates/1.cpp (92%) rename {en => src/en}/cpp11/tests/16-generalized-unions/0.cpp (92%) rename {en => src/en}/cpp11/tests/16-generalized-unions/1.cpp (93%) rename {en => src/en}/cpp11/tests/16-generalized-unions/2.cpp (97%) rename {en => src/en}/cpp11/tests/17-pod-type/0.cpp (96%) rename {en => src/en}/cpp11/tests/17-pod-type/1.cpp (95%) rename {en => src/en}/cpp11/tests/17-pod-type/2.cpp (97%) rename {en => src/en}/cpp14/mcpp.toml (52%) rename {en => src/en}/cpp14/tests/00-generic-lambdas/0.cpp (96%) rename {en => src/en}/cpp14/tests/00-generic-lambdas/1.cpp (96%) rename {en => src/en}/intro/mcpp.toml (52%) rename {en => src/en}/intro/tests/hello-mcpp.cpp (75%) rename {intro => src/intro}/mcpp.toml (52%) rename {intro => src/intro}/tests/hello-mcpp.cpp (69%) diff --git a/.agents/skills/d2mcpp-authoring/SKILL.md b/.agents/skills/d2mcpp-authoring/SKILL.md index 79ef9c4..c40ddaa 100644 --- a/.agents/skills/d2mcpp-authoring/SKILL.md +++ b/.agents/skills/d2mcpp-authoring/SKILL.md @@ -7,7 +7,7 @@ description: >- solution, or when registering them in SUMMARY/changelog. Trigger it for any request like "add a chapter for X", "write an exercise for fold expressions", "add the constexpr lesson", "fill the cpp14 section", or - anything touching book/src, book/en/src, cpp*/tests/, en/, or solutions/. + anything touching book/src, book/en/src, src/ (exercises), or solutions/. Even small edits should follow these conventions so the bilingual + book/code/checker artifacts stay in sync. --- @@ -50,7 +50,7 @@ Honor that axis: feature was *introduced*, not the compile flags. Teach the introduction-era shape through content and assertions (an untested teaching point disappears silently). If one exercise needs special flags, declare a per-glob entry in - the member's `/mcpp.toml` `[build].flags` — never a bare `TODO` hack. + the member's `src//mcpp.toml` `[build].flags` — never a bare `TODO` hack. Background and the full per-feature evolution analysis live in `.agents/docs/2026-06-08-cpp11-feature-evolution-and-cxx20-baseline.md`. Consult @@ -69,8 +69,8 @@ When adding a feature numbered `NN` with slug `topic` (e.g. `06-scoped-enums`): 1. `book/src//NN-topic.md` — zh chapter 2. `book/en/src//NN-topic.md` — en chapter (translate prose, keep code identical) -3. `/tests/NN-topic/K.cpp` — one or more exercises (`K` = 0,1,2…), zh comments -4. `en//tests/NN-topic/K.cpp` — en exercises (translate comments only) +3. `src//tests/NN-topic/K.cpp` — one or more exercises (`K` = 0,1,2…), zh comments +4. `src/en//tests/NN-topic/K.cpp` — en exercises (translate comments only) 5. `solutions//NN-topic/K.cpp` — reference solution per exercise (zh/en 共用) 6. (nothing to register — exercises are tests, discovered by directory convention) 8. Add the chapter line to **both** `book/src/SUMMARY.md` and `book/en/src/SUMMARY.md` @@ -84,7 +84,7 @@ exact registration snippets and SUMMARY/changelog line formats. - Chapter/exercise prefix is a **two-digit** sequence `NN` within the standard section; slug is **kebab-case** and matches book + tests + solutions. - Exercises split into `0.cpp`, `1.cpp`, … by sub-topic inside the chapter - directory `/tests/NN-topic/`; a single-exercise chapter is just `0.cpp`. + directory `src//tests/NN-topic/`; a single-exercise chapter is just `0.cpp`. - The **`d2x checker `** name is the slug (and `-1`, `-2` for later exercises) — it omits the `NN-` prefix. Keep checker names, file names, and book references mutually consistent. @@ -151,12 +151,12 @@ Use `assets/exercise.cpp`. Essentials: `Exercise/练习:` line (` | NN - topic | 中文小标题`), `Tips/提示:`, `Docs/文档:` (cppreference), and the `Auto-Checker/自动检测命令:` with `d2x checker `. -- `import std;` + `import d2x.harness;` — no `#include` unless the lesson +- `import std;` + `import d2x;` — no `#include` unless the lesson needs a macro from a header (e.g. `NULL` teaching needs ``; put the include *before* the imports with a comment saying why). - `main()` seeded with **intentional errors** the learner fixes, each flagged by a numbered inline comment (`// 1.…`, `// 2.…`) telling them what to do. -- Conventions (harness is `import d2x.harness;`, no macros): +- Conventions (the d2x library, `import d2x;`, no macros): - `d2x::check(cond, "原文")` / `d2x::check_eq(a, b, "a == b")` — runtime checkpoints; the third argument is the expression text shown to learners (c++23 has no reflection to capture it automatically — always pass it). diff --git a/.agents/skills/d2mcpp-authoring/assets/exercise.cpp b/.agents/skills/d2mcpp-authoring/assets/exercise.cpp index aecfa9d..439b252 100644 --- a/.agents/skills/d2mcpp-authoring/assets/exercise.cpp +++ b/.agents/skills/d2mcpp-authoring/assets/exercise.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: /tests/NN-topic/K.cpp +// file: src//tests/NN-topic/K.cpp // // Exercise/练习: | NN - topic | <中文小标题> // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/.agents/skills/d2mcpp-authoring/assets/solution.cpp b/.agents/skills/d2mcpp-authoring/assets/solution.cpp index acfaf31..9498f31 100644 --- a/.agents/skills/d2mcpp-authoring/assets/solution.cpp +++ b/.agents/skills/d2mcpp-authoring/assets/solution.cpp @@ -1,13 +1,13 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// reference solution for: /tests/NN-topic/K.cpp +// reference solution for: src//tests/NN-topic/K.cpp // // 用途: 仅给 CI 与维护者参考使用,不是教程入口。 -// 教程练习入口: /tests/NN-topic/K.cpp +// 教程练习入口: src//tests/NN-topic/K.cpp // import std; -import d2x.harness; +import d2x; int main() { diff --git a/.agents/skills/d2mcpp-authoring/references/anatomy.md b/.agents/skills/d2mcpp-authoring/references/anatomy.md index 0c31f66..233e7fd 100644 --- a/.agents/skills/d2mcpp-authoring/references/anatomy.md +++ b/.agents/skills/d2mcpp-authoring/references/anatomy.md @@ -16,8 +16,8 @@ book/en/src/SUMMARY.md # en TOC (register here) book/src/changelog.md # zh changelog (add entry) book/en/src/changelog.md # en changelog (add entry) -/tests/NN-topic/K.cpp # zh exercise(s) — no registration needed -en//tests/NN-topic/K.cpp # en exercise(s) — no registration needed +src//tests/NN-topic/K.cpp # zh exercise(s) — no registration needed +src/en//tests/NN-topic/K.cpp # en exercise(s) — no registration needed solutions//NN-topic/K.cpp # reference solution(s), zh/en 共用 ``` @@ -35,10 +35,10 @@ The `## 二、真实案例` section of every chapter links into `msvc-stl/` for verbatim STL excerpt; you do not add files here per lesson — only refresh the snapshot via `msvc-stl/SOURCE.md` when needed. -A new `` section that does not exist yet needs: a `/mcpp.toml` (copy -`cpp14/mcpp.toml`, change the name), `/tests/` + `en//tests/` + +A new `` section that does not exist yet needs: a `src//mcpp.toml` (copy +`src/cpp14/mcpp.toml`, change the name), `src//tests/` + `src/en//tests/` + `solutions//` directories, the member added to the root `mcpp.toml` -workspace list (both `` and `en/`), and the section heading in both +workspace list (both `src/` and `src/en/`), and the section heading in both `SUMMARY.md` files (e.g. `# C++14核心语言特性` / `# C++14 Core Language Features`). @@ -46,7 +46,7 @@ Features`). **Exercises are tests.** Each `/` is a real mcpp project and exercises are its `tests/`; dropping `/tests/NN-topic/K.cpp` into place is the whole job. -`mcpp test -p ` runs them natively (the report is the progress table), and +`mcpp test -p src/` runs them natively (the report is the progress table), and the d2x Provider (`d2x/buildtools/mcpp/`) derives the exercise id, order and chapter from the same path — one chain, no generated manifests, nothing under `.d2x/` but learner progress. @@ -111,7 +111,7 @@ Run from the project root; report real output, do not assert success blind: ```bash # run the exercise natively (fastest loop while authoring) -mcpp test -p NN-topic +mcpp test -p src/ NN-topic # check it through the Provider path (exactly what `d2x checker` consumes) mcpp run -q -p d2x/buildtools/mcpp -- check cppNN-NN-topic-0 diff --git a/.github/workflows/dslings-ref-ci.yml b/.github/workflows/dslings-ref-ci.yml index 4579be9..1294b4b 100644 --- a/.github/workflows/dslings-ref-ci.yml +++ b/.github/workflows/dslings-ref-ci.yml @@ -15,10 +15,7 @@ on: push: branches: ["main"] paths: - - "harness/**" - - "intro/**" - - "cpp*/**" - - "en/**" + - "src/**" - "solutions/**" - "d2x/**" - "mcpp.toml" @@ -26,10 +23,7 @@ on: - ".github/workflows/dslings-ref-ci.yml" pull_request: paths: - - "harness/**" - - "intro/**" - - "cpp*/**" - - "en/**" + - "src/**" - "solutions/**" - "d2x/**" - "mcpp.toml" diff --git a/book/en/src/README.md b/book/en/src/README.md index 5f2b455..00dc449 100644 --- a/book/en/src/README.md +++ b/book/en/src/README.md @@ -17,7 +17,7 @@ [📚Book]: https://mcpp-community.github.io/d2mcpp [🎥Video]: https://www.bilibili.com/video/BV182MtzPEiX -[⌨️Code]: https://github.com/mcpp-community/d2mcpp/tree/main/dslings +[⌨️Code]: https://github.com/mcpp-community/d2mcpp/tree/main/src/cpp11/tests [👥X]: https://forum.d2learn.org/category/20 ## Goals diff --git a/book/en/src/base/chapter_0.md b/book/en/src/base/chapter_0.md index d933f77..de76941 100644 --- a/book/en/src/base/chapter_0.md +++ b/book/en/src/base/chapter_0.md @@ -12,7 +12,7 @@ d2mcpp is an open-source tutorial project focused on **Modern C++ Core Language - [Book: Online E-book](https://mcpp-community.github.io/d2mcpp/en) - [Video: Instructional Videos](https://youtube.com/playlist?list=PL7uow6t1QjF0ooMLkLSS96swpSuBZvoRE&si=1xHOGVIYpbzZAosI) -- [Code: Practice Code](https://github.com/mcpp-community/d2mcpp/tree/main/dslings/en) +- [Code: Practice Code](https://github.com/mcpp-community/d2mcpp/tree/main/src/cpp11/tests/en) - [X: mcpp Forum](https://forum.d2learn.org/category/20) ## Language Support diff --git a/book/en/src/base/chapter_1.md b/book/en/src/base/chapter_1.md index 802c819..0faefff 100644 --- a/book/en/src/base/chapter_1.md +++ b/book/en/src/base/chapter_1.md @@ -81,28 +81,33 @@ After entering the automated code practice environment using `d2x checker`, the ```cpp // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: dslings/hello-mcpp.cpp +// file: src/intro/tests/hello-mcpp.cpp // // Exercise: Automated Code Practice Tutorial // // Tips: -// This project uses the xlings tool to build automated code practice projects. Execute -// d2x checker in the project root directory to enter "compiler-driven development mode" -// for automatic exercise code detection. -// You need to modify errors in the code based on console error messages and hints. -// When all compilation errors and runtime checkpoints are fixed, you can delete or comment -// out the D2X_WAIT macro in the code to automatically proceed to the next exercise. +// This is an "exercises are tests" automated practice project. Two ways to play: // -// - D2X_WAIT: This macro isolates different exercises. You can delete or comment it out to proceed to the next exercise. -// - d2x_assert_eq: This macro is used for runtime checkpoints. You need to fix code errors so that all -// - D2X_YOUR_ANSWER: This macro indicates code that needs modification, typically used for code completion (replace this macro with correct code) +// d2x checker guided mode: auto-detects your edits, passing advances you +// mcpp test -p src/en/intro native mode: plain mcpp — the test report IS your progress +// +// Fix the code based on console error messages. There are only three conventions: +// +// - D2X_YOUR_ANSWER: the fill-in-the-blank placeholder — replace it with correct +// code. It is not a macro, just a name nobody defines, so the compiler error +// points exactly at the blank +// - d2x::check / d2x::check_eq: runtime checkpoints — fix the code so every +// check passes (do not delete the checkpoints) +// - d2x::wait(): the barrier between exercises — delete it once you have read +// the lesson to really finish it // // Auto-Checker Command: // // d2x checker hello-mcpp // -#include +import std; +import d2x; // You can observe "real-time" changes in the console when modifying code @@ -112,13 +117,13 @@ int main() { int a = 1.1; // 1. Fix this runtime error, change int to double to pass the check - d2x_assert_eq(a, 1.1); // 2. Runtime checkpoint, need to fix code to pass all checkpoints (cannot directly delete checkpoint code) + d2x::check_eq(a, 1.1, "a == 1.1"); // 2. Runtime checkpoint, need to fix code to pass all checkpoints (cannot directly delete checkpoint code) D2X_YOUR_ANSWER b = a; // 3. Fix this compilation error, give b an appropriate type - d2x_assert_eq(b, 1); // 4. Runtime checkpoint 2 + d2x::check_eq(b, 1, "b == 1"); // 4. Runtime checkpoint 2 - D2X_WAIT // 5. Delete or comment out this macro to proceed to the next exercise (project formal code practice) + d2x::wait(); // 5. Delete or comment out this line to proceed to the next exercise (project formal code practice) return 0; } @@ -127,23 +132,23 @@ int main() { **Console Output and Explanation** ```bash -🌏Progress: [>----------] 0/10 -->> Shows current exercise progress +🌏Progress: [>----------] 0/52 -->> Shows current exercise progress -[Target: 00-0-hello-mcpp] - normal -->> Current exercise name +[Exercise: hello-mcpp] -->> Current exercise name -❌ Error: Compilation/Running failed for dslings/hello-mcpp.cpp -->> Shows detection status +❌ Error: Compilation/Running failed for src/en/intro/tests/hello-mcpp.cpp -->> Shows detection status The code exist some error! ----------C-Output--------- - Compiler output information -[HONLY LOGW]: main: dslings/hello-mcpp.cpp:24 - ❌ | a == 1.1 (1 == 1.100000) -->> Error hint and location (line 24) -[HONLY LOGW]: main: dslings/hello-mcpp.cpp:26 - 🥳 Delete the D2X_WAIT to continue... +---------Output--------- - Compile/run output information +❌ | a == 1.1 (1 == 1.1) --> src/en/intro/tests/hello-mcpp.cpp:46 -->> Error hint and location (line 46) +🚧 | Delete the d2x::wait() to continue --> src/en/intro/tests/hello-mcpp.cpp:52 AI-Tips-Config: https://xlings.d2learn.org/en/documents/d2x/intro.html -->> AI hints (requires configuring large model key, optional) ---------E-Files--------- -dslings/hello-mcpp.cpp -->> Current detected file +src/intro/tests/hello-mcpp.cpp -->> Current detected file ------------------------- Homepage: https://github.com/openxlings/xlings @@ -168,16 +173,18 @@ Edit the `lang` attribute in the project configuration file `.d2x.json`. `zh` co If you prefer to use Neovim as your editor with LSP (clangd) support, you can configure it as follows: -**1. Edit the `editor` attribute in the project configuration file `config.xlings` and set it to `nvim` (or `zed`)** +**1. Edit the `editor` field in the project configuration file `.d2x.json` and set it to `nvim` (or `zed`)** -```bash -d2x = { - checker = { - name = "dslings", - editor = "nvim", -- option: vscode, nvim, zed - }, +```json +{ + "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", + "editor": "nvim", + ... +} ``` +> When unset, d2x falls back to `$VISUAL` → `$EDITOR` → `code`; a `{file}` placeholder is supported; an explicit empty string disables auto-open. + **2. Run the one-click dependency installation and environment configuration command in the project root directory** ```bash diff --git a/book/en/src/cpp11/00-auto-and-decltype.md b/book/en/src/cpp11/00-auto-and-decltype.md index b013da0..f0b6df7 100644 --- a/book/en/src/cpp11/00-auto-and-decltype.md +++ b/book/en/src/cpp11/00-auto-and-decltype.md @@ -12,7 +12,7 @@ auto and decltype are powerful **type deduction** tools introduced in C++11. The | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/00-auto-and-decltype.md) | [Video Explanation](https://www.bilibili.com/video/BV1EzJs6HEf7) / [Exercise Walkthrough](https://www.bilibili.com/video/BV1xkdYYUEyH) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp) | | +| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/00-auto-and-decltype.md) | [Video Explanation](https://www.bilibili.com/video/BV1EzJs6HEf7) / [Exercise Walkthrough](https://www.bilibili.com/video/BV1xkdYYUEyH) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/0.cpp) | | **Why were they introduced?** @@ -252,12 +252,12 @@ decltype( (b) ) // Deduction result is int & ### Practice Topics -- 0 - [Declaration & Definition](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp) -- 1 - [Expression Type Deduction](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-1.cpp) -- 2 - [Complex Type Deduction - Iterator / Function](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-2.cpp) -- 3 - [Function Return Type Deduction](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-3.cpp) -- 4 - [Class/Struct Member Type Deduction](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-4.cpp) -- 5 - [const & Reference Stripping and Preservation](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-5.cpp) +- 0 - [Declaration & Definition](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/0.cpp) +- 1 - [Expression Type Deduction](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/1.cpp) +- 2 - [Complex Type Deduction - Iterator / Function](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/2.cpp) +- 3 - [Function Return Type Deduction](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/3.cpp) +- 4 - [Class/Struct Member Type Deduction](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/4.cpp) +- 5 - [const & Reference Stripping and Preservation](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/5.cpp) ### Auto-Checker Commands diff --git a/book/en/src/cpp11/01-default-and-delete.md b/book/en/src/cpp11/01-default-and-delete.md index 4c6466f..72ddfff 100644 --- a/book/en/src/cpp11/01-default-and-delete.md +++ b/book/en/src/cpp11/01-default-and-delete.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/function#Deleted_functions) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/01-default-and-delete.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/01-default-and-delete-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/function#Deleted_functions) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/01-default-and-delete.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/01-default-and-delete/0.cpp) | | **Why were they introduced?** @@ -179,9 +179,9 @@ The real design value of `= default` / `= delete` shows up in combination with t ### Practice Topics -- 0 - [Explicitly control constructor generation behavior](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/01-default-and-delete-0.cpp) -- 1 - [Implement a non-copyable but movable UniquePtr](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/01-default-and-delete-1.cpp) -- 2 - [Use = delete to block a specific parameter-type overload](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/01-default-and-delete-2.cpp) +- 0 - [Explicitly control constructor generation behavior](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/01-default-and-delete/0.cpp) +- 1 - [Implement a non-copyable but movable UniquePtr](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/01-default-and-delete/1.cpp) +- 2 - [Use = delete to block a specific parameter-type overload](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/01-default-and-delete/2.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/02-final-and-override.md b/book/en/src/cpp11/02-final-and-override.md index 2d44627..c79dc24 100644 --- a/book/en/src/cpp11/02-final-and-override.md +++ b/book/en/src/cpp11/02-final-and-override.md @@ -12,7 +12,7 @@ final and override are two **context-sensitive identifiers** introduced in C++11 | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-final](https://en.cppreference.com/w/cpp/language/final) / [cppreference-override](https://en.cppreference.com/w/cpp/language/override) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/02-final-and-override.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/02-final-and-override-0.cpp) | | +| [cppreference-final](https://en.cppreference.com/w/cpp/language/final) / [cppreference-override](https://en.cppreference.com/w/cpp/language/override) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/02-final-and-override.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/02-final-and-override/0.cpp) | | **Why were they introduced?** @@ -175,9 +175,9 @@ struct B : A { ### Practice Topics -- 0 - [Familiarize with override](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/02-final-and-override-0.cpp) -- 1 - [Familiarize with final](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/02-final-and-override-1.cpp) -- 2 - [final + Template Method - AudioPlayer / WAV / MP3 / OGG](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/02-final-and-override-2.cpp) +- 0 - [Familiarize with override](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/02-final-and-override/0.cpp) +- 1 - [Familiarize with final](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/02-final-and-override/1.cpp) +- 2 - [final + Template Method - AudioPlayer / WAV / MP3 / OGG](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/02-final-and-override/2.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/03-trailing-return-type.md b/book/en/src/cpp11/03-trailing-return-type.md index 1564e43..a5f7de3 100644 --- a/book/en/src/cpp11/03-trailing-return-type.md +++ b/book/en/src/cpp11/03-trailing-return-type.md @@ -12,7 +12,7 @@ The trailing return type is a new function declaration syntax introduced in C++1 | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/function) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/03-trailing-return-type.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/03-trailing-return-type.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/function) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/03-trailing-return-type.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/03-trailing-return-type/0.cpp) | | **Why was it introduced?** @@ -166,7 +166,7 @@ auto func() -> int(*)[3]; // ok: returns a pointer to an array ### Practice Topics -- 0 - [Familiarize with trailing return type - ordinary function / template / lambda](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/03-trailing-return-type.cpp) +- 0 - [Familiarize with trailing return type - ordinary function / template / lambda](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/03-trailing-return-type/0.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/04-rvalue-references.md b/book/en/src/cpp11/04-rvalue-references.md index 33b7fce..18098b6 100644 --- a/book/en/src/cpp11/04-rvalue-references.md +++ b/book/en/src/cpp11/04-rvalue-references.md @@ -12,7 +12,7 @@ Rvalue reference `T&&` is a new kind of reference introduced in C++11 that binds | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/reference) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/04-rvalue-references.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/04-rvalue-references.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/reference) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/04-rvalue-references.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/04-rvalue-references/0.cpp) | | **Why was it introduced?** @@ -156,7 +156,7 @@ This chapter focuses on the **value-category + reference-binding** layer. The pa ### Practice Topics -- 0 - [Use an rvalue reference to extend a temporary's lifetime and mutate it](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/04-rvalue-references.cpp) +- 0 - [Use an rvalue reference to extend a temporary's lifetime and mutate it](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/04-rvalue-references/0.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/05-move-semantics.md b/book/en/src/cpp11/05-move-semantics.md index f959246..d489356 100644 --- a/book/en/src/cpp11/05-move-semantics.md +++ b/book/en/src/cpp11/05-move-semantics.md @@ -12,7 +12,7 @@ Move semantics is a **resource ownership transfer** mechanism introduced in C++1 | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-move](https://en.cppreference.com/w/cpp/utility/move) / [cppreference-move-ctor](https://en.cppreference.com/w/cpp/language/move_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/05-move-semantics.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/05-move-semantics-0.cpp) | | +| [cppreference-move](https://en.cppreference.com/w/cpp/utility/move) / [cppreference-move-ctor](https://en.cppreference.com/w/cpp/language/move_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/05-move-semantics.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/05-move-semantics/0.cpp) | | **Why was it introduced?** @@ -190,9 +190,9 @@ Similarly, a **by-value parameter** is already a fresh object, so only consider ### Practice Topics -- 0 - [Move constructor and trigger conditions - make buffer passing allocate only once](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/05-move-semantics-0.cpp) -- 1 - [Move assignment and trigger conditions - temporary / intermediate / explicit std::move](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/05-move-semantics-1.cpp) -- 2 - [Moving resources, not objects - compare object addresses vs. data pointers](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/05-move-semantics-2.cpp) +- 0 - [Move constructor and trigger conditions - make buffer passing allocate only once](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/05-move-semantics/0.cpp) +- 1 - [Move assignment and trigger conditions - temporary / intermediate / explicit std::move](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/05-move-semantics/1.cpp) +- 2 - [Moving resources, not objects - compare object addresses vs. data pointers](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/05-move-semantics/2.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/06-scoped-enums.md b/book/en/src/cpp11/06-scoped-enums.md index 69e1871..94de351 100644 --- a/book/en/src/cpp11/06-scoped-enums.md +++ b/book/en/src/cpp11/06-scoped-enums.md @@ -12,7 +12,7 @@ Scoped enums (`enum class` / `enum struct`) are strongly-typed enumerations intr | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/enum) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/06-scoped-enums.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/06-scoped-enums-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/enum) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/06-scoped-enums.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/06-scoped-enums/0.cpp) | | **Why was it introduced?** @@ -204,8 +204,8 @@ Existing codebases tend to be full of traditional enums; you don't need to rewri ### Practice Topics -- 0 - [Pitfalls of traditional enum - name clashes and implicit comparison](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/06-scoped-enums-0.cpp) -- 1 - [Basic usage of scoped enum - scoping / type safety / underlying type](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/06-scoped-enums-1.cpp) +- 0 - [Pitfalls of traditional enum - name clashes and implicit comparison](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/06-scoped-enums/0.cpp) +- 1 - [Basic usage of scoped enum - scoping / type safety / underlying type](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/06-scoped-enums/1.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/07-constexpr.md b/book/en/src/cpp11/07-constexpr.md index b5bd7c5..836bf5e 100644 --- a/book/en/src/cpp11/07-constexpr.md +++ b/book/en/src/cpp11/07-constexpr.md @@ -12,7 +12,7 @@ constexpr is a keyword introduced in C++11 that lifts "results that would normal | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/constexpr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/07-constexpr.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/07-constexpr-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/constexpr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/07-constexpr.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/07-constexpr/0.cpp) | | **Why was it introduced?** @@ -181,8 +181,8 @@ But here's a detail that's easy to miss: **constexpr functions are implicitly in ### Practice Topics -- 0 - [Compile-time computation basics: constexpr vs const](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/07-constexpr-0.cpp) -- 1 - [Applied compile-time computation: factorial / Sum / mysin auto lookup table](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/07-constexpr-1.cpp) +- 0 - [Compile-time computation basics: constexpr vs const](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/07-constexpr/0.cpp) +- 1 - [Applied compile-time computation: factorial / Sum / mysin auto lookup table](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/07-constexpr/1.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/08-literal-type.md b/book/en/src/cpp11/08-literal-type.md index 636bfbf..969bea9 100644 --- a/book/en/src/cpp11/08-literal-type.md +++ b/book/en/src/cpp11/08-literal-type.md @@ -12,7 +12,7 @@ A *literal type* (LiteralType) is the C++11 named requirement for types that are | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-LiteralType](https://en.cppreference.com/w/cpp/named_req/LiteralType) / [cppreference-user-literal](https://en.cppreference.com/w/cpp/language/user_literal) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/08-literal-type.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/08-literal-type-0.cpp) | | +| [cppreference-LiteralType](https://en.cppreference.com/w/cpp/named_req/LiteralType) / [cppreference-user-literal](https://en.cppreference.com/w/cpp/language/user_literal) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/08-literal-type.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/08-literal-type/0.cpp) | | **Why was it introduced?** @@ -178,8 +178,8 @@ LiteralType requires at least one constexpr constructor that is **not** a copy o ### Practice Topics -- 0 - [Understand the literal-type concept - what can be constexpr](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/08-literal-type-0.cpp) -- 1 - [Define your own literal type - add a constexpr constructor to Vector](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/08-literal-type-1.cpp) +- 0 - [Understand the literal-type concept - what can be constexpr](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/08-literal-type/0.cpp) +- 1 - [Define your own literal type - add a constexpr constructor to Vector](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/08-literal-type/1.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/09-list-initialization.md b/book/en/src/cpp11/09-list-initialization.md index ed7d914..a887e93 100644 --- a/book/en/src/cpp11/09-list-initialization.md +++ b/book/en/src/cpp11/09-list-initialization.md @@ -12,7 +12,7 @@ List initialization is an initialization style that uses `{ arg1, arg2, ... }` l | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/list_initialization.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/09-list-initialization.md) | [Video Explanation](https://www.bilibili.com/video/BV1vKuQzkEo2) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/09-list-initialization-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/list_initialization.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/09-list-initialization.md) | [Video Explanation](https://www.bilibili.com/video/BV1vKuQzkEo2) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/09-list-initialization/0.cpp) | | **Why was it introduced?** diff --git a/book/en/src/cpp11/10-delegating-constructors.md b/book/en/src/cpp11/10-delegating-constructors.md index 98d7a47..5bf6d88 100644 --- a/book/en/src/cpp11/10-delegating-constructors.md +++ b/book/en/src/cpp11/10-delegating-constructors.md @@ -12,7 +12,7 @@ Delegating constructors are syntactic sugar introduced in C++11. Through simple | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/initializer_list.html#Delegating_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/10-delegating-constructors.md) | [Video Explanation](https://www.bilibili.com/video/BV1zft3zSEER) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/10-delegating-constructors-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/initializer_list.html#Delegating_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/10-delegating-constructors.md) | [Video Explanation](https://www.bilibili.com/video/BV1zft3zSEER) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/10-delegating-constructors/0.cpp) | | **Why was it introduced?** diff --git a/book/en/src/cpp11/11-inherited-constructors.md b/book/en/src/cpp11/11-inherited-constructors.md index f179701..18c6b32 100644 --- a/book/en/src/cpp11/11-inherited-constructors.md +++ b/book/en/src/cpp11/11-inherited-constructors.md @@ -12,7 +12,7 @@ Inherited constructors are a syntactic feature introduced in C++11 that solves t | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/using_declaration.html#Inheriting_constructors) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/11-inherited-constructors.md) | [Bili](https://www.bilibili.com/video/BV1bspBzFEEC) / [Youtube](https://youtu.be/p7vbY8XUKnY?si=GZUn9GSW68aU94A6) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/11-inherited-constructors-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/using_declaration.html#Inheriting_constructors) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/11-inherited-constructors.md) | [Bili](https://www.bilibili.com/video/BV1bspBzFEEC) / [Youtube](https://youtu.be/p7vbY8XUKnY?si=GZUn9GSW68aU94A6) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/11-inherited-constructors/0.cpp) | | **Why was it introduced?** @@ -222,9 +222,9 @@ For example, for testing environments or scenarios involving **only functional e ### Practice Code Topics -- 0 - [Familiarize with Inherited Constructors Feature](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/11-inherited-constructors-0.cpp) -- 1 - [Application in Functional Extension - StudentTest](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/11-inherited-constructors-1.cpp) -- 2 - [Application in Generic Templates - NoCopy / NoMove Behavior Constraints](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/11-inherited-constructors-2.cpp) +- 0 - [Familiarize with Inherited Constructors Feature](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/11-inherited-constructors/0.cpp) +- 1 - [Application in Functional Extension - StudentTest](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/11-inherited-constructors/1.cpp) +- 2 - [Application in Generic Templates - NoCopy / NoMove Behavior Constraints](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/11-inherited-constructors/2.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/12-nullptr.md b/book/en/src/cpp11/12-nullptr.md index 71b451d..a53c09b 100644 --- a/book/en/src/cpp11/12-nullptr.md +++ b/book/en/src/cpp11/12-nullptr.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/nullptr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/12-nullptr.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/12-nullptr-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/nullptr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/12-nullptr.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/12-nullptr/0.cpp) | | **Why was it introduced?** @@ -178,9 +178,9 @@ bool isEmpty = (ptr == nullptr); // true ### Practice Code Topics -- 0 - [nullptr Basic Usage](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/12-nullptr-0.cpp) -- 1 - [nullptr Function Overloading](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/12-nullptr-1.cpp) -- 2 - [nullptr Advantages in Template Programming](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/12-nullptr-2.cpp) +- 0 - [nullptr Basic Usage](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/12-nullptr/0.cpp) +- 1 - [nullptr Function Overloading](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/12-nullptr/1.cpp) +- 2 - [nullptr Advantages in Template Programming](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/12-nullptr/2.cpp) ### Auto-Checker Command diff --git a/book/en/src/cpp11/13-long-long.md b/book/en/src/cpp11/13-long-long.md index e1b976d..6722c01 100644 --- a/book/en/src/cpp11/13-long-long.md +++ b/book/en/src/cpp11/13-long-long.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/types) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/13-long-long.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/13-long-long-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/types) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/13-long-long.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/13-long-long/0.cpp) | | **Why was it introduced?** @@ -109,8 +109,8 @@ if (bigValue > std::numeric_limits::max() || bigValue < std::numeric_limits ### Practice Code Topics -- 0 - [long long Basic Usage](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/13-long-long-0.cpp) -- 1 - [long long Large Number Applications and Boundary Values](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/13-long-long-1.cpp) +- 0 - [long long Basic Usage](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/13-long-long/0.cpp) +- 1 - [long long Large Number Applications and Boundary Values](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/13-long-long/1.cpp) ### Auto-Checker Command diff --git a/book/en/src/cpp11/14-type-alias.md b/book/en/src/cpp11/14-type-alias.md index d35abe8..fe1de1d 100644 --- a/book/en/src/cpp11/14-type-alias.md +++ b/book/en/src/cpp11/14-type-alias.md @@ -12,7 +12,7 @@ Type alias and alias template are important features introduced in C++11, used t | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-type-alias](https://en.cppreference.com/w/cpp/language/type_alias) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/14-type-alias.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/14-type-alias-0.cpp) | | +| [cppreference-type-alias](https://en.cppreference.com/w/cpp/language/type_alias) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/14-type-alias.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/14-type-alias/0.cpp) | | > Note: The `using` keyword existed before C++11, but was mainly used for namespace and class member declarations > - Declaring namespaces: `using namespace std;` @@ -192,10 +192,10 @@ struct A { ### Exercise Code Topics -- 0 - [Basic Type Alias](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/14-type-alias-0.cpp) -- 1 - [Complex Types and Function Pointer Aliases](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/14-type-alias-1.cpp) -- 2 - [Alias Template Basics](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/14-type-alias-2.cpp) -- 3 - [Alias Template Applications in Standard Library](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/14-type-alias-3.cpp) +- 0 - [Basic Type Alias](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/14-type-alias/0.cpp) +- 1 - [Complex Types and Function Pointer Aliases](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/14-type-alias/1.cpp) +- 2 - [Alias Template Basics](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/14-type-alias/2.cpp) +- 3 - [Alias Template Applications in Standard Library](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/14-type-alias/3.cpp) ### Exercise Code Auto-Check Command diff --git a/book/en/src/cpp11/15-variadic-templates.md b/book/en/src/cpp11/15-variadic-templates.md index 54a4510..06724c0 100644 --- a/book/en/src/cpp11/15-variadic-templates.md +++ b/book/en/src/cpp11/15-variadic-templates.md @@ -12,7 +12,7 @@ Variadic templates are a core template feature introduced in C++11. They allow f | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/parameter_pack) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/15-variadic-templates.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/15-variadic-templates-0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/parameter_pack) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/15-variadic-templates.md) | [Video Explanation]() | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/15-variadic-templates/0.cpp) | | **Why was it introduced?** @@ -228,8 +228,8 @@ If your project allows C++17, new code should prefer fold expressions + `if cons ### Practice Topics -- 0 - [Variadic templates basics - recursive print](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/15-variadic-templates-0.cpp) -- 1 - [Variadic template sum](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/15-variadic-templates-1.cpp) +- 0 - [Variadic templates basics - recursive print](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/15-variadic-templates/0.cpp) +- 1 - [Variadic template sum](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/15-variadic-templates/1.cpp) ### Practice Code Auto-detection Command diff --git a/book/en/src/cpp11/16-generalized-unions.md b/book/en/src/cpp11/16-generalized-unions.md index 26ef95c..b2ba98b 100644 --- a/book/en/src/cpp11/16-generalized-unions.md +++ b/book/en/src/cpp11/16-generalized-unions.md @@ -15,7 +15,7 @@ The size of a union is at least large enough to hold the largest data member. | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp) | | +| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/16-generalized-unions/0.cpp) | | **Why introduced?** @@ -224,9 +224,9 @@ double c = m.b; // Error: undefined behavior. ### Exercise Code Topics -- 0 - [Union Default Member Initialization](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/16-generalized-unions-0.cpp) -- 1 - [Union with Non-Trivial Types and Lifecycle Management](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/16-generalized-unions-1.cpp) -- 2 - [Tagged Discriminated Union — A Simplified std::variant with enum + union](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/16-generalized-unions-2.cpp) +- 0 - [Union Default Member Initialization](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/16-generalized-unions/0.cpp) +- 1 - [Union with Non-Trivial Types and Lifecycle Management](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/16-generalized-unions/1.cpp) +- 2 - [Tagged Discriminated Union — A Simplified std::variant with enum + union](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/16-generalized-unions/2.cpp) ### Exercise Auto-Checker Command diff --git a/book/en/src/cpp11/17-pod-type.md b/book/en/src/cpp11/17-pod-type.md index defd3d7..76b92db 100644 --- a/book/en/src/cpp11/17-pod-type.md +++ b/book/en/src/cpp11/17-pod-type.md @@ -13,7 +13,7 @@ In C++, there is a corresponding category of **POD types** whose memory layout i | Book | Video | Code | Forum | | --- | --- | --- | --- | -| [cppreference-PODType](https://cppreference.com/w/cpp/named_req/PODType.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/17-pod-type.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/17-pod-type-0.cpp) | [Discussion Forum](https://forum.d2learn.org/category/20) | +| [cppreference-PODType](https://cppreference.com/w/cpp/named_req/PODType.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/17-pod-type.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/17-pod-type/0.cpp) | [Discussion Forum](https://forum.d2learn.org/category/20) | > Note: since C++20, the “PODType” notion in the standard has been marked **deprecated**. > The standard library now prefers more fine-grained categories such as `TrivialType`, `StandardLayoutType`, and `ScalarType` to describe related requirements. @@ -140,9 +140,9 @@ In new code, it is better to express constraints based on actual needs, for exam ### Exercise topics -- 0 – [Use type traits to check POD / trivial / standard-layout](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/17-pod-type-0.cpp) -- 1 – [Simulate byte-wise copying of a POD struct and observe the behavior](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/17-pod-type-1.cpp) -- 2 – [Adapt C++ message types to C interface using POD headers](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/17-pod-type-2.cpp) +- 0 – [Use type traits to check POD / trivial / standard-layout](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/17-pod-type/0.cpp) +- 1 – [Simulate byte-wise copying of a POD struct and observe the behavior](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/17-pod-type/1.cpp) +- 2 – [Adapt C++ message types to C interface using POD headers](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp11/tests/17-pod-type/2.cpp) ### Auto-check command diff --git a/book/en/src/cpp14/00-generic-lambdas.md b/book/en/src/cpp14/00-generic-lambdas.md index 612e50e..aea77e0 100644 --- a/book/en/src/cpp14/00-generic-lambdas.md +++ b/book/en/src/cpp14/00-generic-lambdas.md @@ -12,7 +12,7 @@ C++14 allows lambda parameters to use `auto`, turning the lambda's `operator()` | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-lambda](https://en.cppreference.com/w/cpp/language/lambda) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp14/00-generic-lambdas.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp14/00-generic-lambdas-0.cpp) | | +| [cppreference-lambda](https://en.cppreference.com/w/cpp/language/lambda) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp14/00-generic-lambdas.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp14/tests/00-generic-lambdas/0.cpp) | | **Why introduced?** @@ -191,8 +191,8 @@ C++14 generic lambdas support parameter packs — `[](auto... xs)` accepts any n ### Exercise Code Topics -- 0 - [Basic Generic Lambda — auto parameters and type deduction](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp14/00-generic-lambdas-0.cpp) -- 1 - [Generic Lambda with STL Algorithms — sort, find, factory function](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp14/00-generic-lambdas-1.cpp) +- 0 - [Basic Generic Lambda — auto parameters and type deduction](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp14/tests/00-generic-lambdas/0.cpp) +- 1 - [Generic Lambda with STL Algorithms — sort, find, factory function](https://github.com/mcpp-community/d2mcpp/blob/main/src/en/cpp14/tests/00-generic-lambdas/1.cpp) ### Exercise Auto-Checker Command diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index 4f2ec6c..58c617b 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -81,7 +81,7 @@ d2x update ```cpp // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: intro/tests/hello-mcpp.cpp +// file: src/intro/tests/hello-mcpp.cpp // // Exercise/练习: 自动化代码练习使用教学 // @@ -89,7 +89,7 @@ d2x update // 这是一个「练习即测试」的自动化代码练习项目。两种玩法任选: // // d2x checker 闯关模式: 自动检测、通过即进入下一题 -// mcpp test -p intro 原生模式: 直接用 mcpp 跑, 测试结果就是进度表 +// mcpp test -p src/intro 原生模式: 直接用 mcpp 跑, 测试结果就是进度表 // // 你需要根据控制台的报错和提示信息修改代码。约定只有三个: // @@ -105,7 +105,7 @@ d2x update // import std; -import d2x.harness; +import d2x; // 修改代码时可以观察到控制台"实时"的变化 @@ -134,19 +134,19 @@ int main() { [Exercise: hello-mcpp] -->> 当前的练习名 -❌ Error: Compilation/Running failed for intro/tests/hello-mcpp.cpp -->> 显示检测状态 +❌ Error: Compilation/Running failed for src/intro/tests/hello-mcpp.cpp -->> 显示检测状态 The code exist some error! ---------Output--------- - 编译/运行输出信息 -[HONLY LOGW]: intro/tests/hello-mcpp.cpp:41 - ❌ | a == 1.1 (1 == 1.1) -->> 错误提示及位置(41行) -[HONLY LOGW]: intro/tests/hello-mcpp.cpp:47 - 🥳 Delete the d2x::wait() to continue... +❌ | a == 1.1 (1 == 1.1) --> src/intro/tests/hello-mcpp.cpp:41 -->> 错误提示及位置(41行) +🚧 | Delete the d2x::wait() to continue --> src/intro/tests/hello-mcpp.cpp:47 AI-Tips-Config: https://xlings.d2learn.org/documents/d2x/intro.html -->> AI提示(需要配置大模型的key, 可不使用) ---------E-Files--------- -intro/tests/hello-mcpp.cpp -->> 当前检测的文件 +src/intro/tests/hello-mcpp.cpp -->> 当前检测的文件 ------------------------- Homepage: https://github.com/openxlings/xlings @@ -171,17 +171,18 @@ Homepage: https://github.com/openxlings/xlings 如果你希望使用 Neovim 编辑器并获得 LSP(clangd)支持, 可以按如下步骤进行配置 -**1.编辑项目配置文件`config.xlings`中的`editor`属性, 设置为`nvim` (或`zed`)** +**1.编辑项目配置文件`.d2x.json`中的`editor`字段, 设置为`nvim` (或`zed`)** -```bash +```json { - "version": "0.1.1", "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", - "lang": "en", < -- 修改这里 + "editor": "nvim", ... } ``` +> 未配置时按 `$VISUAL` → `$EDITOR` → `code` 回退;支持 `{file}` 占位符;显式配空串表示关闭自动打开。 + **2.在项目根目录运行一键依赖安装和环境配置命令** ```bash diff --git a/book/src/cpp11/00-auto-and-decltype.md b/book/src/cpp11/00-auto-and-decltype.md index 0ab8719..d6636f4 100644 --- a/book/src/cpp11/00-auto-and-decltype.md +++ b/book/src/cpp11/00-auto-and-decltype.md @@ -12,7 +12,7 @@ auto 和 decltype 是C++11引入的强有力的**类型自动推导**工具. 不 | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/00-auto-and-decltype.md) | [视频解读](https://www.bilibili.com/video/BV1EzJs6HEf7) / [练习讲解](https://www.bilibili.com/video/BV1xkdYYUEyH) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/0.cpp) | | +| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/00-auto-and-decltype.md) | [视频解读](https://www.bilibili.com/video/BV1EzJs6HEf7) / [练习讲解](https://www.bilibili.com/video/BV1xkdYYUEyH) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/0.cpp) | | **为什么引入?** @@ -252,12 +252,12 @@ decltype( (b) ) // 推导结果是 int & ### 练习代码主题 -- 0 - [声明定义](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/0.cpp) -- 1 - [表达式类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/1.cpp) -- 2 - [复杂类型推导 - 迭代器 / 函数](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/2.cpp) -- 3 - [函数返回值类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/3.cpp) -- 4 - [类/结构体成员类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/4.cpp) -- 5 - [const 与引用的剥离和保留](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/00-auto-and-decltype/5.cpp) +- 0 - [声明定义](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/0.cpp) +- 1 - [表达式类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/1.cpp) +- 2 - [复杂类型推导 - 迭代器 / 函数](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/2.cpp) +- 3 - [函数返回值类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/3.cpp) +- 4 - [类/结构体成员类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/4.cpp) +- 5 - [const 与引用的剥离和保留](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/00-auto-and-decltype/5.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/01-default-and-delete.md b/book/src/cpp11/01-default-and-delete.md index d49b2d2..0d92170 100644 --- a/book/src/cpp11/01-default-and-delete.md +++ b/book/src/cpp11/01-default-and-delete.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/function#Deleted_functions) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/01-default-and-delete.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/01-default-and-delete/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/function#Deleted_functions) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/01-default-and-delete.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/01-default-and-delete/0.cpp) | | **为什么引入?** @@ -217,9 +217,9 @@ only_int(1.0); // 错误: 调用 deleted function ### 练习代码主题 -- 0 - [显式指定构造函数生成行为](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/01-default-and-delete/0.cpp) -- 1 - [实现不可拷贝但可移动的 UniquePtr](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/01-default-and-delete/1.cpp) -- 2 - [用 = delete 屏蔽特定参数类型的重载](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/01-default-and-delete/2.cpp) +- 0 - [显式指定构造函数生成行为](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/01-default-and-delete/0.cpp) +- 1 - [实现不可拷贝但可移动的 UniquePtr](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/01-default-and-delete/1.cpp) +- 2 - [用 = delete 屏蔽特定参数类型的重载](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/01-default-and-delete/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/02-final-and-override.md b/book/src/cpp11/02-final-and-override.md index e6fe2ee..6049852 100644 --- a/book/src/cpp11/02-final-and-override.md +++ b/book/src/cpp11/02-final-and-override.md @@ -12,7 +12,7 @@ final 和 override 是 C++11 引入的两个 **上下文相关标识符**, 用 | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-final](https://en.cppreference.com/w/cpp/language/final) / [cppreference-override](https://en.cppreference.com/w/cpp/language/override) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/02-final-and-override.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/02-final-and-override/0.cpp) | | +| [cppreference-final](https://en.cppreference.com/w/cpp/language/final) / [cppreference-override](https://en.cppreference.com/w/cpp/language/override) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/02-final-and-override.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/02-final-and-override/0.cpp) | | **为什么引入?** @@ -174,9 +174,9 @@ struct B : A { ### 练习代码主题 -- 0 - [熟悉 override 的使用](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/02-final-and-override/0.cpp) -- 1 - [熟悉 final 的使用](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/02-final-and-override/1.cpp) -- 2 - [final + 模板方法 - AudioPlayer / WAV / MP3 / OGG](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/02-final-and-override/2.cpp) +- 0 - [熟悉 override 的使用](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/02-final-and-override/0.cpp) +- 1 - [熟悉 final 的使用](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/02-final-and-override/1.cpp) +- 2 - [final + 模板方法 - AudioPlayer / WAV / MP3 / OGG](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/02-final-and-override/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/03-trailing-return-type.md b/book/src/cpp11/03-trailing-return-type.md index 08c0889..042274e 100644 --- a/book/src/cpp11/03-trailing-return-type.md +++ b/book/src/cpp11/03-trailing-return-type.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/function) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/03-trailing-return-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/03-trailing-return-type/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/function) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/03-trailing-return-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/03-trailing-return-type/0.cpp) | | **为什么引入?** @@ -166,7 +166,7 @@ auto func() -> int(*)[3]; // ok: 返回数组指针 ### 练习代码主题 -- 0 - [熟悉尾置返回类型 - 普通函数 / 模板 / lambda 三种形态](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/03-trailing-return-type/0.cpp) +- 0 - [熟悉尾置返回类型 - 普通函数 / 模板 / lambda 三种形态](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/03-trailing-return-type/0.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/04-rvalue-references.md b/book/src/cpp11/04-rvalue-references.md index 664b2fc..92a4b07 100644 --- a/book/src/cpp11/04-rvalue-references.md +++ b/book/src/cpp11/04-rvalue-references.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/reference) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/04-rvalue-references.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/04-rvalue-references/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/reference) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/04-rvalue-references.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/04-rvalue-references/0.cpp) | | **为什么引入?** @@ -156,7 +156,7 @@ template void h(T&&); // 万能引用, 左右值都接受 ### 练习代码主题 -- 0 - [使用右值引用延长临时对象生命周期并修改其值](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/04-rvalue-references/0.cpp) +- 0 - [使用右值引用延长临时对象生命周期并修改其值](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/04-rvalue-references/0.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/05-move-semantics.md b/book/src/cpp11/05-move-semantics.md index 423c47e..f0adf59 100644 --- a/book/src/cpp11/05-move-semantics.md +++ b/book/src/cpp11/05-move-semantics.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-move](https://en.cppreference.com/w/cpp/utility/move) / [cppreference-move-ctor](https://en.cppreference.com/w/cpp/language/move_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/05-move-semantics.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/05-move-semantics/0.cpp) | | +| [cppreference-move](https://en.cppreference.com/w/cpp/utility/move) / [cppreference-move-ctor](https://en.cppreference.com/w/cpp/language/move_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/05-move-semantics.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/05-move-semantics/0.cpp) | | **为什么引入?** @@ -187,9 +187,9 @@ Buffer bad() { ### 练习代码主题 -- 0 - [移动构造与触发时机 - 让 buff 传递只做一次资源分配](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/05-move-semantics/0.cpp) -- 1 - [移动赋值与触发时机 - 临时对象 / 中间对象 / 显式 std::move 三种场景](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/05-move-semantics/1.cpp) -- 2 - [移动的是资源而不是对象 - 对比对象地址和 data 指针](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/05-move-semantics/2.cpp) +- 0 - [移动构造与触发时机 - 让 buff 传递只做一次资源分配](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/05-move-semantics/0.cpp) +- 1 - [移动赋值与触发时机 - 临时对象 / 中间对象 / 显式 std::move 三种场景](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/05-move-semantics/1.cpp) +- 2 - [移动的是资源而不是对象 - 对比对象地址和 data 指针](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/05-move-semantics/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/06-scoped-enums.md b/book/src/cpp11/06-scoped-enums.md index 3e0bbc9..193fe40 100644 --- a/book/src/cpp11/06-scoped-enums.md +++ b/book/src/cpp11/06-scoped-enums.md @@ -12,7 +12,7 @@ scoped enum (`enum class` / `enum struct`) 是 C++11 引入的强类型枚举, | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/enum) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/06-scoped-enums.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/06-scoped-enums/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/enum) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/06-scoped-enums.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/06-scoped-enums/0.cpp) | | **为什么引入?** @@ -204,8 +204,8 @@ constexpr Perm operator|(Perm a, Perm b) { ### 练习代码主题 -- 0 - [传统 enum 的潜在问题 - 名字冲突与隐式比较](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/06-scoped-enums/0.cpp) -- 1 - [scoped enum 基本用法 - 作用域 / 类型安全 / 底层类型](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/06-scoped-enums/1.cpp) +- 0 - [传统 enum 的潜在问题 - 名字冲突与隐式比较](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/06-scoped-enums/0.cpp) +- 1 - [scoped enum 基本用法 - 作用域 / 类型安全 / 底层类型](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/06-scoped-enums/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/07-constexpr.md b/book/src/cpp11/07-constexpr.md index 8b70b32..ea6a3da 100644 --- a/book/src/cpp11/07-constexpr.md +++ b/book/src/cpp11/07-constexpr.md @@ -12,7 +12,7 @@ constexpr 是 C++11 引入的关键字, 用于把"原本要等到运行期才能 | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/constexpr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/07-constexpr.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/07-constexpr/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/constexpr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/07-constexpr.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/07-constexpr/0.cpp) | | **为什么引入?** @@ -181,8 +181,8 @@ constexpr 把计算搬到编译期, 也意味着错误诊断、调试栈信息 ### 练习代码主题 -- 0 - [编译期计算基础: constexpr 和 const 的区别](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/07-constexpr/0.cpp) -- 1 - [编译期计算应用示例: factorial / Sum / mysin 自动打表](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/07-constexpr/1.cpp) +- 0 - [编译期计算基础: constexpr 和 const 的区别](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/07-constexpr/0.cpp) +- 1 - [编译期计算应用示例: factorial / Sum / mysin 自动打表](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/07-constexpr/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/08-literal-type.md b/book/src/cpp11/08-literal-type.md index cc006ca..8cba86c 100644 --- a/book/src/cpp11/08-literal-type.md +++ b/book/src/cpp11/08-literal-type.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-LiteralType](https://en.cppreference.com/w/cpp/named_req/LiteralType) / [cppreference-user-literal](https://en.cppreference.com/w/cpp/language/user_literal) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/08-literal-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/08-literal-type/0.cpp) | | +| [cppreference-LiteralType](https://en.cppreference.com/w/cpp/named_req/LiteralType) / [cppreference-user-literal](https://en.cppreference.com/w/cpp/language/user_literal) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/08-literal-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/08-literal-type/0.cpp) | | **为什么引入?** @@ -178,8 +178,8 @@ LiteralType 要求至少有一个 **不是拷贝 / 移动** 的 constexpr 构造 ### 练习代码主题 -- 0 - [理解字面值类型的概念 - 哪些能 constexpr](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/08-literal-type/0.cpp) -- 1 - [自定义字面值类型 - 给 Vector 加 constexpr 构造](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/08-literal-type/1.cpp) +- 0 - [理解字面值类型的概念 - 哪些能 constexpr](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/08-literal-type/0.cpp) +- 1 - [自定义字面值类型 - 给 Vector 加 constexpr 构造](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/08-literal-type/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/09-list-initialization.md b/book/src/cpp11/09-list-initialization.md index fbac26f..ea54703 100644 --- a/book/src/cpp11/09-list-initialization.md +++ b/book/src/cpp11/09-list-initialization.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/list_initialization.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/09-list-initialization.md) | [视频解读](https://www.bilibili.com/video/BV1vKuQzkEo2) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/09-list-initialization/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/list_initialization.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/09-list-initialization.md) | [视频解读](https://www.bilibili.com/video/BV1vKuQzkEo2) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/09-list-initialization/0.cpp) | | **为什么引入?** diff --git a/book/src/cpp11/10-delegating-constructors.md b/book/src/cpp11/10-delegating-constructors.md index 09aec06..e52e2c9 100644 --- a/book/src/cpp11/10-delegating-constructors.md +++ b/book/src/cpp11/10-delegating-constructors.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/initializer_list.html#Delegating_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/10-delegating-constructors.md) | [视频解读](https://www.bilibili.com/video/BV1zft3zSEER) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/10-delegating-constructors/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/initializer_list.html#Delegating_constructor) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/10-delegating-constructors.md) | [视频解读](https://www.bilibili.com/video/BV1zft3zSEER) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/10-delegating-constructors/0.cpp) | | **为什么引入?** diff --git a/book/src/cpp11/11-inherited-constructors.md b/book/src/cpp11/11-inherited-constructors.md index 0711dd7..0784c4a 100644 --- a/book/src/cpp11/11-inherited-constructors.md +++ b/book/src/cpp11/11-inherited-constructors.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/using_declaration.html#Inheriting_constructors) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/11-inherited-constructors.md) | [Bili](https://www.bilibili.com/video/BV1bspBzFEEC) / [Youtube](https://youtu.be/p7vbY8XUKnY?si=GZUn9GSW68aU94A6) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/11-inherited-constructors/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/using_declaration.html#Inheriting_constructors) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/11-inherited-constructors.md) | [Bili](https://www.bilibili.com/video/BV1bspBzFEEC) / [Youtube](https://youtu.be/p7vbY8XUKnY?si=GZUn9GSW68aU94A6) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/11-inherited-constructors/0.cpp) | | **为什么引入?** @@ -224,9 +224,9 @@ auto p4 = p2; // error (不能拷贝) ### 练习代码主题 -- 0 - [熟悉继承构造函数特性](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/11-inherited-constructors/0.cpp) -- 1 - [在功能扩展中的应用 - StudentTest](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/11-inherited-constructors/1.cpp) -- 2 - [在泛型模板中的应用 - NoCopy / NoMove 行为约束](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/11-inherited-constructors/2.cpp) +- 0 - [熟悉继承构造函数特性](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/11-inherited-constructors/0.cpp) +- 1 - [在功能扩展中的应用 - StudentTest](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/11-inherited-constructors/1.cpp) +- 2 - [在泛型模板中的应用 - NoCopy / NoMove 行为约束](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/11-inherited-constructors/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/12-nullptr.md b/book/src/cpp11/12-nullptr.md index 7a9c0aa..3fa767f 100644 --- a/book/src/cpp11/12-nullptr.md +++ b/book/src/cpp11/12-nullptr.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/nullptr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/12-nullptr.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/12-nullptr/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/nullptr) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/12-nullptr.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/12-nullptr/0.cpp) | | **为什么引入?** @@ -193,9 +193,9 @@ bool isEmpty = (ptr == nullptr); // true ### 练习代码主题 -- 0 - [nullptr基础用法](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/12-nullptr/0.cpp) -- 1 - [nullptr的函数重载](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/12-nullptr/1.cpp) -- 2 - [nullptr在模板编程中的优势](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/12-nullptr/2.cpp) +- 0 - [nullptr基础用法](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/12-nullptr/0.cpp) +- 1 - [nullptr的函数重载](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/12-nullptr/1.cpp) +- 2 - [nullptr在模板编程中的优势](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/12-nullptr/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/13-long-long.md b/book/src/cpp11/13-long-long.md index 3854f86..2ed5b68 100644 --- a/book/src/cpp11/13-long-long.md +++ b/book/src/cpp11/13-long-long.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/types) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/13-long-long.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/13-long-long/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/types) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/13-long-long.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/13-long-long/0.cpp) | | **为什么引入?** @@ -109,8 +109,8 @@ if (bigValue > std::numeric_limits::max() || bigValue < std::numeric_limits ### 练习代码主题 -- 0 - [long long基础用法](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/13-long-long/0.cpp) -- 1 - [long long大数应用和边界值](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/13-long-long/1.cpp) +- 0 - [long long基础用法](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/13-long-long/0.cpp) +- 1 - [long long大数应用和边界值](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/13-long-long/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/14-type-alias.md b/book/src/cpp11/14-type-alias.md index 5487a45..745fcd9 100644 --- a/book/src/cpp11/14-type-alias.md +++ b/book/src/cpp11/14-type-alias.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-type-alias](https://en.cppreference.com/w/cpp/language/type_alias) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/14-type-alias.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/0.cpp) | | +| [cppreference-type-alias](https://en.cppreference.com/w/cpp/language/type_alias) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/14-type-alias.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/14-type-alias/0.cpp) | | > 注: `using`关键字在C++11之前就已经存在, 但当时主要是作为命名空间和类成员声明来使用的 > - 声明命名空间: `using namespace std;` @@ -192,10 +192,10 @@ struct A { ### 练习代码主题 -- 0 - [基本类型别名](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/0.cpp) -- 1 - [复杂类型和函数指针别名](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/1.cpp) -- 2 - [别名模板基础](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/2.cpp) -- 3 - [标准库中的别名模板应用](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/14-type-alias/3.cpp) +- 0 - [基本类型别名](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/14-type-alias/0.cpp) +- 1 - [复杂类型和函数指针别名](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/14-type-alias/1.cpp) +- 2 - [别名模板基础](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/14-type-alias/2.cpp) +- 3 - [标准库中的别名模板应用](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/14-type-alias/3.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/15-variadic-templates.md b/book/src/cpp11/15-variadic-templates.md index e33f441..8a3a646 100644 --- a/book/src/cpp11/15-variadic-templates.md +++ b/book/src/cpp11/15-variadic-templates.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference](https://en.cppreference.com/w/cpp/language/parameter_pack) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/15-variadic-templates.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/15-variadic-templates/0.cpp) | | +| [cppreference](https://en.cppreference.com/w/cpp/language/parameter_pack) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/15-variadic-templates.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/15-variadic-templates/0.cpp) | | **为什么引入?** @@ -228,8 +228,8 @@ auto save(Args&&... args) { ### 练习代码主题 -- 0 - [可变参数模板基础 - 递归展开 print](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/15-variadic-templates/0.cpp) -- 1 - [可变参数模板求和 - sum](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/15-variadic-templates/1.cpp) +- 0 - [可变参数模板基础 - 递归展开 print](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/15-variadic-templates/0.cpp) +- 1 - [可变参数模板求和 - sum](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/15-variadic-templates/1.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/16-generalized-unions.md b/book/src/cpp11/16-generalized-unions.md index a8c0fc3..d098a4d 100644 --- a/book/src/cpp11/16-generalized-unions.md +++ b/book/src/cpp11/16-generalized-unions.md @@ -14,7 +14,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/16-generalized-unions/0.cpp) | | +| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/16-generalized-unions/0.cpp) | | **为什么引入** @@ -222,9 +222,9 @@ double c = m.b; // 错误:未定义行为 ### 练习代码主题 -- 0 - [联合体默认成员初始化 - 最多一个变体成员可带默认初始化器](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/16-generalized-unions/0.cpp) -- 1 - [联合体包含非平凡类型及生命周期管理 - placement new 构造 / 显式析构](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/16-generalized-unions/1.cpp) -- 2 - [带标签的鉴别联合体 - 用 enum + union 实现简易 variant](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/16-generalized-unions/2.cpp) +- 0 - [联合体默认成员初始化 - 最多一个变体成员可带默认初始化器](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/16-generalized-unions/0.cpp) +- 1 - [联合体包含非平凡类型及生命周期管理 - placement new 构造 / 显式析构](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/16-generalized-unions/1.cpp) +- 2 - [带标签的鉴别联合体 - 用 enum + union 实现简易 variant](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/16-generalized-unions/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp11/17-pod-type.md b/book/src/cpp11/17-pod-type.md index ec582b3..8ffc389 100644 --- a/book/src/cpp11/17-pod-type.md +++ b/book/src/cpp11/17-pod-type.md @@ -13,7 +13,7 @@ C++ 早期标准为了描述这类“行为上接近 C struct 的类型”,引 | 书籍 | 视频 | 代码 | 交流 | | --- | --- | --- | --- | -| [cppreference-PODType](https://cppreference.com/w/cpp/named_req/PODType.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/17-pod-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/17-pod-type/0.cpp) | [论坛讨论](https://forum.d2learn.org/category/20) | +| [cppreference-PODType](https://cppreference.com/w/cpp/named_req/PODType.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/17-pod-type.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/17-pod-type/0.cpp) | [论坛讨论](https://forum.d2learn.org/category/20) | > 注意:从 C++20 开始,标准中的 “PODType” 概念已被标记为**弃用**。标准库更倾向于使用更细化的类别,如 `TrivialType`、`StandardLayoutType`、`ScalarType` 等来描述相关需求。 @@ -140,9 +140,9 @@ void pod_only_copy(const T& src, T& dst) { ### 练习代码主题 -- 0 - [使用类型特征判断 POD / trivial / standard layout](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/17-pod-type/0.cpp) -- 1 - [模拟按字节拷贝 POD 结构体,体会其行为](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/17-pod-type/1.cpp) -- 2 - [为 C 接口传入合适的 POD 类型数据](https://github.com/mcpp-community/d2mcpp/blob/main/cpp11/tests/17-pod-type/2.cpp) +- 0 - [使用类型特征判断 POD / trivial / standard layout](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/17-pod-type/0.cpp) +- 1 - [模拟按字节拷贝 POD 结构体,体会其行为](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/17-pod-type/1.cpp) +- 2 - [为 C 接口传入合适的 POD 类型数据](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp11/tests/17-pod-type/2.cpp) ### 练习代码自动检测命令 diff --git a/book/src/cpp14/00-generic-lambdas.md b/book/src/cpp14/00-generic-lambdas.md index 44d0b13..a2a8d9d 100644 --- a/book/src/cpp14/00-generic-lambdas.md +++ b/book/src/cpp14/00-generic-lambdas.md @@ -12,7 +12,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [cppreference-lambda](https://en.cppreference.com/w/cpp/language/lambda) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/00-generic-lambdas.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/cpp14/tests/00-generic-lambdas/0.cpp) | | +| [cppreference-lambda](https://en.cppreference.com/w/cpp/language/lambda) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp14/00-generic-lambdas.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp14/tests/00-generic-lambdas/0.cpp) | | **为什么引入?** @@ -191,8 +191,8 @@ C++14 的泛型 lambda 支持参数包 — `[](auto... xs)` 可以接受任意 ### 练习代码主题 -- 0 - [泛型 lambda 基础 — auto 参数与类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/cpp14/tests/00-generic-lambdas/0.cpp) -- 1 - [泛型 lambda 与 STL 算法 — 排序、查找、函数工厂](https://github.com/mcpp-community/d2mcpp/blob/main/cpp14/tests/00-generic-lambdas/1.cpp) +- 0 - [泛型 lambda 基础 — auto 参数与类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp14/tests/00-generic-lambdas/0.cpp) +- 1 - [泛型 lambda 与 STL 算法 — 排序、查找、函数工厂](https://github.com/mcpp-community/d2mcpp/blob/main/src/cpp14/tests/00-generic-lambdas/1.cpp) ### 练习代码自动检测命令 diff --git a/cpp14/mcpp.toml b/cpp14/mcpp.toml deleted file mode 100644 index f1ab7f1..0000000 --- a/cpp14/mcpp.toml +++ /dev/null @@ -1,11 +0,0 @@ -# cpp14 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: -# mcpp test -p cpp14 全部练习(进度表) -# mcpp test -p cpp14 00-generic 只跑匹配的练习 -# 内容只用 cpp14 的特性;编译统一 c++23(cppNN 目录表示特性引入于哪个标准)。 -[package] -name = "cpp14" -version = "0.1.0" -standard = "c++23" - -[dependencies] -harness = { path = "../harness" } diff --git a/d2x/buildtools/mcpp/src/discovery.cppm b/d2x/buildtools/mcpp/src/discovery.cppm index 371eb45..527d1c1 100644 --- a/d2x/buildtools/mcpp/src/discovery.cppm +++ b/d2x/buildtools/mcpp/src/discovery.cppm @@ -5,9 +5,9 @@ // 酿成 bug。任何独立声明文件都是第二套真相源。这里的真相只有一处,且 // 无法漂移:目录结构本身。 // -// intro/tests/hello-mcpp.cpp → id: hello-mcpp (chapter: intro) -// cpp11/tests/00-auto-and-decltype/0.cpp → id: cpp11-00-auto-and-decltype-0 -// en/cpp11/tests/... lang=en 时启用,与 zh 互斥 +// src/intro/tests/hello-mcpp.cpp → id: hello-mcpp (chapter: intro) +// src/cpp11/tests/00-auto-and-decltype/0.cpp → id: cpp11-00-auto-and-decltype-0 +// src/en/cpp11/tests/... lang=en 时启用,与 zh 互斥 // // 每个 / 是真实 mcpp 工程,练习就是它的 tests/ —— 学员可以绕过 d2x // 直接 `mcpp test -p cpp11` 看进度表,Provider 走的是同一条路。 @@ -152,15 +152,15 @@ void scan_member(const fs::path& repo_root, const std::string& member, } } -// 扫描全部练习。lang=zh 用根下的工程,lang=en 用 en/ 前缀的镜像工程。 +// 扫描全部练习。课程工程住在 src/ 下;lang=en 用 src/en/ 镜像。 export std::vector scan(const fs::path& repo_root, std::string_view lang) { - std::string prefix = (lang == "en") ? "en/" : ""; + std::string prefix = (lang == "en") ? "src/en/" : "src/"; std::vector found; scan_member(repo_root, prefix + "intro", "", found); std::vector std_dirs; - auto base = (lang == "en") ? repo_root / "en" : repo_root; + auto base = (lang == "en") ? repo_root / "src" / "en" : repo_root / "src"; if (fs::is_directory(base)) { for (const auto& entry : fs::directory_iterator(base)) { auto name = entry.path().filename().string(); diff --git a/d2x/buildtools/mcpp/tests/e2e.sh b/d2x/buildtools/mcpp/tests/e2e.sh index dde2f26..fda1625 100755 --- a/d2x/buildtools/mcpp/tests/e2e.sh +++ b/d2x/buildtools/mcpp/tests/e2e.sh @@ -21,7 +21,7 @@ cd "$ROOT" MODE="${1:-all}" run_lang() { - local prefix="$1" # "" (zh) 或 "en/" + local prefix="$1" # "src/" (zh) 或 "src/en/" local label="$2" local members=("${prefix}intro" "${prefix}cpp11" "${prefix}cpp14") local test_dirs=() @@ -111,8 +111,8 @@ provider_smoke() { rc=0 provider_smoke || rc=1 -if [[ "$MODE" == "zh" || "$MODE" == "all" ]]; then run_lang "" zh || rc=1; fi -if [[ "$MODE" == "en" || "$MODE" == "all" ]]; then run_lang "en/" en || rc=1; fi +if [[ "$MODE" == "zh" || "$MODE" == "all" ]]; then run_lang "src/" zh || rc=1; fi +if [[ "$MODE" == "en" || "$MODE" == "all" ]]; then run_lang "src/en/" en || rc=1; fi if [[ "$rc" == 0 ]]; then echo "E2E: ALL GREEN"; else echo "E2E: FAILED"; fi exit "$rc" diff --git a/d2x/mcpp.toml b/d2x/mcpp.toml new file mode 100644 index 0000000..4f274ef --- /dev/null +++ b/d2x/mcpp.toml @@ -0,0 +1,9 @@ +[package] +name = "d2x" +version = "0.2.0" +description = "d2x 练习库:检查点、路障、侧信道上报(纯模块 import d2x,零宏)" +license = "Apache-2.0" +standard = "c++23" + +[targets.d2x] +kind = "lib" diff --git a/harness/src/harness.cppm b/d2x/src/d2x.cppm similarity index 64% rename from harness/src/harness.cppm rename to d2x/src/d2x.cppm index 6d87b9b..dabe30e 100644 --- a/harness/src/harness.cppm +++ b/d2x/src/d2x.cppm @@ -4,24 +4,30 @@ module; #include #include -export module d2x.harness; +export module d2x; import std; -// d2mcpp 练习脚手架 —— 唯一路径,纯模块,零宏。 +// d2x 练习库 —— 唯一路径,纯模块,零宏。练习侧 `import d2x;` 即可。 // // 填空占位符 D2X_YOUR_ANSWER 不在这里:它是**纯约定**,不定义在任何地方。 // 一个未定义的标识符本身就是编译错误,而且报错正好指着要填的位置 -// (`unknown type name 'D2X_YOUR_ANSWER'`)——比旧宏展开为空后指着别处的 -// 级联报错更好。所以它不需要头文件、在模块化练习里天然可用、拷进 -// Compiler Explorer 也成立。 +// (`unknown type name 'D2X_YOUR_ANSWER'`)。所以它不需要任何导入、在 +// 模块化练习里天然可用、拷进 Compiler Explorer 也成立。 // // 判定信号走两条路,各有消费者: -// 进程退出码 —— 有失败断言或未拆的 wait() 时退出码变 1。这让裸 +// 进程退出码 —— 有失败检查点或未拆的 wait() 时退出码变 1。这让裸 // `mcpp test` 不需要懂任何 d2x 概念就能显示对错。 // 侧信道 v2 —— D2X_RESULT_FILE 指定的 NDJSON 文件,Provider 读它 // 把「为什么失败」变成结构化诊断、把 wait 区分成 blocked。 // 未设置时不写文件:学员直接跑二进制零摩擦。 +// +// 可见输出的设计(学员每天面对的三行): +// ✅ | ( == ) 绿 +// ❌ | ( == ) --> : 红 +// 🚧 | Delete the d2x::wait() to continue --> : 黄 +// 标识就是 emoji 本身,不带日志框架前缀;file 尽量以仓库相对路径展示 +// (定位用的绝对路径走侧信道,展示归展示、定位归定位)。 namespace d2x::detail { @@ -29,11 +35,11 @@ int g_failures = 0; int g_waits = 0; bool g_hooked = false; -// 进程正常退出时,若有失败断言或未拆的路障,把退出码改成 1。 +// 进程正常退出时,若有失败检查点或未拆的路障,把退出码改成 1。 // 只在这两种情况下覆盖——练习自己 return 非 0 时不动它。 // _Exit 跳过后续清理,所以先冲刷 stdio。 void exit_hook() { - if (d2x::detail::g_failures > 0 || d2x::detail::g_waits > 0) { + if (g_failures > 0 || g_waits > 0) { std::fflush(stdout); std::fflush(stderr); std::_Exit(1); @@ -107,14 +113,31 @@ inline std::string show_impl(const auto& v) { else return {}; } +// 展示用路径:能剥掉当前工作目录前缀就剥(学员从仓库根跑时看到 +// src/cpp11/tests/... 而不是一长串 /home/...)。剥不掉就原样。 +inline std::string show_path(const char* file) { + if (!file) return {}; + std::string_view path(file); + std::error_code ec; + auto cwd = std::filesystem::current_path(ec); + if (!ec) { + auto prefix = cwd.generic_string() + "/"; + if (path.starts_with(prefix)) return std::string(path.substr(prefix.size())); + } + return std::string(path); +} + +constexpr std::string_view kGreen = "\033[32m"; +constexpr std::string_view kRed = "\033[31m"; +constexpr std::string_view kYellow = "\033[33m"; +constexpr std::string_view kReset = "\033[0m"; + } // namespace d2x::detail export namespace d2x { -// 可见输出格式沿用旧宏时代的样子([HONLY LOGI]: - ✅ | ...)——book 里 -// 引用了这些输出,格式漂移就是文档漂移。`what` 是断言的语义标签(通常是 -// 表达式原文,由撰稿或迁移脚本填写),补上 c++23 无反射拿不到 #expr 的缺。 - +// 检查点。`what` 是给学员看的语义标签(通常是表达式原文——c++23 没有 +// 反射拿不到 #expr,由撰稿或迁移脚本填写)。 inline bool check(bool ok, std::string_view what = {}, std::source_location loc = std::source_location::current()) { d2x::detail::ensure_hook(); @@ -123,9 +146,10 @@ inline bool check(bool ok, std::string_view what = {}, auto label = what.empty() ? std::string_view("check") : what; d2x::detail::report_assert(ok, label, "true", ok ? "true" : "false", loc.file_name(), static_cast(loc.line())); - if (ok) std::print("\033[32m[HONLY LOGI]: - ✅ | {}\033[0m\n", label); - else std::print("\033[33m[HONLY LOGW]: {}:{} - ❌(error) | {}\033[0m\n", - loc.file_name(), loc.line(), label); + if (ok) std::print("{}✅ | {}{}\n", d2x::detail::kGreen, label, d2x::detail::kReset); + else std::print("{}❌ | {} --> {}:{}{}\n", d2x::detail::kRed, label, + d2x::detail::show_path(loc.file_name()), loc.line(), + d2x::detail::kReset); std::fflush(stdout); return ok; } @@ -144,29 +168,32 @@ inline bool check_eq(const A& a, const B& b, std::string_view what = {}, d2x::detail::report_assert(ok, label, sb, sa, loc.file_name(), static_cast(loc.line())); - if (ok) std::print("\033[32m[HONLY LOGI]: - ✅ | {} ({} == {})\033[0m\n", label, sa, sb); - else std::print("\033[33m[HONLY LOGW]: {}:{} - ❌ | {} ({} == {})\033[0m\n", - loc.file_name(), loc.line(), label, sa, sb); + if (ok) std::print("{}✅ | {} ({} == {}){}\n", + d2x::detail::kGreen, label, sa, sb, d2x::detail::kReset); + else std::print("{}❌ | {} ({} == {}) --> {}:{}{}\n", + d2x::detail::kRed, label, sa, sb, + d2x::detail::show_path(loc.file_name()), loc.line(), + d2x::detail::kReset); std::fflush(stdout); return ok; } -// 恒等透传:标记「这一行是教学观测点,别删」。旧宏 D2X_DONT_DELETE_THIS -// 的函数化等价物——对表达式原样求值并返回。 +// 恒等透传:标记「这一行是教学观测点,别删」。 template constexpr decltype(auto) dont_delete_this(T&& x) { return std::forward(x); } // 显式路障:学员读完说明、删掉这一行才算真正完成这一题。 -// 记录后照常返回(不中断程序)——后续断言还要跑;退出码由 exit_hook 收口, -// Provider 再根据侧信道把「只剩 wait」区分成 blocked。 +// 记录后照常返回(不中断程序)——后续检查点还要跑;退出码由 exit_hook +// 收口,Provider 再根据侧信道把「只剩 wait」区分成 blocked。 inline void wait(std::source_location loc = std::source_location::current()) { d2x::detail::ensure_hook(); ++d2x::detail::g_waits; d2x::detail::report_wait(loc.file_name(), static_cast(loc.line())); - std::print("\033[33m[HONLY LOGW]: {}:{} - 🥳 Delete the d2x::wait() to continue...\033[0m\n", - loc.file_name(), loc.line()); + std::print("{}🚧 | Delete the d2x::wait() to continue --> {}:{}{}\n", + d2x::detail::kYellow, d2x::detail::show_path(loc.file_name()), + loc.line(), d2x::detail::kReset); std::fflush(stdout); } diff --git a/harness/mcpp.toml b/harness/mcpp.toml deleted file mode 100644 index 05ac43c..0000000 --- a/harness/mcpp.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "harness" -version = "0.2.0" -description = "d2mcpp 练习脚手架:断言、路障、侧信道上报(纯模块,无宏)" -license = "Apache-2.0" -standard = "c++23" - -[targets.harness] -kind = "lib" diff --git a/mcpp.toml b/mcpp.toml index a90e86e..d4cc89a 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,8 +1,10 @@ -# d2mcpp 根 workspace。练习即测试:每个 C++ 标准一个真实工程, +# d2mcpp 根 workspace。练习即测试:每个 C++ 标准是 src/ 下的一个真实工程, # 练习是它的 tests/,mcpp test 直接就是进度表;d2x checker 驱动同一条链。 +# d2x/ 是练习库(import d2x)与 Provider 的家。 [workspace] members = [ - "harness", "intro", "cpp11", "cpp14", - "en/intro", "en/cpp11", "en/cpp14", + "d2x", + "src/intro", "src/cpp11", "src/cpp14", + "src/en/intro", "src/en/cpp11", "src/en/cpp14", "d2x/buildtools/mcpp", ] diff --git a/solutions/cpp11/00-auto-and-decltype/0.cpp b/solutions/cpp11/00-auto-and-decltype/0.cpp index 49ab0a1..56a61f7 100644 --- a/solutions/cpp11/00-auto-and-decltype/0.cpp +++ b/solutions/cpp11/00-auto-and-decltype/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/solutions/cpp11/00-auto-and-decltype/1.cpp b/solutions/cpp11/00-auto-and-decltype/1.cpp index 85c7bda..e9be137 100644 --- a/solutions/cpp11/00-auto-and-decltype/1.cpp +++ b/solutions/cpp11/00-auto-and-decltype/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/solutions/cpp11/00-auto-and-decltype/2.cpp b/solutions/cpp11/00-auto-and-decltype/2.cpp index 9e86b6a..528682c 100644 --- a/solutions/cpp11/00-auto-and-decltype/2.cpp +++ b/solutions/cpp11/00-auto-and-decltype/2.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; int add_func(int a, int b) { diff --git a/solutions/cpp11/00-auto-and-decltype/3.cpp b/solutions/cpp11/00-auto-and-decltype/3.cpp index 2cbae82..a83aaa4 100644 --- a/solutions/cpp11/00-auto-and-decltype/3.cpp +++ b/solutions/cpp11/00-auto-and-decltype/3.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; // 3. 函数返回值类型 diff --git a/solutions/cpp11/00-auto-and-decltype/4.cpp b/solutions/cpp11/00-auto-and-decltype/4.cpp index cbd2a5a..697f430 100644 --- a/solutions/cpp11/00-auto-and-decltype/4.cpp +++ b/solutions/cpp11/00-auto-and-decltype/4.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/solutions/cpp11/00-auto-and-decltype/5.cpp b/solutions/cpp11/00-auto-and-decltype/5.cpp index f7524b2..6fcd04c 100644 --- a/solutions/cpp11/00-auto-and-decltype/5.cpp +++ b/solutions/cpp11/00-auto-and-decltype/5.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/solutions/cpp11/01-default-and-delete/0.cpp b/solutions/cpp11/01-default-and-delete/0.cpp index eae670e..a53f0da 100644 --- a/solutions/cpp11/01-default-and-delete/0.cpp +++ b/solutions/cpp11/01-default-and-delete/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; // default和delete显式控制 -> 编译器默认构造函数的生成行为 diff --git a/solutions/cpp11/01-default-and-delete/1.cpp b/solutions/cpp11/01-default-and-delete/1.cpp index 5b6bae0..5c3f520 100644 --- a/solutions/cpp11/01-default-and-delete/1.cpp +++ b/solutions/cpp11/01-default-and-delete/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; // 实现std::unique_ptr不可以拷贝, 但可以移动的属性 diff --git a/solutions/cpp11/01-default-and-delete/2.cpp b/solutions/cpp11/01-default-and-delete/2.cpp index 9b020f4..d2dcf8d 100644 --- a/solutions/cpp11/01-default-and-delete/2.cpp +++ b/solutions/cpp11/01-default-and-delete/2.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; void func(int x) { diff --git a/solutions/cpp11/02-final-and-override/0.cpp b/solutions/cpp11/02-final-and-override/0.cpp index e3568c8..22f9237 100644 --- a/solutions/cpp11/02-final-and-override/0.cpp +++ b/solutions/cpp11/02-final-and-override/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; struct A { diff --git a/solutions/cpp11/02-final-and-override/1.cpp b/solutions/cpp11/02-final-and-override/1.cpp index da0aaf4..b12f39a 100644 --- a/solutions/cpp11/02-final-and-override/1.cpp +++ b/solutions/cpp11/02-final-and-override/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; struct A { diff --git a/solutions/cpp11/02-final-and-override/2.cpp b/solutions/cpp11/02-final-and-override/2.cpp index 5a85f62..c14ef40 100644 --- a/solutions/cpp11/02-final-and-override/2.cpp +++ b/solutions/cpp11/02-final-and-override/2.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; struct AudioPlayer { // 不要直接修改AudioPlayer类 diff --git a/solutions/cpp11/03-trailing-return-type/0.cpp b/solutions/cpp11/03-trailing-return-type/0.cpp index 33709e2..c9efef8 100644 --- a/solutions/cpp11/03-trailing-return-type/0.cpp +++ b/solutions/cpp11/03-trailing-return-type/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; int add0(double a, int b) { diff --git a/solutions/cpp11/04-rvalue-references/0.cpp b/solutions/cpp11/04-rvalue-references/0.cpp index 7a9dc5c..eefde90 100644 --- a/solutions/cpp11/04-rvalue-references/0.cpp +++ b/solutions/cpp11/04-rvalue-references/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; struct Object; diff --git a/solutions/cpp11/05-move-semantics/0.cpp b/solutions/cpp11/05-move-semantics/0.cpp index 7d65c68..350bb70 100644 --- a/solutions/cpp11/05-move-semantics/0.cpp +++ b/solutions/cpp11/05-move-semantics/0.cpp @@ -9,7 +9,7 @@ // 这样每个传递点最终都指向同一块缓冲区, 三个 d2x::check 都通过。 import std; -import d2x.harness; +import d2x; struct Buffer { diff --git a/solutions/cpp11/05-move-semantics/1.cpp b/solutions/cpp11/05-move-semantics/1.cpp index 36119ce..a8087ee 100644 --- a/solutions/cpp11/05-move-semantics/1.cpp +++ b/solutions/cpp11/05-move-semantics/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/solutions/cpp11/05-move-semantics/2.cpp b/solutions/cpp11/05-move-semantics/2.cpp index 1f22feb..7f1a814 100644 --- a/solutions/cpp11/05-move-semantics/2.cpp +++ b/solutions/cpp11/05-move-semantics/2.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; struct Buffer { diff --git a/solutions/cpp11/06-scoped-enums/0.cpp b/solutions/cpp11/06-scoped-enums/0.cpp index 4becf10..eeae15a 100644 --- a/solutions/cpp11/06-scoped-enums/0.cpp +++ b/solutions/cpp11/06-scoped-enums/0.cpp @@ -9,7 +9,7 @@ // 保留并展示 "color == Apple" 这种本不该相等却被静默接受的逻辑错误。 import std; -import d2x.harness; +import d2x; enum Color { diff --git a/solutions/cpp11/06-scoped-enums/1.cpp b/solutions/cpp11/06-scoped-enums/1.cpp index 23735f8..2fd050c 100644 --- a/solutions/cpp11/06-scoped-enums/1.cpp +++ b/solutions/cpp11/06-scoped-enums/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; enum class Color { diff --git a/solutions/cpp11/07-constexpr/0.cpp b/solutions/cpp11/07-constexpr/0.cpp index 4677664..068f9bb 100644 --- a/solutions/cpp11/07-constexpr/0.cpp +++ b/solutions/cpp11/07-constexpr/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; constexpr int sum_for_1_to(int n) { diff --git a/solutions/cpp11/07-constexpr/1.cpp b/solutions/cpp11/07-constexpr/1.cpp index 4985098..28534c5 100644 --- a/solutions/cpp11/07-constexpr/1.cpp +++ b/solutions/cpp11/07-constexpr/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; template diff --git a/solutions/cpp11/08-literal-type/0.cpp b/solutions/cpp11/08-literal-type/0.cpp index 236c958..50390b4 100644 --- a/solutions/cpp11/08-literal-type/0.cpp +++ b/solutions/cpp11/08-literal-type/0.cpp @@ -10,7 +10,7 @@ // 仍可参与编译期计算。 import std; -import d2x.harness; +import d2x; constexpr char compile_time_compute(char c, int a) { diff --git a/solutions/cpp11/08-literal-type/1.cpp b/solutions/cpp11/08-literal-type/1.cpp index 9b47643..068ee4f 100644 --- a/solutions/cpp11/08-literal-type/1.cpp +++ b/solutions/cpp11/08-literal-type/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; struct Vector { diff --git a/solutions/cpp11/09-list-initialization/0.cpp b/solutions/cpp11/09-list-initialization/0.cpp index 2dd5642..6607e39 100644 --- a/solutions/cpp11/09-list-initialization/0.cpp +++ b/solutions/cpp11/09-list-initialization/0.cpp @@ -8,7 +8,7 @@ // 教学要点: 大括号(列表)初始化对窄化转换不允许, 需要使用显式 static_cast 修复。 import std; -import d2x.harness; +import d2x; int main() { diff --git a/solutions/cpp11/09-list-initialization/1.cpp b/solutions/cpp11/09-list-initialization/1.cpp index a0db470..e9416f2 100644 --- a/solutions/cpp11/09-list-initialization/1.cpp +++ b/solutions/cpp11/09-list-initialization/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; struct Object { diff --git a/solutions/cpp11/09-list-initialization/2.cpp b/solutions/cpp11/09-list-initialization/2.cpp index 2d88c1d..666622a 100644 --- a/solutions/cpp11/09-list-initialization/2.cpp +++ b/solutions/cpp11/09-list-initialization/2.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; class MyVector { diff --git a/solutions/cpp11/09-list-initialization/3.cpp b/solutions/cpp11/09-list-initialization/3.cpp index 661301c..fe1f294 100644 --- a/solutions/cpp11/09-list-initialization/3.cpp +++ b/solutions/cpp11/09-list-initialization/3.cpp @@ -12,7 +12,7 @@ // 让 `MyVector vec4 { 1, 10 }` 命中 (int, int) 构造函数。 import std; -import d2x.harness; +import d2x; class MyVector { diff --git a/solutions/cpp11/10-delegating-constructors/0.cpp b/solutions/cpp11/10-delegating-constructors/0.cpp index 22d6102..634d17b 100644 --- a/solutions/cpp11/10-delegating-constructors/0.cpp +++ b/solutions/cpp11/10-delegating-constructors/0.cpp @@ -10,7 +10,7 @@ // 计数器 1->3->5->6 与断言匹配。 import std; -import d2x.harness; +import d2x; static int construction_counter { 0 }; diff --git a/solutions/cpp11/10-delegating-constructors/1.cpp b/solutions/cpp11/10-delegating-constructors/1.cpp index 14467ca..a78680c 100644 --- a/solutions/cpp11/10-delegating-constructors/1.cpp +++ b/solutions/cpp11/10-delegating-constructors/1.cpp @@ -12,7 +12,7 @@ // 3. 用成员初始化列表直接构造 obj, 避免多余的默认构造 + 移动赋值, 让计数 == 1. import std; -import d2x.harness; +import d2x; struct Object { // 不要修改这个类的代码 diff --git a/solutions/cpp11/11-inherited-constructors/0.cpp b/solutions/cpp11/11-inherited-constructors/0.cpp index e76d33a..ec3d236 100644 --- a/solutions/cpp11/11-inherited-constructors/0.cpp +++ b/solutions/cpp11/11-inherited-constructors/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; class ObjectBase { diff --git a/solutions/cpp11/11-inherited-constructors/1.cpp b/solutions/cpp11/11-inherited-constructors/1.cpp index ef0261a..6ea1de7 100644 --- a/solutions/cpp11/11-inherited-constructors/1.cpp +++ b/solutions/cpp11/11-inherited-constructors/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; class Student { // 不要直接修改 Student 类中的代码 diff --git a/solutions/cpp11/11-inherited-constructors/2.cpp b/solutions/cpp11/11-inherited-constructors/2.cpp index ce8d4ea..6d73f64 100644 --- a/solutions/cpp11/11-inherited-constructors/2.cpp +++ b/solutions/cpp11/11-inherited-constructors/2.cpp @@ -9,7 +9,7 @@ // 移动操作 -> 移动操作不会被隐式生成, `std::move(p3)` 会回退到拷贝. import std; -import d2x.harness; +import d2x; struct Point { diff --git a/solutions/cpp11/12-nullptr/0.cpp b/solutions/cpp11/12-nullptr/0.cpp index a31d793..1e38352 100644 --- a/solutions/cpp11/12-nullptr/0.cpp +++ b/solutions/cpp11/12-nullptr/0.cpp @@ -8,7 +8,7 @@ #include // NULL 宏是这一课的教具,import std 不带宏 import std; -import d2x.harness; +import d2x; int main() { diff --git a/solutions/cpp11/12-nullptr/1.cpp b/solutions/cpp11/12-nullptr/1.cpp index 1e6857b..e9c6525 100644 --- a/solutions/cpp11/12-nullptr/1.cpp +++ b/solutions/cpp11/12-nullptr/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; bool process_int_called = false; bool process_ptr_called = false; diff --git a/solutions/cpp11/12-nullptr/2.cpp b/solutions/cpp11/12-nullptr/2.cpp index 62029ca..f508072 100644 --- a/solutions/cpp11/12-nullptr/2.cpp +++ b/solutions/cpp11/12-nullptr/2.cpp @@ -8,7 +8,7 @@ #include // NULL 宏是这一课的教具,import std 不带宏 import std; -import d2x.harness; +import d2x; // 模板函数示例 template diff --git a/solutions/cpp11/13-long-long/0.cpp b/solutions/cpp11/13-long-long/0.cpp index 6c1cc9b..ac0a6ad 100644 --- a/solutions/cpp11/13-long-long/0.cpp +++ b/solutions/cpp11/13-long-long/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/solutions/cpp11/13-long-long/1.cpp b/solutions/cpp11/13-long-long/1.cpp index 823f5f7..1c0fc55 100644 --- a/solutions/cpp11/13-long-long/1.cpp +++ b/solutions/cpp11/13-long-long/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/solutions/cpp11/14-type-alias/0.cpp b/solutions/cpp11/14-type-alias/0.cpp index cb278c0..98efc7c 100644 --- a/solutions/cpp11/14-type-alias/0.cpp +++ b/solutions/cpp11/14-type-alias/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/solutions/cpp11/14-type-alias/1.cpp b/solutions/cpp11/14-type-alias/1.cpp index fe6a586..51a91fb 100644 --- a/solutions/cpp11/14-type-alias/1.cpp +++ b/solutions/cpp11/14-type-alias/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; static int func_called = 0; diff --git a/solutions/cpp11/14-type-alias/2.cpp b/solutions/cpp11/14-type-alias/2.cpp index 263d9bc..1a660b5 100644 --- a/solutions/cpp11/14-type-alias/2.cpp +++ b/solutions/cpp11/14-type-alias/2.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; // 1. 基本别名模板 diff --git a/solutions/cpp11/14-type-alias/3.cpp b/solutions/cpp11/14-type-alias/3.cpp index 8cb4bde..c1e16f6 100644 --- a/solutions/cpp11/14-type-alias/3.cpp +++ b/solutions/cpp11/14-type-alias/3.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; // 别名模板必须定义在命名空间作用域(不能写在函数体内) template diff --git a/solutions/cpp11/15-variadic-templates/0.cpp b/solutions/cpp11/15-variadic-templates/0.cpp index c023f12..ba2a48d 100644 --- a/solutions/cpp11/15-variadic-templates/0.cpp +++ b/solutions/cpp11/15-variadic-templates/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; std::stringstream ss; diff --git a/solutions/cpp11/15-variadic-templates/1.cpp b/solutions/cpp11/15-variadic-templates/1.cpp index cdf51d4..b1035b6 100644 --- a/solutions/cpp11/15-variadic-templates/1.cpp +++ b/solutions/cpp11/15-variadic-templates/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; // 递归终止: 只剩一个参数时直接返回 template diff --git a/solutions/cpp11/16-generalized-unions/0.cpp b/solutions/cpp11/16-generalized-unions/0.cpp index 5be020c..11680de 100644 --- a/solutions/cpp11/16-generalized-unions/0.cpp +++ b/solutions/cpp11/16-generalized-unions/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; union M diff --git a/solutions/cpp11/16-generalized-unions/1.cpp b/solutions/cpp11/16-generalized-unions/1.cpp index 780ea91..a417409 100644 --- a/solutions/cpp11/16-generalized-unions/1.cpp +++ b/solutions/cpp11/16-generalized-unions/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; union M { diff --git a/solutions/cpp11/16-generalized-unions/2.cpp b/solutions/cpp11/16-generalized-unions/2.cpp index 6c19c73..c42c62b 100644 --- a/solutions/cpp11/16-generalized-unions/2.cpp +++ b/solutions/cpp11/16-generalized-unions/2.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; enum class Tag { INTEGER, diff --git a/solutions/cpp11/17-pod-type/0.cpp b/solutions/cpp11/17-pod-type/0.cpp index f8b2fb2..d51a7d3 100644 --- a/solutions/cpp11/17-pod-type/0.cpp +++ b/solutions/cpp11/17-pod-type/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; struct A { diff --git a/solutions/cpp11/17-pod-type/1.cpp b/solutions/cpp11/17-pod-type/1.cpp index 070a834..ead5c5e 100644 --- a/solutions/cpp11/17-pod-type/1.cpp +++ b/solutions/cpp11/17-pod-type/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/solutions/cpp11/17-pod-type/2.cpp b/solutions/cpp11/17-pod-type/2.cpp index 6bc2868..ba90193 100644 --- a/solutions/cpp11/17-pod-type/2.cpp +++ b/solutions/cpp11/17-pod-type/2.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/solutions/cpp14/00-generic-lambdas/0.cpp b/solutions/cpp14/00-generic-lambdas/0.cpp index 5ba5c2d..a199b79 100644 --- a/solutions/cpp14/00-generic-lambdas/0.cpp +++ b/solutions/cpp14/00-generic-lambdas/0.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/solutions/cpp14/00-generic-lambdas/1.cpp b/solutions/cpp14/00-generic-lambdas/1.cpp index 61887e7..72c8713 100644 --- a/solutions/cpp14/00-generic-lambdas/1.cpp +++ b/solutions/cpp14/00-generic-lambdas/1.cpp @@ -7,7 +7,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/solutions/intro/hello-mcpp.cpp b/solutions/intro/hello-mcpp.cpp index fbaaa71..41340ac 100644 --- a/solutions/intro/hello-mcpp.cpp +++ b/solutions/intro/hello-mcpp.cpp @@ -1,9 +1,9 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: intro/tests/hello-mcpp.cpp (参考答案) +// file: src/intro/tests/hello-mcpp.cpp (参考答案) import std; -import d2x.harness; +import d2x; int main() { diff --git a/cpp11/mcpp.toml b/src/cpp11/mcpp.toml similarity index 52% rename from cpp11/mcpp.toml rename to src/cpp11/mcpp.toml index 97c1c0c..8c12878 100644 --- a/cpp11/mcpp.toml +++ b/src/cpp11/mcpp.toml @@ -1,10 +1,10 @@ # cpp11 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: -# mcpp test -p cpp11 全部练习(进度表) -# mcpp test -p cpp11 <子串> 只跑匹配的练习 +# mcpp test -p src/cpp11 全部练习(进度表) +# mcpp test -p src/cpp11 <子串> 只跑匹配的练习 [package] name = "cpp11" version = "0.1.0" standard = "c++23" [dependencies] -harness = { path = "../harness" } +d2x = { path = "../../d2x" } diff --git a/cpp11/tests/00-auto-and-decltype/0.cpp b/src/cpp11/tests/00-auto-and-decltype/0.cpp similarity index 94% rename from cpp11/tests/00-auto-and-decltype/0.cpp rename to src/cpp11/tests/00-auto-and-decltype/0.cpp index 77c4b50..d00cf0a 100644 --- a/cpp11/tests/00-auto-and-decltype/0.cpp +++ b/src/cpp11/tests/00-auto-and-decltype/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/00-auto-and-decltype/0.cpp +// file: src/cpp11/tests/00-auto-and-decltype/0.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 自动类型推导 // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/cpp11/tests/00-auto-and-decltype/1.cpp b/src/cpp11/tests/00-auto-and-decltype/1.cpp similarity index 94% rename from cpp11/tests/00-auto-and-decltype/1.cpp rename to src/cpp11/tests/00-auto-and-decltype/1.cpp index 32feffe..47da877 100644 --- a/cpp11/tests/00-auto-and-decltype/1.cpp +++ b/src/cpp11/tests/00-auto-and-decltype/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/00-auto-and-decltype/1.cpp +// file: src/cpp11/tests/00-auto-and-decltype/1.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 表达式类型推导 // @@ -20,7 +20,7 @@ import std; -import d2x.harness; +import d2x; int main() { diff --git a/cpp11/tests/00-auto-and-decltype/2.cpp b/src/cpp11/tests/00-auto-and-decltype/2.cpp similarity index 95% rename from cpp11/tests/00-auto-and-decltype/2.cpp rename to src/cpp11/tests/00-auto-and-decltype/2.cpp index 6ae104c..4350995 100644 --- a/cpp11/tests/00-auto-and-decltype/2.cpp +++ b/src/cpp11/tests/00-auto-and-decltype/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/00-auto-and-decltype/2.cpp +// file: src/cpp11/tests/00-auto-and-decltype/2.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 复杂类型推导 // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; int add_func(int a, int b) { diff --git a/cpp11/tests/00-auto-and-decltype/3.cpp b/src/cpp11/tests/00-auto-and-decltype/3.cpp similarity index 94% rename from cpp11/tests/00-auto-and-decltype/3.cpp rename to src/cpp11/tests/00-auto-and-decltype/3.cpp index 299ab26..81fddc4 100644 --- a/cpp11/tests/00-auto-and-decltype/3.cpp +++ b/src/cpp11/tests/00-auto-and-decltype/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/00-auto-and-decltype/3.cpp +// file: src/cpp11/tests/00-auto-and-decltype/3.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 函数返回值类型推导 // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; // 3. 函数返回值类型 diff --git a/cpp11/tests/00-auto-and-decltype/4.cpp b/src/cpp11/tests/00-auto-and-decltype/4.cpp similarity index 96% rename from cpp11/tests/00-auto-and-decltype/4.cpp rename to src/cpp11/tests/00-auto-and-decltype/4.cpp index f4f3d64..239a417 100644 --- a/cpp11/tests/00-auto-and-decltype/4.cpp +++ b/src/cpp11/tests/00-auto-and-decltype/4.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/00-auto-and-decltype/4.cpp +// file: src/cpp11/tests/00-auto-and-decltype/4.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | 类/结构体成员类型推导 // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/cpp11/tests/00-auto-and-decltype/5.cpp b/src/cpp11/tests/00-auto-and-decltype/5.cpp similarity index 96% rename from cpp11/tests/00-auto-and-decltype/5.cpp rename to src/cpp11/tests/00-auto-and-decltype/5.cpp index 2006c8b..3bca5ba 100644 --- a/cpp11/tests/00-auto-and-decltype/5.cpp +++ b/src/cpp11/tests/00-auto-and-decltype/5.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/00-auto-and-decltype/5.cpp +// file: src/cpp11/tests/00-auto-and-decltype/5.cpp // // Exercise/练习: cpp11 | 00 - auto and decltype | const 与引用的剥离和保留 // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/cpp11/tests/01-default-and-delete/0.cpp b/src/cpp11/tests/01-default-and-delete/0.cpp similarity index 92% rename from cpp11/tests/01-default-and-delete/0.cpp rename to src/cpp11/tests/01-default-and-delete/0.cpp index 034f754..ae892af 100644 --- a/cpp11/tests/01-default-and-delete/0.cpp +++ b/src/cpp11/tests/01-default-and-delete/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/01-default-and-delete/0.cpp +// file: src/cpp11/tests/01-default-and-delete/0.cpp // // Exercise/练习: cpp11 | 01 - default and delete | 显示指定构造函数生成行为 // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; // default和delete显式控制 -> 编译器默认构造函数的生成行为 diff --git a/cpp11/tests/01-default-and-delete/1.cpp b/src/cpp11/tests/01-default-and-delete/1.cpp similarity index 95% rename from cpp11/tests/01-default-and-delete/1.cpp rename to src/cpp11/tests/01-default-and-delete/1.cpp index 2d0bd88..c269dc6 100644 --- a/cpp11/tests/01-default-and-delete/1.cpp +++ b/src/cpp11/tests/01-default-and-delete/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/01-default-and-delete/1.cpp +// file: src/cpp11/tests/01-default-and-delete/1.cpp // // Exercise/练习: cpp11 | 01 - default and delete | 不可拷贝对像 // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; // 实现std::unique_ptr不可以拷贝, 但可以移动的属性 diff --git a/cpp11/tests/01-default-and-delete/2.cpp b/src/cpp11/tests/01-default-and-delete/2.cpp similarity index 91% rename from cpp11/tests/01-default-and-delete/2.cpp rename to src/cpp11/tests/01-default-and-delete/2.cpp index 3a77334..800294c 100644 --- a/cpp11/tests/01-default-and-delete/2.cpp +++ b/src/cpp11/tests/01-default-and-delete/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/01-default-and-delete/2.cpp +// file: src/cpp11/tests/01-default-and-delete/2.cpp // // Exercise/练习: cpp11 | 01 - default and delete | 禁止函数重载 // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; void func(int x) { diff --git a/cpp11/tests/02-final-and-override/0.cpp b/src/cpp11/tests/02-final-and-override/0.cpp similarity index 93% rename from cpp11/tests/02-final-and-override/0.cpp rename to src/cpp11/tests/02-final-and-override/0.cpp index d9e7900..f702ddd 100644 --- a/cpp11/tests/02-final-and-override/0.cpp +++ b/src/cpp11/tests/02-final-and-override/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/02-final-and-override/0.cpp +// file: src/cpp11/tests/02-final-and-override/0.cpp // // Exercise/练习: cpp11 | 02 - final and override // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct A { diff --git a/cpp11/tests/02-final-and-override/1.cpp b/src/cpp11/tests/02-final-and-override/1.cpp similarity index 93% rename from cpp11/tests/02-final-and-override/1.cpp rename to src/cpp11/tests/02-final-and-override/1.cpp index b76d0a8..175357e 100644 --- a/cpp11/tests/02-final-and-override/1.cpp +++ b/src/cpp11/tests/02-final-and-override/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/02-final-and-override/1.cpp +// file: src/cpp11/tests/02-final-and-override/1.cpp // // Exercise/练习: cpp11 | 02 - final and override // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct A { diff --git a/cpp11/tests/02-final-and-override/2.cpp b/src/cpp11/tests/02-final-and-override/2.cpp similarity index 96% rename from cpp11/tests/02-final-and-override/2.cpp rename to src/cpp11/tests/02-final-and-override/2.cpp index 122be66..9652bc0 100644 --- a/cpp11/tests/02-final-and-override/2.cpp +++ b/src/cpp11/tests/02-final-and-override/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/02-final-and-override/2.cpp +// file: src/cpp11/tests/02-final-and-override/2.cpp // // Exercise/练习: cpp11 | 02 - final and override // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct AudioPlayer { // 不要直接修改AudioPlayer类 diff --git a/cpp11/tests/03-trailing-return-type/0.cpp b/src/cpp11/tests/03-trailing-return-type/0.cpp similarity index 93% rename from cpp11/tests/03-trailing-return-type/0.cpp rename to src/cpp11/tests/03-trailing-return-type/0.cpp index 158a76b..4aee4cc 100644 --- a/cpp11/tests/03-trailing-return-type/0.cpp +++ b/src/cpp11/tests/03-trailing-return-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/03-trailing-return-type/0.cpp +// file: src/cpp11/tests/03-trailing-return-type/0.cpp // // Exercise/练习: cpp11 | 03 - trailing return type // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; int add0(double a, int b) { diff --git a/cpp11/tests/04-rvalue-references/0.cpp b/src/cpp11/tests/04-rvalue-references/0.cpp similarity index 97% rename from cpp11/tests/04-rvalue-references/0.cpp rename to src/cpp11/tests/04-rvalue-references/0.cpp index f16992c..ee7cadc 100644 --- a/cpp11/tests/04-rvalue-references/0.cpp +++ b/src/cpp11/tests/04-rvalue-references/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/04-rvalue-references/0.cpp +// file: src/cpp11/tests/04-rvalue-references/0.cpp // // Exercise/练习: cpp11 | 04 - rvalue references // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; struct Object; diff --git a/cpp11/tests/05-move-semantics/0.cpp b/src/cpp11/tests/05-move-semantics/0.cpp similarity index 96% rename from cpp11/tests/05-move-semantics/0.cpp rename to src/cpp11/tests/05-move-semantics/0.cpp index 982c807..a78fc14 100644 --- a/cpp11/tests/05-move-semantics/0.cpp +++ b/src/cpp11/tests/05-move-semantics/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/05-move-semantics/0.cpp +// file: src/cpp11/tests/05-move-semantics/0.cpp // // Exercise/练习: cpp11 | 05 - move semantics | 移动构造与触发时机 // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; struct Buffer { diff --git a/cpp11/tests/05-move-semantics/1.cpp b/src/cpp11/tests/05-move-semantics/1.cpp similarity index 97% rename from cpp11/tests/05-move-semantics/1.cpp rename to src/cpp11/tests/05-move-semantics/1.cpp index ab08a16..5d172cc 100644 --- a/cpp11/tests/05-move-semantics/1.cpp +++ b/src/cpp11/tests/05-move-semantics/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/05-move-semantics/1.cpp +// file: src/cpp11/tests/05-move-semantics/1.cpp // // Exercise/练习: cpp11 | 05 - move semantics | 移动赋值与触发时机 // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/cpp11/tests/05-move-semantics/2.cpp b/src/cpp11/tests/05-move-semantics/2.cpp similarity index 97% rename from cpp11/tests/05-move-semantics/2.cpp rename to src/cpp11/tests/05-move-semantics/2.cpp index 651aa03..30bc986 100644 --- a/cpp11/tests/05-move-semantics/2.cpp +++ b/src/cpp11/tests/05-move-semantics/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/05-move-semantics/2.cpp +// file: src/cpp11/tests/05-move-semantics/2.cpp // // Exercise/练习: cpp11 | 05 - move semantics | 移动的是资源而不是对象 // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; struct Buffer { diff --git a/cpp11/tests/06-scoped-enums/0.cpp b/src/cpp11/tests/06-scoped-enums/0.cpp similarity index 94% rename from cpp11/tests/06-scoped-enums/0.cpp rename to src/cpp11/tests/06-scoped-enums/0.cpp index e217071..f22808e 100644 --- a/cpp11/tests/06-scoped-enums/0.cpp +++ b/src/cpp11/tests/06-scoped-enums/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/06-scoped-enums/0.cpp +// file: src/cpp11/tests/06-scoped-enums/0.cpp // // Exercise/练习: cpp11 | 06 - scoped enums | 传统枚举类型潜在问题 // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; enum Color { diff --git a/cpp11/tests/06-scoped-enums/1.cpp b/src/cpp11/tests/06-scoped-enums/1.cpp similarity index 96% rename from cpp11/tests/06-scoped-enums/1.cpp rename to src/cpp11/tests/06-scoped-enums/1.cpp index 799cf2f..79b69a1 100644 --- a/cpp11/tests/06-scoped-enums/1.cpp +++ b/src/cpp11/tests/06-scoped-enums/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/06-scoped-enums/1.cpp +// file: src/cpp11/tests/06-scoped-enums/1.cpp // // Exercise/练习: cpp11 | 06 - scoped enums | 范围枚举类型基本用法 // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; enum class Color { diff --git a/cpp11/tests/07-constexpr/0.cpp b/src/cpp11/tests/07-constexpr/0.cpp similarity index 93% rename from cpp11/tests/07-constexpr/0.cpp rename to src/cpp11/tests/07-constexpr/0.cpp index 3ac2086..d7e1222 100644 --- a/cpp11/tests/07-constexpr/0.cpp +++ b/src/cpp11/tests/07-constexpr/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/07-constexpr/0.cpp +// file: src/cpp11/tests/07-constexpr/0.cpp // // Exercise/练习: cpp11 | 07 - constexpr | 编译期计算基础: constexpr 和 const的区别 // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; int sum_for_1_to(int n) { diff --git a/cpp11/tests/07-constexpr/1.cpp b/src/cpp11/tests/07-constexpr/1.cpp similarity index 96% rename from cpp11/tests/07-constexpr/1.cpp rename to src/cpp11/tests/07-constexpr/1.cpp index 2a1ac25..06c505a 100644 --- a/cpp11/tests/07-constexpr/1.cpp +++ b/src/cpp11/tests/07-constexpr/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/07-constexpr/1.cpp +// file: src/cpp11/tests/07-constexpr/1.cpp // // Exercise/练习: cpp11 | 07 - constexpr | 编译期计算应用示例 // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; template diff --git a/cpp11/tests/08-literal-type/0.cpp b/src/cpp11/tests/08-literal-type/0.cpp similarity index 95% rename from cpp11/tests/08-literal-type/0.cpp rename to src/cpp11/tests/08-literal-type/0.cpp index 989b20d..feda694 100644 --- a/cpp11/tests/08-literal-type/0.cpp +++ b/src/cpp11/tests/08-literal-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/08-literal-type/0.cpp +// file: src/cpp11/tests/08-literal-type/0.cpp // // Exercise/练习: cpp11 | 08 - literal type | 什么是字面值类型 // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; constexpr char compile_time_compute(char c, int a) { diff --git a/cpp11/tests/08-literal-type/1.cpp b/src/cpp11/tests/08-literal-type/1.cpp similarity index 92% rename from cpp11/tests/08-literal-type/1.cpp rename to src/cpp11/tests/08-literal-type/1.cpp index 929d04b..09de452 100644 --- a/cpp11/tests/08-literal-type/1.cpp +++ b/src/cpp11/tests/08-literal-type/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/08-literal-type/1.cpp +// file: src/cpp11/tests/08-literal-type/1.cpp // // Exercise/练习: cpp11 | 08 - literal type | 自定义字面值类型 // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; struct Vector { diff --git a/cpp11/tests/09-list-initialization/0.cpp b/src/cpp11/tests/09-list-initialization/0.cpp similarity index 94% rename from cpp11/tests/09-list-initialization/0.cpp rename to src/cpp11/tests/09-list-initialization/0.cpp index e9e0cef..f6e1a69 100644 --- a/cpp11/tests/09-list-initialization/0.cpp +++ b/src/cpp11/tests/09-list-initialization/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/09-list-initialization/0.cpp +// file: src/cpp11/tests/09-list-initialization/0.cpp // // Exercise/练习: cpp11 | 09 - list initialization | 窄化检查 // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/cpp11/tests/09-list-initialization/1.cpp b/src/cpp11/tests/09-list-initialization/1.cpp similarity index 93% rename from cpp11/tests/09-list-initialization/1.cpp rename to src/cpp11/tests/09-list-initialization/1.cpp index 6fc9934..a3b5e88 100644 --- a/cpp11/tests/09-list-initialization/1.cpp +++ b/src/cpp11/tests/09-list-initialization/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/09-list-initialization/1.cpp +// file: src/cpp11/tests/09-list-initialization/1.cpp // // Exercise/练习: cpp11 | 09 - list initialization | 默认初始化语法陷阱 // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct Object { diff --git a/cpp11/tests/09-list-initialization/2.cpp b/src/cpp11/tests/09-list-initialization/2.cpp similarity index 94% rename from cpp11/tests/09-list-initialization/2.cpp rename to src/cpp11/tests/09-list-initialization/2.cpp index f37fe73..eefc9b2 100644 --- a/cpp11/tests/09-list-initialization/2.cpp +++ b/src/cpp11/tests/09-list-initialization/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/09-list-initialization/2.cpp +// file: src/cpp11/tests/09-list-initialization/2.cpp // // Exercise/练习: cpp11 | 09 - list initialization | 容器列表初始化 // @@ -17,7 +17,7 @@ // import std; -import d2x.harness; +import d2x; class MyVector { diff --git a/cpp11/tests/09-list-initialization/3.cpp b/src/cpp11/tests/09-list-initialization/3.cpp similarity index 96% rename from cpp11/tests/09-list-initialization/3.cpp rename to src/cpp11/tests/09-list-initialization/3.cpp index bbe5d32..ea2e2da 100644 --- a/cpp11/tests/09-list-initialization/3.cpp +++ b/src/cpp11/tests/09-list-initialization/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/09-list-initialization/3.cpp +// file: src/cpp11/tests/09-list-initialization/3.cpp // // Exercise/练习: cpp11 | 09 - list initialization | 注意事项 // @@ -17,7 +17,7 @@ // import std; -import d2x.harness; +import d2x; class MyVector { diff --git a/cpp11/tests/10-delegating-constructors/0.cpp b/src/cpp11/tests/10-delegating-constructors/0.cpp similarity index 96% rename from cpp11/tests/10-delegating-constructors/0.cpp rename to src/cpp11/tests/10-delegating-constructors/0.cpp index 53eea89..9471ee0 100644 --- a/cpp11/tests/10-delegating-constructors/0.cpp +++ b/src/cpp11/tests/10-delegating-constructors/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/10-delegating-constructors/0.cpp +// file: src/cpp11/tests/10-delegating-constructors/0.cpp // // Exercise/练习: cpp11 | 10 - delegating constructors | 委托构造函数 // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; static int construction_counter { 0 }; diff --git a/cpp11/tests/10-delegating-constructors/1.cpp b/src/cpp11/tests/10-delegating-constructors/1.cpp similarity index 96% rename from cpp11/tests/10-delegating-constructors/1.cpp rename to src/cpp11/tests/10-delegating-constructors/1.cpp index 0e1fcb2..db72db0 100644 --- a/cpp11/tests/10-delegating-constructors/1.cpp +++ b/src/cpp11/tests/10-delegating-constructors/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/10-delegating-constructors/1.cpp +// file: src/cpp11/tests/10-delegating-constructors/1.cpp // // Exercise/练习: cpp11 | 10 - delegating constructors | 委托构造函数注意事项 // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct Object { // 不要修改这个类的代码 diff --git a/cpp11/tests/11-inherited-constructors/0.cpp b/src/cpp11/tests/11-inherited-constructors/0.cpp similarity index 96% rename from cpp11/tests/11-inherited-constructors/0.cpp rename to src/cpp11/tests/11-inherited-constructors/0.cpp index 0419e6d..5457644 100644 --- a/cpp11/tests/11-inherited-constructors/0.cpp +++ b/src/cpp11/tests/11-inherited-constructors/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/11-inherited-constructors/0.cpp +// file: src/cpp11/tests/11-inherited-constructors/0.cpp // // Exercise/练习: cpp11 | 11 - inherited constructors | 继承构造函数 // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; class ObjectBase { diff --git a/cpp11/tests/11-inherited-constructors/1.cpp b/src/cpp11/tests/11-inherited-constructors/1.cpp similarity index 97% rename from cpp11/tests/11-inherited-constructors/1.cpp rename to src/cpp11/tests/11-inherited-constructors/1.cpp index 44cca90..352194f 100644 --- a/cpp11/tests/11-inherited-constructors/1.cpp +++ b/src/cpp11/tests/11-inherited-constructors/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/11-inherited-constructors/1.cpp +// file: src/cpp11/tests/11-inherited-constructors/1.cpp // // Exercise/练习: cpp11 | 11 - inherited constructors | 继承构造函数于功能扩展 // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; class Student { // 不要直接修改 Student 类中的代码 diff --git a/cpp11/tests/11-inherited-constructors/2.cpp b/src/cpp11/tests/11-inherited-constructors/2.cpp similarity index 96% rename from cpp11/tests/11-inherited-constructors/2.cpp rename to src/cpp11/tests/11-inherited-constructors/2.cpp index 425ce63..b354821 100644 --- a/cpp11/tests/11-inherited-constructors/2.cpp +++ b/src/cpp11/tests/11-inherited-constructors/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/11-inherited-constructors/2.cpp +// file: src/cpp11/tests/11-inherited-constructors/2.cpp // // Exercise/练习: cpp11 | 11 - inherited constructors | 继承构造函数在泛型装饰器的应用 // @@ -17,7 +17,7 @@ // import std; -import d2x.harness; +import d2x; struct Point { diff --git a/cpp11/tests/12-nullptr/0.cpp b/src/cpp11/tests/12-nullptr/0.cpp similarity index 96% rename from cpp11/tests/12-nullptr/0.cpp rename to src/cpp11/tests/12-nullptr/0.cpp index e4bf4ec..72f72f8 100644 --- a/cpp11/tests/12-nullptr/0.cpp +++ b/src/cpp11/tests/12-nullptr/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/12-nullptr/0.cpp +// file: src/cpp11/tests/12-nullptr/0.cpp // // Exercise/练习: cpp11 | 12 - nullptr | 指针字面量 - 基础用法 // @@ -19,7 +19,7 @@ #include // NULL 宏是这一课的教具,import std 不带宏 import std; -import d2x.harness; +import d2x; int main() { diff --git a/cpp11/tests/12-nullptr/1.cpp b/src/cpp11/tests/12-nullptr/1.cpp similarity index 95% rename from cpp11/tests/12-nullptr/1.cpp rename to src/cpp11/tests/12-nullptr/1.cpp index 1cc4d64..1fd6549 100644 --- a/cpp11/tests/12-nullptr/1.cpp +++ b/src/cpp11/tests/12-nullptr/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/12-nullptr/1.cpp +// file: src/cpp11/tests/12-nullptr/1.cpp // // Exercise/练习: cpp11 | 12 - nullptr | 指针字面量 - 函数重载 // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; bool process_int_called = false; bool process_ptr_called = false; diff --git a/cpp11/tests/12-nullptr/2.cpp b/src/cpp11/tests/12-nullptr/2.cpp similarity index 95% rename from cpp11/tests/12-nullptr/2.cpp rename to src/cpp11/tests/12-nullptr/2.cpp index b9e6c95..81d7a8d 100644 --- a/cpp11/tests/12-nullptr/2.cpp +++ b/src/cpp11/tests/12-nullptr/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/12-nullptr/2.cpp +// file: src/cpp11/tests/12-nullptr/2.cpp // // Exercise/练习: cpp11 | 12 - nullptr | 指针字面量 - 模板编程 // @@ -19,7 +19,7 @@ #include // NULL 宏是这一课的教具,import std 不带宏 import std; -import d2x.harness; +import d2x; // 模板函数示例 template diff --git a/cpp11/tests/13-long-long/0.cpp b/src/cpp11/tests/13-long-long/0.cpp similarity index 95% rename from cpp11/tests/13-long-long/0.cpp rename to src/cpp11/tests/13-long-long/0.cpp index 5fff88b..f858057 100644 --- a/cpp11/tests/13-long-long/0.cpp +++ b/src/cpp11/tests/13-long-long/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/13-long-long/0.cpp +// file: src/cpp11/tests/13-long-long/0.cpp // // Exercise/练习: cpp11 | 13 - long long | 64位整数类型 - 基础用法 // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/cpp11/tests/13-long-long/1.cpp b/src/cpp11/tests/13-long-long/1.cpp similarity index 95% rename from cpp11/tests/13-long-long/1.cpp rename to src/cpp11/tests/13-long-long/1.cpp index f1ea697..119747f 100644 --- a/cpp11/tests/13-long-long/1.cpp +++ b/src/cpp11/tests/13-long-long/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/13-long-long/1.cpp +// file: src/cpp11/tests/13-long-long/1.cpp // // Exercise/练习: cpp11 | 13 - long long | 64位整数类型 - 大数应用和边界值 // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/cpp11/tests/14-type-alias/0.cpp b/src/cpp11/tests/14-type-alias/0.cpp similarity index 94% rename from cpp11/tests/14-type-alias/0.cpp rename to src/cpp11/tests/14-type-alias/0.cpp index 73f1dff..f55b741 100644 --- a/cpp11/tests/14-type-alias/0.cpp +++ b/src/cpp11/tests/14-type-alias/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/14-type-alias/0.cpp +// file: src/cpp11/tests/14-type-alias/0.cpp // // Exercise/练习: cpp11 | 14 - type alias | 基本类型别名 // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/cpp11/tests/14-type-alias/1.cpp b/src/cpp11/tests/14-type-alias/1.cpp similarity index 95% rename from cpp11/tests/14-type-alias/1.cpp rename to src/cpp11/tests/14-type-alias/1.cpp index 894a05f..8f9baad 100644 --- a/cpp11/tests/14-type-alias/1.cpp +++ b/src/cpp11/tests/14-type-alias/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/14-type-alias/1.cpp +// file: src/cpp11/tests/14-type-alias/1.cpp // // Exercise/练习: cpp11 | 14 - type alias | 复杂类型和函数指针别名 // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; static int func_called = 0; diff --git a/cpp11/tests/14-type-alias/2.cpp b/src/cpp11/tests/14-type-alias/2.cpp similarity index 95% rename from cpp11/tests/14-type-alias/2.cpp rename to src/cpp11/tests/14-type-alias/2.cpp index 542f620..e4603a0 100644 --- a/cpp11/tests/14-type-alias/2.cpp +++ b/src/cpp11/tests/14-type-alias/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/14-type-alias/2.cpp +// file: src/cpp11/tests/14-type-alias/2.cpp // // Exercise/练习: cpp11 | 14 - type alias | 别名模板基础 // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; // 1. 基本别名模板 diff --git a/cpp11/tests/14-type-alias/3.cpp b/src/cpp11/tests/14-type-alias/3.cpp similarity index 93% rename from cpp11/tests/14-type-alias/3.cpp rename to src/cpp11/tests/14-type-alias/3.cpp index e7040df..e7a5ec9 100644 --- a/cpp11/tests/14-type-alias/3.cpp +++ b/src/cpp11/tests/14-type-alias/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/14-type-alias/3.cpp +// file: src/cpp11/tests/14-type-alias/3.cpp // // Exercise/练习: cpp11 | 14 - type alias | 标准库中的别名模板应用 // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/cpp11/tests/15-variadic-templates/0.cpp b/src/cpp11/tests/15-variadic-templates/0.cpp similarity index 94% rename from cpp11/tests/15-variadic-templates/0.cpp rename to src/cpp11/tests/15-variadic-templates/0.cpp index 153674d..278b934 100644 --- a/cpp11/tests/15-variadic-templates/0.cpp +++ b/src/cpp11/tests/15-variadic-templates/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/15-variadic-templates/0.cpp +// file: src/cpp11/tests/15-variadic-templates/0.cpp // // Exercise/练习: cpp11 | 15 - variadic templates | 可变参数模板基础 // @@ -21,7 +21,7 @@ // import std; -import d2x.harness; +import d2x; std::stringstream ss; diff --git a/cpp11/tests/15-variadic-templates/1.cpp b/src/cpp11/tests/15-variadic-templates/1.cpp similarity index 93% rename from cpp11/tests/15-variadic-templates/1.cpp rename to src/cpp11/tests/15-variadic-templates/1.cpp index 92a7f1b..6eaefb8 100644 --- a/cpp11/tests/15-variadic-templates/1.cpp +++ b/src/cpp11/tests/15-variadic-templates/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/15-variadic-templates/1.cpp +// file: src/cpp11/tests/15-variadic-templates/1.cpp // // Exercise/练习: cpp11 | 15 - variadic templates | 可变参数模板求和 // @@ -14,7 +14,7 @@ // import std; -import d2x.harness; +import d2x; D2X_YOUR_ANSWER diff --git a/cpp11/tests/16-generalized-unions/0.cpp b/src/cpp11/tests/16-generalized-unions/0.cpp similarity index 92% rename from cpp11/tests/16-generalized-unions/0.cpp rename to src/cpp11/tests/16-generalized-unions/0.cpp index 847b132..ce5c71f 100644 --- a/cpp11/tests/16-generalized-unions/0.cpp +++ b/src/cpp11/tests/16-generalized-unions/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/16-generalized-unions/0.cpp +// file: src/cpp11/tests/16-generalized-unions/0.cpp // // Exercise/练习: cpp11 | 16 - generalized unions | 广义非平凡联合体 // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; union M diff --git a/cpp11/tests/16-generalized-unions/1.cpp b/src/cpp11/tests/16-generalized-unions/1.cpp similarity index 93% rename from cpp11/tests/16-generalized-unions/1.cpp rename to src/cpp11/tests/16-generalized-unions/1.cpp index c3b18a7..df7d1b7 100644 --- a/cpp11/tests/16-generalized-unions/1.cpp +++ b/src/cpp11/tests/16-generalized-unions/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/16-generalized-unions/1.cpp +// file: src/cpp11/tests/16-generalized-unions/1.cpp // // Exercise/练习: cpp11 | 16 - generalized unions | 广义非平凡联合体 // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; union M { diff --git a/cpp11/tests/16-generalized-unions/2.cpp b/src/cpp11/tests/16-generalized-unions/2.cpp similarity index 97% rename from cpp11/tests/16-generalized-unions/2.cpp rename to src/cpp11/tests/16-generalized-unions/2.cpp index 1f739aa..21ab994 100644 --- a/cpp11/tests/16-generalized-unions/2.cpp +++ b/src/cpp11/tests/16-generalized-unions/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/16-generalized-unions/2.cpp +// file: src/cpp11/tests/16-generalized-unions/2.cpp // // Exercise/练习: cpp11 | 16 - generalized unions | 带标签的鉴别联合体 // @@ -21,7 +21,7 @@ // import std; -import d2x.harness; +import d2x; // 标签类型 — 标记联合体中哪个成员是活跃的 enum class Tag { diff --git a/cpp11/tests/17-pod-type/0.cpp b/src/cpp11/tests/17-pod-type/0.cpp similarity index 96% rename from cpp11/tests/17-pod-type/0.cpp rename to src/cpp11/tests/17-pod-type/0.cpp index 9149270..407b6db 100644 --- a/cpp11/tests/17-pod-type/0.cpp +++ b/src/cpp11/tests/17-pod-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/17-pod-type/0.cpp +// file: src/cpp11/tests/17-pod-type/0.cpp // // Exercise/练习: cpp11 | 17 - POD Type | 基本类型性质判断 // @@ -19,7 +19,7 @@ // d2x checker pod-type import std; -import d2x.harness; +import d2x; struct A { diff --git a/cpp11/tests/17-pod-type/1.cpp b/src/cpp11/tests/17-pod-type/1.cpp similarity index 95% rename from cpp11/tests/17-pod-type/1.cpp rename to src/cpp11/tests/17-pod-type/1.cpp index 43b0452..fcfa251 100644 --- a/cpp11/tests/17-pod-type/1.cpp +++ b/src/cpp11/tests/17-pod-type/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/17-pod-type/1.cpp +// file: src/cpp11/tests/17-pod-type/1.cpp // // Exercise/练习: cpp11 | 17 - POD Type | 按字节拷贝 POD 结构体 // @@ -19,7 +19,7 @@ // d2x checker pod-type import std; -import d2x.harness; +import d2x; diff --git a/cpp11/tests/17-pod-type/2.cpp b/src/cpp11/tests/17-pod-type/2.cpp similarity index 97% rename from cpp11/tests/17-pod-type/2.cpp rename to src/cpp11/tests/17-pod-type/2.cpp index a5de1c2..700cf50 100644 --- a/cpp11/tests/17-pod-type/2.cpp +++ b/src/cpp11/tests/17-pod-type/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp11/tests/17-pod-type/2.cpp +// file: src/cpp11/tests/17-pod-type/2.cpp // // Exercise/练习: cpp11 | 17 - POD Type | 适配 C 接口的 POD 头部 // @@ -19,7 +19,7 @@ // d2x checker pod-type import std; -import d2x.harness; +import d2x; diff --git a/src/cpp14/mcpp.toml b/src/cpp14/mcpp.toml new file mode 100644 index 0000000..4be442e --- /dev/null +++ b/src/cpp14/mcpp.toml @@ -0,0 +1,10 @@ +# cpp14 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: +# mcpp test -p src/cpp14 全部练习(进度表) +# mcpp test -p src/cpp14 <子串> 只跑匹配的练习 +[package] +name = "cpp14" +version = "0.1.0" +standard = "c++23" + +[dependencies] +d2x = { path = "../../d2x" } diff --git a/cpp14/tests/00-generic-lambdas/0.cpp b/src/cpp14/tests/00-generic-lambdas/0.cpp similarity index 97% rename from cpp14/tests/00-generic-lambdas/0.cpp rename to src/cpp14/tests/00-generic-lambdas/0.cpp index 7a39550..d072e0b 100644 --- a/cpp14/tests/00-generic-lambdas/0.cpp +++ b/src/cpp14/tests/00-generic-lambdas/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp14/tests/00-generic-lambdas/0.cpp +// file: src/cpp14/tests/00-generic-lambdas/0.cpp // // Exercise/练习: cpp14 | 00 - generic lambdas | 泛型 lambda // diff --git a/cpp14/tests/00-generic-lambdas/1.cpp b/src/cpp14/tests/00-generic-lambdas/1.cpp similarity index 96% rename from cpp14/tests/00-generic-lambdas/1.cpp rename to src/cpp14/tests/00-generic-lambdas/1.cpp index 2bc06a3..561be60 100644 --- a/cpp14/tests/00-generic-lambdas/1.cpp +++ b/src/cpp14/tests/00-generic-lambdas/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: cpp14/tests/00-generic-lambdas/1.cpp +// file: src/cpp14/tests/00-generic-lambdas/1.cpp // // Exercise/练习: cpp14 | 00 - generic lambdas | 泛型 lambda 与 STL 算法 // @@ -20,7 +20,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp11/mcpp.toml b/src/en/cpp11/mcpp.toml similarity index 52% rename from en/cpp11/mcpp.toml rename to src/en/cpp11/mcpp.toml index 7c2d85d..805a7ca 100644 --- a/en/cpp11/mcpp.toml +++ b/src/en/cpp11/mcpp.toml @@ -1,10 +1,10 @@ # cpp11 (en) 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: -# mcpp test -p en/cpp11 全部练习(进度表) -# mcpp test -p en/cpp11 <子串> 只跑匹配的练习 +# mcpp test -p src/en/cpp11 全部练习(进度表) +# mcpp test -p src/en/cpp11 <子串> 只跑匹配的练习 [package] name = "en-cpp11" version = "0.1.0" standard = "c++23" [dependencies] -harness = { path = "../../harness" } +d2x = { path = "../../../d2x" } diff --git a/en/cpp11/tests/00-auto-and-decltype/0.cpp b/src/en/cpp11/tests/00-auto-and-decltype/0.cpp similarity index 94% rename from en/cpp11/tests/00-auto-and-decltype/0.cpp rename to src/en/cpp11/tests/00-auto-and-decltype/0.cpp index 6a871c2..da2e8e2 100644 --- a/en/cpp11/tests/00-auto-and-decltype/0.cpp +++ b/src/en/cpp11/tests/00-auto-and-decltype/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/00-auto-and-decltype/0.cpp +// file: src/en/cpp11/tests/00-auto-and-decltype/0.cpp // // Exercise: cpp11 | 00 - auto and decltype | Automatic Type Deduction // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp11/tests/00-auto-and-decltype/1.cpp b/src/en/cpp11/tests/00-auto-and-decltype/1.cpp similarity index 94% rename from en/cpp11/tests/00-auto-and-decltype/1.cpp rename to src/en/cpp11/tests/00-auto-and-decltype/1.cpp index bc9ce6d..1f1c08d 100644 --- a/en/cpp11/tests/00-auto-and-decltype/1.cpp +++ b/src/en/cpp11/tests/00-auto-and-decltype/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/00-auto-and-decltype/1.cpp +// file: src/en/cpp11/tests/00-auto-and-decltype/1.cpp // // Exercise: cpp11 | 00 - auto and decltype | Expression Type Deduction // @@ -20,7 +20,7 @@ import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp11/tests/00-auto-and-decltype/2.cpp b/src/en/cpp11/tests/00-auto-and-decltype/2.cpp similarity index 95% rename from en/cpp11/tests/00-auto-and-decltype/2.cpp rename to src/en/cpp11/tests/00-auto-and-decltype/2.cpp index 251dbae..664480d 100644 --- a/en/cpp11/tests/00-auto-and-decltype/2.cpp +++ b/src/en/cpp11/tests/00-auto-and-decltype/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/00-auto-and-decltype/2.cpp +// file: src/en/cpp11/tests/00-auto-and-decltype/2.cpp // // Exercise: cpp11 | 00 - auto and decltype | Complex Type Deduction // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; int add_func(int a, int b) { diff --git a/en/cpp11/tests/00-auto-and-decltype/3.cpp b/src/en/cpp11/tests/00-auto-and-decltype/3.cpp similarity index 93% rename from en/cpp11/tests/00-auto-and-decltype/3.cpp rename to src/en/cpp11/tests/00-auto-and-decltype/3.cpp index 71470e8..fbe07b1 100644 --- a/en/cpp11/tests/00-auto-and-decltype/3.cpp +++ b/src/en/cpp11/tests/00-auto-and-decltype/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/00-auto-and-decltype/3.cpp +// file: src/en/cpp11/tests/00-auto-and-decltype/3.cpp // // Exercise: cpp11 | 00 - auto and decltype | Function Return Type Deduction // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; // 3. Function return types diff --git a/en/cpp11/tests/00-auto-and-decltype/4.cpp b/src/en/cpp11/tests/00-auto-and-decltype/4.cpp similarity index 96% rename from en/cpp11/tests/00-auto-and-decltype/4.cpp rename to src/en/cpp11/tests/00-auto-and-decltype/4.cpp index 65e24ed..e3d9059 100644 --- a/en/cpp11/tests/00-auto-and-decltype/4.cpp +++ b/src/en/cpp11/tests/00-auto-and-decltype/4.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/00-auto-and-decltype/4.cpp +// file: src/en/cpp11/tests/00-auto-and-decltype/4.cpp // // Exercise: cpp11 | 00 - auto and decltype | Class/Struct Member Type Deduction // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/en/cpp11/tests/00-auto-and-decltype/5.cpp b/src/en/cpp11/tests/00-auto-and-decltype/5.cpp similarity index 96% rename from en/cpp11/tests/00-auto-and-decltype/5.cpp rename to src/en/cpp11/tests/00-auto-and-decltype/5.cpp index c30f3ad..628371e 100644 --- a/en/cpp11/tests/00-auto-and-decltype/5.cpp +++ b/src/en/cpp11/tests/00-auto-and-decltype/5.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/00-auto-and-decltype/5.cpp +// file: src/en/cpp11/tests/00-auto-and-decltype/5.cpp // // Exercise: cpp11 | 00 - auto and decltype | const & reference stripping and preservation // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/en/cpp11/tests/01-default-and-delete/0.cpp b/src/en/cpp11/tests/01-default-and-delete/0.cpp similarity index 92% rename from en/cpp11/tests/01-default-and-delete/0.cpp rename to src/en/cpp11/tests/01-default-and-delete/0.cpp index 2c9e868..f47b3ed 100644 --- a/en/cpp11/tests/01-default-and-delete/0.cpp +++ b/src/en/cpp11/tests/01-default-and-delete/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/01-default-and-delete/0.cpp +// file: src/en/cpp11/tests/01-default-and-delete/0.cpp // // Exercise: cpp11 | 01 - default and delete | Explicitly specifying constructor generation behavior // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; // default and delete explicitly control -> compiler default constructor generation behavior diff --git a/en/cpp11/tests/01-default-and-delete/1.cpp b/src/en/cpp11/tests/01-default-and-delete/1.cpp similarity index 95% rename from en/cpp11/tests/01-default-and-delete/1.cpp rename to src/en/cpp11/tests/01-default-and-delete/1.cpp index 600af67..5c5c94b 100644 --- a/en/cpp11/tests/01-default-and-delete/1.cpp +++ b/src/en/cpp11/tests/01-default-and-delete/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/01-default-and-delete/1.cpp +// file: src/en/cpp11/tests/01-default-and-delete/1.cpp // // Exercise: cpp11 | 01 - default and delete | Non-copyable objects // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; // Implement std::unique_ptr property: not copyable but movable diff --git a/en/cpp11/tests/01-default-and-delete/2.cpp b/src/en/cpp11/tests/01-default-and-delete/2.cpp similarity index 90% rename from en/cpp11/tests/01-default-and-delete/2.cpp rename to src/en/cpp11/tests/01-default-and-delete/2.cpp index aa9ea5d..a6ba9d3 100644 --- a/en/cpp11/tests/01-default-and-delete/2.cpp +++ b/src/en/cpp11/tests/01-default-and-delete/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/01-default-and-delete/2.cpp +// file: src/en/cpp11/tests/01-default-and-delete/2.cpp // // Exercise: cpp11 | 01 - default and delete | Disabling function overloading // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; void func(int x) { diff --git a/en/cpp11/tests/02-final-and-override/0.cpp b/src/en/cpp11/tests/02-final-and-override/0.cpp similarity index 93% rename from en/cpp11/tests/02-final-and-override/0.cpp rename to src/en/cpp11/tests/02-final-and-override/0.cpp index db4c662..c60461a 100644 --- a/en/cpp11/tests/02-final-and-override/0.cpp +++ b/src/en/cpp11/tests/02-final-and-override/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/02-final-and-override/0.cpp +// file: src/en/cpp11/tests/02-final-and-override/0.cpp // // Exercise: cpp11 | 02 - final and override // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct A { diff --git a/en/cpp11/tests/02-final-and-override/1.cpp b/src/en/cpp11/tests/02-final-and-override/1.cpp similarity index 93% rename from en/cpp11/tests/02-final-and-override/1.cpp rename to src/en/cpp11/tests/02-final-and-override/1.cpp index 7580f54..e4e85c3 100644 --- a/en/cpp11/tests/02-final-and-override/1.cpp +++ b/src/en/cpp11/tests/02-final-and-override/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/02-final-and-override/1.cpp +// file: src/en/cpp11/tests/02-final-and-override/1.cpp // // Exercise: cpp11 | 02 - final and override // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct A { diff --git a/en/cpp11/tests/02-final-and-override/2.cpp b/src/en/cpp11/tests/02-final-and-override/2.cpp similarity index 96% rename from en/cpp11/tests/02-final-and-override/2.cpp rename to src/en/cpp11/tests/02-final-and-override/2.cpp index 4a61163..f3b34d5 100644 --- a/en/cpp11/tests/02-final-and-override/2.cpp +++ b/src/en/cpp11/tests/02-final-and-override/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/02-final-and-override/2.cpp +// file: src/en/cpp11/tests/02-final-and-override/2.cpp // // Exercise: cpp11 | 02 - final and override // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct AudioPlayer { // Do not directly modify the AudioPlayer class diff --git a/en/cpp11/tests/03-trailing-return-type/0.cpp b/src/en/cpp11/tests/03-trailing-return-type/0.cpp similarity index 93% rename from en/cpp11/tests/03-trailing-return-type/0.cpp rename to src/en/cpp11/tests/03-trailing-return-type/0.cpp index 20f36ff..1ec229e 100644 --- a/en/cpp11/tests/03-trailing-return-type/0.cpp +++ b/src/en/cpp11/tests/03-trailing-return-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/03-trailing-return-type/0.cpp +// file: src/en/cpp11/tests/03-trailing-return-type/0.cpp // // Exercise: cpp11 | 03 - trailing return type // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; int add0(double a, int b) { diff --git a/en/cpp11/tests/04-rvalue-references/0.cpp b/src/en/cpp11/tests/04-rvalue-references/0.cpp similarity index 97% rename from en/cpp11/tests/04-rvalue-references/0.cpp rename to src/en/cpp11/tests/04-rvalue-references/0.cpp index 871e4c1..e34bed1 100644 --- a/en/cpp11/tests/04-rvalue-references/0.cpp +++ b/src/en/cpp11/tests/04-rvalue-references/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/04-rvalue-references/0.cpp +// file: src/en/cpp11/tests/04-rvalue-references/0.cpp // // Exercise: cpp11 | 04 - rvalue references // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; struct Object; diff --git a/en/cpp11/tests/05-move-semantics/0.cpp b/src/en/cpp11/tests/05-move-semantics/0.cpp similarity index 96% rename from en/cpp11/tests/05-move-semantics/0.cpp rename to src/en/cpp11/tests/05-move-semantics/0.cpp index 5461403..8b6df38 100644 --- a/en/cpp11/tests/05-move-semantics/0.cpp +++ b/src/en/cpp11/tests/05-move-semantics/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/05-move-semantics/0.cpp +// file: src/en/cpp11/tests/05-move-semantics/0.cpp // // Exercise: cpp11 | 05 - move semantics | Move Construction and Trigger Timing // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; struct Buffer { diff --git a/en/cpp11/tests/05-move-semantics/1.cpp b/src/en/cpp11/tests/05-move-semantics/1.cpp similarity index 97% rename from en/cpp11/tests/05-move-semantics/1.cpp rename to src/en/cpp11/tests/05-move-semantics/1.cpp index a87c0f7..11b4e2c 100644 --- a/en/cpp11/tests/05-move-semantics/1.cpp +++ b/src/en/cpp11/tests/05-move-semantics/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/05-move-semantics/1.cpp +// file: src/en/cpp11/tests/05-move-semantics/1.cpp // // Exercise: cpp11 | 05 - move semantics | Move Assignment and Trigger Timing // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; diff --git a/en/cpp11/tests/05-move-semantics/2.cpp b/src/en/cpp11/tests/05-move-semantics/2.cpp similarity index 97% rename from en/cpp11/tests/05-move-semantics/2.cpp rename to src/en/cpp11/tests/05-move-semantics/2.cpp index c8224de..847db82 100644 --- a/en/cpp11/tests/05-move-semantics/2.cpp +++ b/src/en/cpp11/tests/05-move-semantics/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/05-move-semantics/2.cpp +// file: src/en/cpp11/tests/05-move-semantics/2.cpp // // Exercise: cpp11 | 05 - move semantics | Moving resources not objects // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; struct Buffer { diff --git a/en/cpp11/tests/06-scoped-enums/0.cpp b/src/en/cpp11/tests/06-scoped-enums/0.cpp similarity index 93% rename from en/cpp11/tests/06-scoped-enums/0.cpp rename to src/en/cpp11/tests/06-scoped-enums/0.cpp index ac5b251..17980c1 100644 --- a/en/cpp11/tests/06-scoped-enums/0.cpp +++ b/src/en/cpp11/tests/06-scoped-enums/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/06-scoped-enums/0.cpp +// file: src/en/cpp11/tests/06-scoped-enums/0.cpp // // Exercise: cpp11 | 06 - scoped enums | Traditional enum type potential issues // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; enum Color { diff --git a/en/cpp11/tests/06-scoped-enums/1.cpp b/src/en/cpp11/tests/06-scoped-enums/1.cpp similarity index 96% rename from en/cpp11/tests/06-scoped-enums/1.cpp rename to src/en/cpp11/tests/06-scoped-enums/1.cpp index cfc347d..3f4b207 100644 --- a/en/cpp11/tests/06-scoped-enums/1.cpp +++ b/src/en/cpp11/tests/06-scoped-enums/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/06-scoped-enums/1.cpp +// file: src/en/cpp11/tests/06-scoped-enums/1.cpp // // Exercise: cpp11 | 06 - scoped enums | Scoped enum type basic usage // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; enum class Color { diff --git a/en/cpp11/tests/07-constexpr/0.cpp b/src/en/cpp11/tests/07-constexpr/0.cpp similarity index 93% rename from en/cpp11/tests/07-constexpr/0.cpp rename to src/en/cpp11/tests/07-constexpr/0.cpp index 52e429e..a68b340 100644 --- a/en/cpp11/tests/07-constexpr/0.cpp +++ b/src/en/cpp11/tests/07-constexpr/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/07-constexpr/0.cpp +// file: src/en/cpp11/tests/07-constexpr/0.cpp // // Exercise: cpp11 | 07 - constexpr | Compile-time computation basics: Difference between constexpr and const // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; int sum_for_1_to(int n) { diff --git a/en/cpp11/tests/07-constexpr/1.cpp b/src/en/cpp11/tests/07-constexpr/1.cpp similarity index 96% rename from en/cpp11/tests/07-constexpr/1.cpp rename to src/en/cpp11/tests/07-constexpr/1.cpp index 5f7aaf1..1fd5acc 100644 --- a/en/cpp11/tests/07-constexpr/1.cpp +++ b/src/en/cpp11/tests/07-constexpr/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/07-constexpr/1.cpp +// file: src/en/cpp11/tests/07-constexpr/1.cpp // // Exercise: cpp11 | 07 - constexpr | Compile-time computation application examples // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; template diff --git a/en/cpp11/tests/08-literal-type/0.cpp b/src/en/cpp11/tests/08-literal-type/0.cpp similarity index 95% rename from en/cpp11/tests/08-literal-type/0.cpp rename to src/en/cpp11/tests/08-literal-type/0.cpp index a99b103..0a8b098 100644 --- a/en/cpp11/tests/08-literal-type/0.cpp +++ b/src/en/cpp11/tests/08-literal-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/08-literal-type/0.cpp +// file: src/en/cpp11/tests/08-literal-type/0.cpp // // Exercise: cpp11 | 08 - literal type | What are literal types // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; constexpr char compile_time_compute(char c, int a) { diff --git a/en/cpp11/tests/08-literal-type/1.cpp b/src/en/cpp11/tests/08-literal-type/1.cpp similarity index 92% rename from en/cpp11/tests/08-literal-type/1.cpp rename to src/en/cpp11/tests/08-literal-type/1.cpp index 7d5db3f..b75ad25 100644 --- a/en/cpp11/tests/08-literal-type/1.cpp +++ b/src/en/cpp11/tests/08-literal-type/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/08-literal-type/1.cpp +// file: src/en/cpp11/tests/08-literal-type/1.cpp // // Exercise: cpp11 | 08 - literal type | Custom literal types // @@ -15,7 +15,7 @@ // import std; -import d2x.harness; +import d2x; struct Vector { diff --git a/en/cpp11/tests/09-list-initialization/0.cpp b/src/en/cpp11/tests/09-list-initialization/0.cpp similarity index 94% rename from en/cpp11/tests/09-list-initialization/0.cpp rename to src/en/cpp11/tests/09-list-initialization/0.cpp index dd8d73f..6cbed68 100644 --- a/en/cpp11/tests/09-list-initialization/0.cpp +++ b/src/en/cpp11/tests/09-list-initialization/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/09-list-initialization/0.cpp +// file: src/en/cpp11/tests/09-list-initialization/0.cpp // // Exercise: cpp11 | 09 - list initialization | Narrowing checks // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp11/tests/09-list-initialization/1.cpp b/src/en/cpp11/tests/09-list-initialization/1.cpp similarity index 92% rename from en/cpp11/tests/09-list-initialization/1.cpp rename to src/en/cpp11/tests/09-list-initialization/1.cpp index 52357b1..7a5a5f2 100644 --- a/en/cpp11/tests/09-list-initialization/1.cpp +++ b/src/en/cpp11/tests/09-list-initialization/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/09-list-initialization/1.cpp +// file: src/en/cpp11/tests/09-list-initialization/1.cpp // // Exercise: cpp11 | 09 - list initialization | Default initialization syntax pitfalls // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct Object { diff --git a/en/cpp11/tests/09-list-initialization/2.cpp b/src/en/cpp11/tests/09-list-initialization/2.cpp similarity index 94% rename from en/cpp11/tests/09-list-initialization/2.cpp rename to src/en/cpp11/tests/09-list-initialization/2.cpp index ca84a41..2e245e3 100644 --- a/en/cpp11/tests/09-list-initialization/2.cpp +++ b/src/en/cpp11/tests/09-list-initialization/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/09-list-initialization/2.cpp +// file: src/en/cpp11/tests/09-list-initialization/2.cpp // // Exercise: cpp11 | 09 - list initialization | Container list initialization // @@ -17,7 +17,7 @@ // import std; -import d2x.harness; +import d2x; class MyVector { diff --git a/en/cpp11/tests/09-list-initialization/3.cpp b/src/en/cpp11/tests/09-list-initialization/3.cpp similarity index 96% rename from en/cpp11/tests/09-list-initialization/3.cpp rename to src/en/cpp11/tests/09-list-initialization/3.cpp index 0b5cd94..82b80d1 100644 --- a/en/cpp11/tests/09-list-initialization/3.cpp +++ b/src/en/cpp11/tests/09-list-initialization/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/09-list-initialization/3.cpp +// file: src/en/cpp11/tests/09-list-initialization/3.cpp // // Exercise: cpp11 | 09 - list initialization | Precautions // @@ -17,7 +17,7 @@ // import std; -import d2x.harness; +import d2x; class MyVector { diff --git a/en/cpp11/tests/10-delegating-constructors/0.cpp b/src/en/cpp11/tests/10-delegating-constructors/0.cpp similarity index 96% rename from en/cpp11/tests/10-delegating-constructors/0.cpp rename to src/en/cpp11/tests/10-delegating-constructors/0.cpp index 991585d..5818f47 100644 --- a/en/cpp11/tests/10-delegating-constructors/0.cpp +++ b/src/en/cpp11/tests/10-delegating-constructors/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/10-delegating-constructors/0.cpp +// file: src/en/cpp11/tests/10-delegating-constructors/0.cpp // // Exercise: cpp11 | 10 - delegating constructors | Delegating Constructors // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; static int construction_counter { 0 }; diff --git a/en/cpp11/tests/10-delegating-constructors/1.cpp b/src/en/cpp11/tests/10-delegating-constructors/1.cpp similarity index 96% rename from en/cpp11/tests/10-delegating-constructors/1.cpp rename to src/en/cpp11/tests/10-delegating-constructors/1.cpp index 5b15c61..c2a6398 100644 --- a/en/cpp11/tests/10-delegating-constructors/1.cpp +++ b/src/en/cpp11/tests/10-delegating-constructors/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/10-delegating-constructors/1.cpp +// file: src/en/cpp11/tests/10-delegating-constructors/1.cpp // // Exercise: cpp11 | 10 - delegating constructors | Delegating Constructors Precautions // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; struct Object { // Do not modify the code of this class diff --git a/en/cpp11/tests/11-inherited-constructors/0.cpp b/src/en/cpp11/tests/11-inherited-constructors/0.cpp similarity index 95% rename from en/cpp11/tests/11-inherited-constructors/0.cpp rename to src/en/cpp11/tests/11-inherited-constructors/0.cpp index 3af5a91..019e51e 100644 --- a/en/cpp11/tests/11-inherited-constructors/0.cpp +++ b/src/en/cpp11/tests/11-inherited-constructors/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/11-inherited-constructors/0.cpp +// file: src/en/cpp11/tests/11-inherited-constructors/0.cpp // // Exercise: cpp11 | 11 - inherited constructors | Inherited Constructors // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; class ObjectBase { diff --git a/en/cpp11/tests/11-inherited-constructors/1.cpp b/src/en/cpp11/tests/11-inherited-constructors/1.cpp similarity index 97% rename from en/cpp11/tests/11-inherited-constructors/1.cpp rename to src/en/cpp11/tests/11-inherited-constructors/1.cpp index ff96b9b..81a6853 100644 --- a/en/cpp11/tests/11-inherited-constructors/1.cpp +++ b/src/en/cpp11/tests/11-inherited-constructors/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/11-inherited-constructors/1.cpp +// file: src/en/cpp11/tests/11-inherited-constructors/1.cpp // // Exercise: cpp11 | 11 - inherited constructors | Inherited Constructors for Function Extension // @@ -16,7 +16,7 @@ // import std; -import d2x.harness; +import d2x; class Student { // Do not directly modify the code in the Student class diff --git a/en/cpp11/tests/11-inherited-constructors/2.cpp b/src/en/cpp11/tests/11-inherited-constructors/2.cpp similarity index 96% rename from en/cpp11/tests/11-inherited-constructors/2.cpp rename to src/en/cpp11/tests/11-inherited-constructors/2.cpp index 21ba7cf..689abab 100644 --- a/en/cpp11/tests/11-inherited-constructors/2.cpp +++ b/src/en/cpp11/tests/11-inherited-constructors/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/11-inherited-constructors/2.cpp +// file: src/en/cpp11/tests/11-inherited-constructors/2.cpp // // Exercise: cpp11 | 11 - inherited constructors | Inherited Constructors in Generic Decorator Applications // @@ -17,7 +17,7 @@ // import std; -import d2x.harness; +import d2x; struct Point { diff --git a/en/cpp11/tests/12-nullptr/0.cpp b/src/en/cpp11/tests/12-nullptr/0.cpp similarity index 96% rename from en/cpp11/tests/12-nullptr/0.cpp rename to src/en/cpp11/tests/12-nullptr/0.cpp index 6da67eb..dd0bb1a 100644 --- a/en/cpp11/tests/12-nullptr/0.cpp +++ b/src/en/cpp11/tests/12-nullptr/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/12-nullptr/0.cpp +// file: src/en/cpp11/tests/12-nullptr/0.cpp // // Exercise: cpp11 | 12 - nullptr | Pointer Literal - Basic Usage // @@ -19,7 +19,7 @@ #include // NULL 宏是这一课的教具,import std 不带宏 import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp11/tests/12-nullptr/1.cpp b/src/en/cpp11/tests/12-nullptr/1.cpp similarity index 95% rename from en/cpp11/tests/12-nullptr/1.cpp rename to src/en/cpp11/tests/12-nullptr/1.cpp index d08ad29..c098af2 100644 --- a/en/cpp11/tests/12-nullptr/1.cpp +++ b/src/en/cpp11/tests/12-nullptr/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/12-nullptr/1.cpp +// file: src/en/cpp11/tests/12-nullptr/1.cpp // // Exercise: cpp11 | 12 - nullptr | Pointer Literal - Function Overloading // @@ -19,7 +19,7 @@ #include // NULL 宏是这一课的教具,import std 不带宏 import std; -import d2x.harness; +import d2x; bool process_int_called = false; bool process_ptr_called = false; diff --git a/en/cpp11/tests/12-nullptr/2.cpp b/src/en/cpp11/tests/12-nullptr/2.cpp similarity index 95% rename from en/cpp11/tests/12-nullptr/2.cpp rename to src/en/cpp11/tests/12-nullptr/2.cpp index 457f05d..abc89c7 100644 --- a/en/cpp11/tests/12-nullptr/2.cpp +++ b/src/en/cpp11/tests/12-nullptr/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/12-nullptr/2.cpp +// file: src/en/cpp11/tests/12-nullptr/2.cpp // // Exercise: cpp11 | 12 - nullptr | Pointer Literal - Template Programming // @@ -19,7 +19,7 @@ #include // NULL 宏是这一课的教具,import std 不带宏 import std; -import d2x.harness; +import d2x; // Template function example template diff --git a/en/cpp11/tests/13-long-long/0.cpp b/src/en/cpp11/tests/13-long-long/0.cpp similarity index 95% rename from en/cpp11/tests/13-long-long/0.cpp rename to src/en/cpp11/tests/13-long-long/0.cpp index 921a6a4..bda0fe8 100644 --- a/en/cpp11/tests/13-long-long/0.cpp +++ b/src/en/cpp11/tests/13-long-long/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/13-long-long/0.cpp +// file: src/en/cpp11/tests/13-long-long/0.cpp // // Exercise: cpp11 | 13 - long long | 64-bit Integer Type - Basic Usage // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp11/tests/13-long-long/1.cpp b/src/en/cpp11/tests/13-long-long/1.cpp similarity index 95% rename from en/cpp11/tests/13-long-long/1.cpp rename to src/en/cpp11/tests/13-long-long/1.cpp index 6286f62..fe16571 100644 --- a/en/cpp11/tests/13-long-long/1.cpp +++ b/src/en/cpp11/tests/13-long-long/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/13-long-long/1.cpp +// file: src/en/cpp11/tests/13-long-long/1.cpp // // Exercise: cpp11 | 13 - long long | 64-bit Integer Type - Large Number Applications and Boundary Values // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp11/tests/14-type-alias/0.cpp b/src/en/cpp11/tests/14-type-alias/0.cpp similarity index 94% rename from en/cpp11/tests/14-type-alias/0.cpp rename to src/en/cpp11/tests/14-type-alias/0.cpp index 9d9e27f..352b6b8 100644 --- a/en/cpp11/tests/14-type-alias/0.cpp +++ b/src/en/cpp11/tests/14-type-alias/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/14-type-alias/0.cpp +// file: src/en/cpp11/tests/14-type-alias/0.cpp // // Exercise: cpp11 | 14 - type alias | Basic Type Aliases // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp11/tests/14-type-alias/1.cpp b/src/en/cpp11/tests/14-type-alias/1.cpp similarity index 95% rename from en/cpp11/tests/14-type-alias/1.cpp rename to src/en/cpp11/tests/14-type-alias/1.cpp index aa4a4b8..4528ddf 100644 --- a/en/cpp11/tests/14-type-alias/1.cpp +++ b/src/en/cpp11/tests/14-type-alias/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/14-type-alias/1.cpp +// file: src/en/cpp11/tests/14-type-alias/1.cpp // // Exercise: cpp11 | 14 - type alias | Complex Types and Function Pointer Aliases // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; static int func_called = 0; diff --git a/en/cpp11/tests/14-type-alias/2.cpp b/src/en/cpp11/tests/14-type-alias/2.cpp similarity index 95% rename from en/cpp11/tests/14-type-alias/2.cpp rename to src/en/cpp11/tests/14-type-alias/2.cpp index b45b155..4857785 100644 --- a/en/cpp11/tests/14-type-alias/2.cpp +++ b/src/en/cpp11/tests/14-type-alias/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/14-type-alias/2.cpp +// file: src/en/cpp11/tests/14-type-alias/2.cpp // // Exercise: cpp11 | 14 - type alias | Alias Templates Basics // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; // 1. Basic alias template diff --git a/en/cpp11/tests/14-type-alias/3.cpp b/src/en/cpp11/tests/14-type-alias/3.cpp similarity index 93% rename from en/cpp11/tests/14-type-alias/3.cpp rename to src/en/cpp11/tests/14-type-alias/3.cpp index 0ff4aae..f35abb3 100644 --- a/en/cpp11/tests/14-type-alias/3.cpp +++ b/src/en/cpp11/tests/14-type-alias/3.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/14-type-alias/3.cpp +// file: src/en/cpp11/tests/14-type-alias/3.cpp // // Exercise: cpp11 | 14 - type alias | Standard Library Style Alias Templates // @@ -18,7 +18,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp11/tests/15-variadic-templates/0.cpp b/src/en/cpp11/tests/15-variadic-templates/0.cpp similarity index 94% rename from en/cpp11/tests/15-variadic-templates/0.cpp rename to src/en/cpp11/tests/15-variadic-templates/0.cpp index 73638c0..fe85706 100644 --- a/en/cpp11/tests/15-variadic-templates/0.cpp +++ b/src/en/cpp11/tests/15-variadic-templates/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/15-variadic-templates/0.cpp +// file: src/en/cpp11/tests/15-variadic-templates/0.cpp // // Exercise: cpp11 | 15 - variadic templates | Variadic templates basics // @@ -21,7 +21,7 @@ // import std; -import d2x.harness; +import d2x; std::stringstream ss; diff --git a/en/cpp11/tests/15-variadic-templates/1.cpp b/src/en/cpp11/tests/15-variadic-templates/1.cpp similarity index 92% rename from en/cpp11/tests/15-variadic-templates/1.cpp rename to src/en/cpp11/tests/15-variadic-templates/1.cpp index 1221f74..86c6819 100644 --- a/en/cpp11/tests/15-variadic-templates/1.cpp +++ b/src/en/cpp11/tests/15-variadic-templates/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/15-variadic-templates/1.cpp +// file: src/en/cpp11/tests/15-variadic-templates/1.cpp // // Exercise: cpp11 | 15 - variadic templates | Variadic template sum // @@ -14,7 +14,7 @@ // import std; -import d2x.harness; +import d2x; D2X_YOUR_ANSWER diff --git a/en/cpp11/tests/16-generalized-unions/0.cpp b/src/en/cpp11/tests/16-generalized-unions/0.cpp similarity index 92% rename from en/cpp11/tests/16-generalized-unions/0.cpp rename to src/en/cpp11/tests/16-generalized-unions/0.cpp index e9a4c7a..59dced2 100644 --- a/en/cpp11/tests/16-generalized-unions/0.cpp +++ b/src/en/cpp11/tests/16-generalized-unions/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/16-generalized-unions/0.cpp +// file: src/en/cpp11/tests/16-generalized-unions/0.cpp // // Exercise: cpp11 | 16 - generalized unions | generalized (non-trivial) unions // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; union M diff --git a/en/cpp11/tests/16-generalized-unions/1.cpp b/src/en/cpp11/tests/16-generalized-unions/1.cpp similarity index 93% rename from en/cpp11/tests/16-generalized-unions/1.cpp rename to src/en/cpp11/tests/16-generalized-unions/1.cpp index 2e830b0..d0b4be2 100644 --- a/en/cpp11/tests/16-generalized-unions/1.cpp +++ b/src/en/cpp11/tests/16-generalized-unions/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/16-generalized-unions/1.cpp +// file: src/en/cpp11/tests/16-generalized-unions/1.cpp // // Exercise: cpp11 | 16 - generalized unions | generalized (non-trivial) unions // @@ -19,7 +19,7 @@ // import std; -import d2x.harness; +import d2x; union M { diff --git a/en/cpp11/tests/16-generalized-unions/2.cpp b/src/en/cpp11/tests/16-generalized-unions/2.cpp similarity index 97% rename from en/cpp11/tests/16-generalized-unions/2.cpp rename to src/en/cpp11/tests/16-generalized-unions/2.cpp index c818da5..dc7339d 100644 --- a/en/cpp11/tests/16-generalized-unions/2.cpp +++ b/src/en/cpp11/tests/16-generalized-unions/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/16-generalized-unions/2.cpp +// file: src/en/cpp11/tests/16-generalized-unions/2.cpp // // Exercise: cpp11 | 16 - generalized unions | tagged discriminated union // @@ -21,7 +21,7 @@ // import std; -import d2x.harness; +import d2x; // Tag type — marks which member of the union is active enum class Tag { diff --git a/en/cpp11/tests/17-pod-type/0.cpp b/src/en/cpp11/tests/17-pod-type/0.cpp similarity index 96% rename from en/cpp11/tests/17-pod-type/0.cpp rename to src/en/cpp11/tests/17-pod-type/0.cpp index d1eec66..a1dfa13 100644 --- a/en/cpp11/tests/17-pod-type/0.cpp +++ b/src/en/cpp11/tests/17-pod-type/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/17-pod-type/0.cpp +// file: src/en/cpp11/tests/17-pod-type/0.cpp // // Exercise: cpp11 | 17 - POD Type | Basic type properties // @@ -19,7 +19,7 @@ // d2x checker pod-type import std; -import d2x.harness; +import d2x; struct A { diff --git a/en/cpp11/tests/17-pod-type/1.cpp b/src/en/cpp11/tests/17-pod-type/1.cpp similarity index 95% rename from en/cpp11/tests/17-pod-type/1.cpp rename to src/en/cpp11/tests/17-pod-type/1.cpp index 004386b..2f03b5a 100644 --- a/en/cpp11/tests/17-pod-type/1.cpp +++ b/src/en/cpp11/tests/17-pod-type/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/17-pod-type/1.cpp +// file: src/en/cpp11/tests/17-pod-type/1.cpp // // Exercise: cpp11 | 17 - POD Type | Byte-wise copying of a POD struct // @@ -19,7 +19,7 @@ // d2x checker pod-type import std; -import d2x.harness; +import d2x; struct Packet { diff --git a/en/cpp11/tests/17-pod-type/2.cpp b/src/en/cpp11/tests/17-pod-type/2.cpp similarity index 97% rename from en/cpp11/tests/17-pod-type/2.cpp rename to src/en/cpp11/tests/17-pod-type/2.cpp index 294b83c..7c39ace 100644 --- a/en/cpp11/tests/17-pod-type/2.cpp +++ b/src/en/cpp11/tests/17-pod-type/2.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp11/tests/17-pod-type/2.cpp +// file: src/en/cpp11/tests/17-pod-type/2.cpp // // Exercise: cpp11 | 17 - POD Type | Adapting a C++ message to a C POD header // @@ -19,7 +19,7 @@ // d2x checker pod-type import std; -import d2x.harness; +import d2x; // C-style message header, POD-only, used for cross-language / cross-module transfer. diff --git a/en/cpp14/mcpp.toml b/src/en/cpp14/mcpp.toml similarity index 52% rename from en/cpp14/mcpp.toml rename to src/en/cpp14/mcpp.toml index ff4f10e..6ef0957 100644 --- a/en/cpp14/mcpp.toml +++ b/src/en/cpp14/mcpp.toml @@ -1,10 +1,10 @@ # cpp14 (en) 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: -# mcpp test -p en/cpp14 全部练习(进度表) -# mcpp test -p en/cpp14 <子串> 只跑匹配的练习 +# mcpp test -p src/en/cpp14 全部练习(进度表) +# mcpp test -p src/en/cpp14 <子串> 只跑匹配的练习 [package] name = "en-cpp14" version = "0.1.0" standard = "c++23" [dependencies] -harness = { path = "../../harness" } +d2x = { path = "../../../d2x" } diff --git a/en/cpp14/tests/00-generic-lambdas/0.cpp b/src/en/cpp14/tests/00-generic-lambdas/0.cpp similarity index 96% rename from en/cpp14/tests/00-generic-lambdas/0.cpp rename to src/en/cpp14/tests/00-generic-lambdas/0.cpp index a740c88..d21832a 100644 --- a/en/cpp14/tests/00-generic-lambdas/0.cpp +++ b/src/en/cpp14/tests/00-generic-lambdas/0.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp14/tests/00-generic-lambdas/0.cpp +// file: src/en/cpp14/tests/00-generic-lambdas/0.cpp // // Exercise: cpp14 | 00 - generic lambdas | basic generic lambda // @@ -21,7 +21,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/cpp14/tests/00-generic-lambdas/1.cpp b/src/en/cpp14/tests/00-generic-lambdas/1.cpp similarity index 96% rename from en/cpp14/tests/00-generic-lambdas/1.cpp rename to src/en/cpp14/tests/00-generic-lambdas/1.cpp index 88cf159..5405b62 100644 --- a/en/cpp14/tests/00-generic-lambdas/1.cpp +++ b/src/en/cpp14/tests/00-generic-lambdas/1.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: en/cpp14/tests/00-generic-lambdas/1.cpp +// file: src/en/cpp14/tests/00-generic-lambdas/1.cpp // // Exercise: cpp14 | 00 - generic lambdas | generic lambda with STL algorithms // @@ -21,7 +21,7 @@ // import std; -import d2x.harness; +import d2x; int main() { diff --git a/en/intro/mcpp.toml b/src/en/intro/mcpp.toml similarity index 52% rename from en/intro/mcpp.toml rename to src/en/intro/mcpp.toml index 324bd6b..7353556 100644 --- a/en/intro/mcpp.toml +++ b/src/en/intro/mcpp.toml @@ -1,10 +1,10 @@ # intro (en) 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: -# mcpp test -p en/intro 全部练习(进度表) -# mcpp test -p en/intro <子串> 只跑匹配的练习 +# mcpp test -p src/en/intro 全部练习(进度表) +# mcpp test -p src/en/intro <子串> 只跑匹配的练习 [package] name = "en-intro" version = "0.1.0" standard = "c++23" [dependencies] -harness = { path = "../../harness" } +d2x = { path = "../../../d2x" } diff --git a/en/intro/tests/hello-mcpp.cpp b/src/en/intro/tests/hello-mcpp.cpp similarity index 75% rename from en/intro/tests/hello-mcpp.cpp rename to src/en/intro/tests/hello-mcpp.cpp index 0e48f6d..9f2426f 100644 --- a/en/intro/tests/hello-mcpp.cpp +++ b/src/en/intro/tests/hello-mcpp.cpp @@ -1,6 +1,6 @@ // mcpp-standard: https://github.com/Sunrisepeak/mcpp-standard // license: Apache-2.0 -// file: en/intro/tests/hello-mcpp.cpp +// file: src/en/intro/tests/hello-mcpp.cpp // // Exercise: Automated Code Practice Usage Tutorial // @@ -8,7 +8,7 @@ // This is an "exercises are tests" automated practice project. Two ways to play: // // d2x checker guided mode: auto-detects your edits, passing advances you -// mcpp test -p en/intro native mode: plain mcpp — the test report IS your progress +// mcpp test -p src/en/intro native mode: plain mcpp — the test report IS your progress // // Fix the code based on console error messages. There are only three conventions: // @@ -30,7 +30,7 @@ // import std; -import d2x.harness; +import d2x; // You can observe "real-time" changes in the console when modifying code @@ -57,23 +57,23 @@ int main() { # [[ Console Output Interpretation ]] -🌏Progress: [>----------] 0/10 -->> Shows current exercise progress +🌏Progress: [>----------] 0/52 -->> Shows current exercise progress -[Target: 00-0-hello-mcpp] - normal -->> Current exercise name +[Exercise: hello-mcpp] -->> Current exercise name -❌ Error: Compilation/Running failed for en/intro/tests/hello-mcpp.cpp -->> Shows detection status +❌ Error: Compilation/Running failed for src/en/intro/tests/hello-mcpp.cpp -->> Shows detection status The code exist some error! ----------C-Output--------- - Compiler output information -[HONLY LOGW]: main: en/intro/tests/hello-mcpp.cpp:24 - ❌ | a == 1.1 (1 == 1.100000) -->> Error prompt and location (line 24) -[HONLY LOGW]: main: en/intro/tests/hello-mcpp.cpp:26 - 🥳 Delete the d2x::wait() to continue... +---------Output--------- - Compile/run output information +❌ | a == 1.1 (1 == 1.1) --> src/en/intro/tests/hello-mcpp.cpp:46 -->> Error prompt and location (line 46) +🚧 | Delete the d2x::wait() to continue --> src/en/intro/tests/hello-mcpp.cpp:52 AI-Tips-Config: https://xlings.d2learn.org/en/documents/d2x/intro.html -->> AI tips (requires configuring large model key, optional) ---------E-Files--------- -en/intro/tests/hello-mcpp.cpp -->> Current file being checked +src/en/intro/tests/hello-mcpp.cpp -->> Current file being checked ------------------------- Homepage: https://github.com/openxlings/xlings diff --git a/intro/mcpp.toml b/src/intro/mcpp.toml similarity index 52% rename from intro/mcpp.toml rename to src/intro/mcpp.toml index 8065e72..3772c86 100644 --- a/intro/mcpp.toml +++ b/src/intro/mcpp.toml @@ -1,10 +1,10 @@ # intro 练习工程。练习 = tests/<章节>/<序号>.cpp,由 mcpp test 直接驱动: -# mcpp test -p intro 全部练习(进度表) -# mcpp test -p intro <子串> 只跑匹配的练习 +# mcpp test -p src/intro 全部练习(进度表) +# mcpp test -p src/intro <子串> 只跑匹配的练习 [package] name = "intro" version = "0.1.0" standard = "c++23" [dependencies] -harness = { path = "../harness" } +d2x = { path = "../../d2x" } diff --git a/intro/tests/hello-mcpp.cpp b/src/intro/tests/hello-mcpp.cpp similarity index 69% rename from intro/tests/hello-mcpp.cpp rename to src/intro/tests/hello-mcpp.cpp index a3c5e82..87e4493 100644 --- a/intro/tests/hello-mcpp.cpp +++ b/src/intro/tests/hello-mcpp.cpp @@ -1,6 +1,6 @@ // d2mcpp: https://github.com/mcpp-community/d2mcpp // license: Apache-2.0 -// file: intro/tests/hello-mcpp.cpp +// file: src/intro/tests/hello-mcpp.cpp // // Exercise/练习: 自动化代码练习使用教学 // @@ -8,7 +8,7 @@ // 这是一个「练习即测试」的自动化代码练习项目。两种玩法任选: // // d2x checker 闯关模式: 自动检测、通过即进入下一题 -// mcpp test -p intro 原生模式: 直接用 mcpp 跑, 测试结果就是进度表 +// mcpp test -p src/intro 原生模式: 直接用 mcpp 跑, 测试结果就是进度表 // // 你需要根据控制台的报错和提示信息修改代码。约定只有三个: // @@ -28,23 +28,23 @@ // import std; -import d2x.harness; +import d2x; // 修改代码时可以观察到控制台"实时"的变化 int main() { - std::cout << "hello, mcpp!" << std:endl; // 0.修复这个编译错误 + std::cout << "hello, mcpp!" << std::endl; // 0.修复这个编译错误 - int a = 1.1; // 1.修复这个运行时错误, 修改int为double, 通过检查 + double a = 1.1; // 1.修复这个运行时错误, 修改int为double, 通过检查 d2x::check_eq(a, 1.1, "a == 1.1"); // 2.运行时检查点, 需要修复代码通过所有检查点(不能直接删除检查点代码) - D2X_YOUR_ANSWER b = a; // 3.修复这个编译错误, 给b一个合适的类型 + int b = a; d2x::check_eq(b, 1, "b == 1"); // 4.运行时检查点2 - d2x::wait(); // 5.删除或注释掉这一行, 进入下一个练习(项目正式代码练习) + //d2x::wait(); // 5.删除或注释掉这一行, 进入下一个练习(项目正式代码练习) return 0; } @@ -59,16 +59,16 @@ int main() { [Exercise: hello-mcpp] -->> 当前的练习名 -❌ Error: Compilation/Running failed for intro/tests/hello-mcpp.cpp -->> 显示检测状态 +❌ Error: Compilation/Running failed for src/intro/tests/hello-mcpp.cpp -->> 显示检测状态 ---------Output--------- - 编译/运行输出信息 -[HONLY LOGW]: intro/tests/hello-mcpp.cpp:41 - ❌ | a == 1.1 (1 == 1.1) -->> 错误提示及位置(41行) -[HONLY LOGW]: intro/tests/hello-mcpp.cpp:47 - 🥳 Delete the d2x::wait() to continue... +❌ | a == 1.1 (1 == 1.1) --> src/intro/tests/hello-mcpp.cpp:41 -->> 错误提示及位置(41行) +🚧 | Delete the d2x::wait() to continue --> src/intro/tests/hello-mcpp.cpp:47 AI-Tips-Config: https://xlings.d2learn.org/documents/d2x/intro.html -->> AI提示(需要配置大模型的key, 可不使用) ---------E-Files--------- -intro/tests/hello-mcpp.cpp -->> 当前检测的文件 +src/intro/tests/hello-mcpp.cpp -->> 当前检测的文件 ------------------------- Homepage: https://github.com/openxlings/xlings From 543a13a7bcf904573c94f5bc01f6ad6e437741e9 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 23:34:10 +0800 Subject: [PATCH 19/25] =?UTF-8?q?docs:=20=E6=9C=AF=E8=AF=AD=E7=BB=9F?= =?UTF-8?q?=E4=B8=80(=E5=AD=A6=E5=91=98=E2=86=92=E5=AD=A6=E4=B9=A0?= =?UTF-8?q?=E8=80=85)=20+=20=E5=88=A4=E5=AE=9A=E9=A1=BA=E5=BA=8F=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E4=B8=8E=E5=AE=9E=E7=8E=B0=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/docs/2026-07-20-mcpp-provider-reference.md | 14 +++++++------- .../2026-07-23-exercises-as-tests-reference.md | 4 ++-- .github/workflows/dslings-ref-ci.yml | 4 ++-- d2x/buildtools/mcpp/src/discovery.cppm | 2 +- d2x/buildtools/mcpp/src/emit.cppm | 2 +- d2x/buildtools/mcpp/src/main.cpp | 4 ++-- d2x/buildtools/mcpp/src/runner.cppm | 7 ++++--- d2x/src/d2x.cppm | 12 ++++++------ 8 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.agents/docs/2026-07-20-mcpp-provider-reference.md b/.agents/docs/2026-07-20-mcpp-provider-reference.md index b6820cb..fcb8cb4 100644 --- a/.agents/docs/2026-07-20-mcpp-provider-reference.md +++ b/.agents/docs/2026-07-20-mcpp-provider-reference.md @@ -40,7 +40,7 @@ dslings/harness/ 练习脚手架库,standard = "c++23" .d2x/build//mcpp.toml 该标准下全部 target —— 供 clangd .d2x/build/_current/mcpp.toml 只含当前一题 —— 供 checker .d2x/build/_current/result.ndjson 判定用侧信道 -.d2x/state.json 学员进度(d2x 写) +.d2x/state.json 学习者进度(d2x 写) ``` --- @@ -132,9 +132,9 @@ member 含全部 3 题(后两题未做完)→ mcpp build -p → exit=1 ← - `main` 用 `../../../` 逃逸出包根,**练习源文件原地不动** - 脚手架走 `[dependencies] path` 依赖,而非把仓库根塞进 include 搜索路径(后者会让练习能 `#include` 仓库里任何文件) -- `-fmacro-prefix-map=/=` 让 `__FILE__` 相对仓库根,学员看到的报错不再顶着一长串 `/home/...` +- `-fmacro-prefix-map=/=` 让 `__FILE__` 相对仓库根,学习者看到的报错不再顶着一长串 `/home/...` - 所有清单**内容比对后才落盘**,不推进 mtime,保住 mcpp 的快速路径 -- `check` 会先 `write_full` 再 `write_current`——全新仓库上学员直接跑 `d2x checker` 时根清单还不存在,否则 mcpp 报退出码 2 +- `check` 会先 `write_full` 再 `write_current`——全新仓库上学习者直接跑 `d2x checker` 时根清单还不存在,否则 mcpp 报退出码 2 --- @@ -154,7 +154,7 @@ member 含全部 3 题(后两题未做完)→ mcpp build -p → exit=1 ← **逐条追加而非退出时统一写** —— 练习段错误时,崩溃之前的断言结果照样保留。 -`D2X_RESULT_FILE` 环境变量未设置时 harness 只打印不写文件:**学员直接跑二进制零摩擦**。 +`D2X_RESULT_FILE` 环境变量未设置时 harness 只打印不写文件:**学习者直接跑二进制零摩擦**。 ### 判定顺序 @@ -165,11 +165,11 @@ member 含全部 3 题(后两题未做完)→ mcpp build -p → exit=1 ← | 无失败、有 `wait` | `blocked` | | **文件不存在**(练习没用 harness) | 退回「编译通过 + 退出 0 = `pass`」 | -最后一条让 **harness 自动变成可选的**,不需要额外机制:纯观察型练习(现有 18 个只用 `D2X_WAIT`)可以写成零依赖的纯 C++ 文件,学员能原样拷进 Compiler Explorer。 +最后一条让 **harness 自动变成可选的**,不需要额外机制:纯观察型练习(现有 18 个只用 `D2X_WAIT`)可以写成零依赖的纯 C++ 文件,学习者能原样拷进 Compiler Explorer。 ### 路径:展示 vs 定位 -侧信道里的 `file` 因 `-fmacro-prefix-map` 是相对路径(学员看着舒服),但协议要求绝对路径(d2x 靠它开编辑器、监听变更)。**在协议边界上还原成绝对。** +侧信道里的 `file` 因 `-fmacro-prefix-map` 是相对路径(学习者看着舒服),但协议要求绝对路径(d2x 靠它开编辑器、监听变更)。**在协议边界上还原成绝对。** --- @@ -201,7 +201,7 @@ member 含全部 3 题(后两题未做完)→ mcpp build -p → exit=1 ← 断言两件事,缺一不可:**每个练习未完成时不通过**、**每个参考答案放进去后通过**。等价于 rustlings 的 `cargo dev check --require-solutions`。 -走的是 `d2x checker` 内部同一条 Provider 路径,所以绿灯等价于学员本地能跑通,而非另一条平行路径。 +走的是 `d2x checker` 内部同一条 Provider 路径,所以绿灯等价于学习者本地能跑通,而非另一条平行路径。 **两道防线:** diff --git a/.agents/docs/2026-07-23-exercises-as-tests-reference.md b/.agents/docs/2026-07-23-exercises-as-tests-reference.md index f1a6ba6..b044906 100644 --- a/.agents/docs/2026-07-23-exercises-as-tests-reference.md +++ b/.agents/docs/2026-07-23-exercises-as-tests-reference.md @@ -23,7 +23,7 @@ d2x/buildtools/mcpp/ Provider(C++26) - **零生成物**:`.d2x/build/` 及三套清单生成已删除;`.d2x/` 下只剩学习进度 `state.json`(d2x 写)与判定侧信道 `result.ndjson`(harness 写)。 -- id 推导与旧布局**逐字节一致**(`cpp11-00-auto-and-decltype-0`),学员进度不丢。 +- id 推导与旧布局**逐字节一致**(`cpp11-00-auto-and-decltype-0`),学习者进度不丢。 ## 2. 双入口 @@ -41,7 +41,7 @@ Provider 只是把 mcpp 的 JSON 记录 × 侧信道合并成协议 verdict。 | 设施 | 说明 | |---|---| -| `d2x::check(cond, "原文")` / `check_eq(a, b, "a == b")` | 第三参是给学员看的表达式原文(迁移脚本自动从宏参数生成,教学输出零漂移);`std::formattable` 探测转印值(SFINAE 安全,requires 里直接写 std::format 会踩未特化 formatter 的 static_assert) | +| `d2x::check(cond, "原文")` / `check_eq(a, b, "a == b")` | 第三参是给学习者看的表达式原文(迁移脚本自动从宏参数生成,教学输出零漂移);`std::formattable` 探测转印值(SFINAE 安全,requires 里直接写 std::format 会踩未特化 formatter 的 static_assert) | | `D2X_YOUR_ANSWER` | **纯约定,无定义**。裸标识符的编译报错正好指着要填的位置 | | `d2x::wait()` | 路障;记录后继续执行(后续断言照跑) | | `d2x::dont_delete_this(expr)` | 恒等透传(旧 D2X_DONT_DELETE_THIS) | diff --git a/.github/workflows/dslings-ref-ci.yml b/.github/workflows/dslings-ref-ci.yml index 1294b4b..9ebafee 100644 --- a/.github/workflows/dslings-ref-ci.yml +++ b/.github/workflows/dslings-ref-ci.yml @@ -1,11 +1,11 @@ name: Validate exercises and reference solutions # 断言两件事,缺一不可: -# 1. 每个练习「未完成时」不通过 —— 否则学员会被直接跳过,练习形同虚设 +# 1. 每个练习「未完成时」不通过 —— 否则学习者会被直接跳过,练习形同虚设 # 2. 每个参考答案「放进去后」通过 —— 否则参考答案本身是错的 # # 练习即测试:e2e 走 `mcpp test`,与 `d2x checker` 内部的 Provider 是同一条 -# 链路,所以 CI 绿灯等价于学员本地能跑通,而不是另一条平行的构建路径。 +# 链路,所以 CI 绿灯等价于学习者本地能跑通,而不是另一条平行的构建路径。 # # ⚠️ 依赖 mcpp「测试能力批次」(逐测试隔离 / 过滤 / --message-format json / # tests 吃 [build].flags,本地分支 feat/test-isolation-json)。在 mcpp 发布 diff --git a/d2x/buildtools/mcpp/src/discovery.cppm b/d2x/buildtools/mcpp/src/discovery.cppm index 527d1c1..f1cf454 100644 --- a/d2x/buildtools/mcpp/src/discovery.cppm +++ b/d2x/buildtools/mcpp/src/discovery.cppm @@ -9,7 +9,7 @@ // src/cpp11/tests/00-auto-and-decltype/0.cpp → id: cpp11-00-auto-and-decltype-0 // src/en/cpp11/tests/... lang=en 时启用,与 zh 互斥 // -// 每个 / 是真实 mcpp 工程,练习就是它的 tests/ —— 学员可以绕过 d2x +// 每个 / 是真实 mcpp 工程,练习就是它的 tests/ —— 学习者可以绕过 d2x // 直接 `mcpp test -p cpp11` 看进度表,Provider 走的是同一条路。 module; diff --git a/d2x/buildtools/mcpp/src/emit.cppm b/d2x/buildtools/mcpp/src/emit.cppm index 4b516b1..4615ca5 100644 --- a/d2x/buildtools/mcpp/src/emit.cppm +++ b/d2x/buildtools/mcpp/src/emit.cppm @@ -90,7 +90,7 @@ export void verdict(std::string_view outcome, std::string_view stage_name, int e } // 一条失败的断言 → 一个 Diagnostic。d2x 的前端据此做行内高亮和跳转, -// 学员不用在几十行输出里找是哪一条没过。 +// 学习者不用在几十行输出里找是哪一条没过。 export std::string diagnostic(std::string_view file, int line_no, std::string_view expr, std::string_view expected, std::string_view actual) { diff --git a/d2x/buildtools/mcpp/src/main.cpp b/d2x/buildtools/mcpp/src/main.cpp index 39df051..8dc7e19 100644 --- a/d2x/buildtools/mcpp/src/main.cpp +++ b/d2x/buildtools/mcpp/src/main.cpp @@ -9,7 +9,7 @@ // // 练习即测试:check 就是一次 `mcpp test -p --message-format // json`,编译/退出码事实来自 mcpp 的 JSON 记录,运行期语义(断言/路障) -// 来自 harness 的侧信道 v2 —— 学员绕过 d2x 直接 `mcpp test -p cpp11` +// 来自 harness 的侧信道 v2 —— 学习者绕过 d2x 直接 `mcpp test -p cpp11` // 走的是完全相同的链路,不存在平行路径。 // // 用法(由 .d2x.json 的 buildtools 字段驱动,从仓库根执行,无需 cd): @@ -79,7 +79,7 @@ int cmd_check(const fs::path& root, std::string_view id) { return 1; } - // 侧信道放在 .d2x/ 下:学员进度旁边,不污染工程目录。 + // 侧信道放在 .d2x/ 下:学习者进度旁边,不污染工程目录。 auto result_file = root / ".d2x" / "result.ndjson"; // 从仓库根 spawn(Provider 由 d2x 在仓库根启动,mcpp 自己解析 workspace) diff --git a/d2x/buildtools/mcpp/src/runner.cppm b/d2x/buildtools/mcpp/src/runner.cppm index 511f231..b7d19ec 100644 --- a/d2x/buildtools/mcpp/src/runner.cppm +++ b/d2x/buildtools/mcpp/src/runner.cppm @@ -185,14 +185,15 @@ export struct RunReport { std::vector failures; }; -// 判定顺序: +// 判定顺序(与代码一致,注意 wait 先于退出码——d2x 库里 wait 会把退出码 +// 顶成 1,若先看退出码,blocked 会全部误判成 fail): // 有 ok:false → Fail,每条失败都能转成一个 Diagnostic -// 无失败但退出码非 0 → Fail(纯崩溃 / 练习自己 return 非 0) // 无失败、有 wait → Blocked(答案已对,只差拆路障) +// 无失败但退出码非 0 → Fail(纯崩溃 / 练习自己 return 非 0) // 侧信道文件不存在 → 退回「退出码为 0 即通过」 // // 最后一条让 harness 自动变成可选的:纯观察型练习可以是零依赖的 -// 纯 C++ 文件,学员能原样拷进 Compiler Explorer。 +// 纯 C++ 文件,学习者能原样拷进 Compiler Explorer。 export RunReport judge_run(int exit_code, const fs::path& result_file) { RunReport report; diff --git a/d2x/src/d2x.cppm b/d2x/src/d2x.cppm index dabe30e..eaf413a 100644 --- a/d2x/src/d2x.cppm +++ b/d2x/src/d2x.cppm @@ -20,9 +20,9 @@ import std; // `mcpp test` 不需要懂任何 d2x 概念就能显示对错。 // 侧信道 v2 —— D2X_RESULT_FILE 指定的 NDJSON 文件,Provider 读它 // 把「为什么失败」变成结构化诊断、把 wait 区分成 blocked。 -// 未设置时不写文件:学员直接跑二进制零摩擦。 +// 未设置时不写文件:学习者直接跑二进制零摩擦。 // -// 可见输出的设计(学员每天面对的三行): +// 可见输出的设计(学习者每天面对的三行): // ✅ | ( == ) 绿 // ❌ | ( == ) --> : 红 // 🚧 | Delete the d2x::wait() to continue --> : 黄 @@ -75,7 +75,7 @@ inline std::string escape(std::string_view s) { // 进程异常终止不丢已写入的行;无需依赖静态析构顺序。 inline void emit(const std::string& json_line) { const char* path = std::getenv("D2X_RESULT_FILE"); - if (!path || !*path) return; // 学员直接运行:什么都不做 + if (!path || !*path) return; // 学习者直接运行:什么都不做 if (std::FILE* f = std::fopen(path, "a")) { std::fputs(json_line.c_str(), f); @@ -113,7 +113,7 @@ inline std::string show_impl(const auto& v) { else return {}; } -// 展示用路径:能剥掉当前工作目录前缀就剥(学员从仓库根跑时看到 +// 展示用路径:能剥掉当前工作目录前缀就剥(学习者从仓库根跑时看到 // src/cpp11/tests/... 而不是一长串 /home/...)。剥不掉就原样。 inline std::string show_path(const char* file) { if (!file) return {}; @@ -136,7 +136,7 @@ constexpr std::string_view kReset = "\033[0m"; export namespace d2x { -// 检查点。`what` 是给学员看的语义标签(通常是表达式原文——c++23 没有 +// 检查点。`what` 是给学习者看的语义标签(通常是表达式原文——c++23 没有 // 反射拿不到 #expr,由撰稿或迁移脚本填写)。 inline bool check(bool ok, std::string_view what = {}, std::source_location loc = std::source_location::current()) { @@ -184,7 +184,7 @@ constexpr decltype(auto) dont_delete_this(T&& x) { return std::forward(x); } -// 显式路障:学员读完说明、删掉这一行才算真正完成这一题。 +// 显式路障:学习者读完说明、删掉这一行才算真正完成这一题。 // 记录后照常返回(不中断程序)——后续检查点还要跑;退出码由 exit_hook // 收口,Provider 再根据侧信道把「只剩 wait」区分成 blocked。 inline void wait(std::source_location loc = std::source_location::current()) { From 9a74dbd2dfa28e00c3abff7e63a423c54abf90e9 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 23 Jul 2026 23:36:07 +0800 Subject: [PATCH 20/25] =?UTF-8?q?fix:=20hello-mcpp=20=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E6=9C=AA=E5=AE=8C=E6=88=90=E6=80=81(=E4=BD=93=E9=AA=8C?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E9=80=9A=E5=85=B3=E6=94=B9=E5=8A=A8=E8=A2=AB?= =?UTF-8?q?=E8=AF=AF=E6=8F=90=E4=BA=A4,pristine=20=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E6=8A=93=E5=87=BA)=20+=20=E5=8F=82=E8=80=83=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=96=B0=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2026-07-23-exercises-as-tests-reference.md | 42 ++++++++++++------- src/intro/tests/hello-mcpp.cpp | 8 ++-- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/.agents/docs/2026-07-23-exercises-as-tests-reference.md b/.agents/docs/2026-07-23-exercises-as-tests-reference.md index b044906..fcf81aa 100644 --- a/.agents/docs/2026-07-23-exercises-as-tests-reference.md +++ b/.agents/docs/2026-07-23-exercises-as-tests-reference.md @@ -8,17 +8,17 @@ --- -## 1. 布局 +## 1. 布局(第二轮重排后:库入驻 d2x/、课程收进 src/) ``` -mcpp.toml workspace = [harness, intro, cpp11, cpp14, en/*, d2x/buildtools/mcpp] -harness/ 练习脚手架包(纯模块 d2x.harness,零宏) -intro/tests/hello-mcpp.cpp -cpp11/tests//.cpp 49 题 -cpp14/tests/00-generic-lambdas/{0,1}.cpp -en/{intro,cpp11,cpp14}/ en 镜像工程(lang=en 时启用,与 zh 互斥) +mcpp.toml workspace = [d2x, src/*, src/en/*, d2x/buildtools/mcpp] +d2x/ d2x 练习库(纯模块 `import d2x;`,零宏) +d2x/buildtools/mcpp/ Provider(C++26) +src/intro/tests/hello-mcpp.cpp +src/cpp11/tests//.cpp 49 题 +src/cpp14/tests/00-generic-lambdas/{0,1}.cpp +src/en/{intro,cpp11,cpp14}/ en 镜像工程(lang=en 时启用,与 zh 互斥) solutions///.cpp 52 份(zh/en 共用,含 hello-mcpp) -d2x/buildtools/mcpp/ Provider(C++26) ``` - **零生成物**:`.d2x/build/` 及三套清单生成已删除;`.d2x/` 下只剩学习进度 @@ -28,26 +28,38 @@ d2x/buildtools/mcpp/ Provider(C++26) ## 2. 双入口 ``` -mcpp test -p cpp11 原生:进度表 -mcpp test -p cpp11 00-auto 原生:单题(子串过滤) -d2x checker 闯关:文件监听、通过自动推进(已实测:改对 - hello-mcpp 后自动进入 cpp11-00-auto-and-decltype-0) +mcpp test -p src/cpp11 原生:进度表(-p cpp11 的 basename 简写同样可用) +mcpp test -p src/cpp11 00-auto 原生:按子串过滤(匹配整章;更长的名字如 + 00-auto-and-decltype/3 即单题) +d2x checker 闯关:文件监听、通过自动推进(已实测:改对 + hello-mcpp 后自动进入 cpp11-00-auto-and-decltype-0) ``` 同一条链路:`d2x checker → Provider → mcpp test --message-format json → 判定`; Provider 只是把 mcpp 的 JSON 记录 × 侧信道合并成协议 verdict。 -## 3. harness(`import d2x.harness;`) +## 3. d2x 库(`import d2x;`,包与模块同名,住在 `d2x/`) | 设施 | 说明 | |---|---| | `d2x::check(cond, "原文")` / `check_eq(a, b, "a == b")` | 第三参是给学习者看的表达式原文(迁移脚本自动从宏参数生成,教学输出零漂移);`std::formattable` 探测转印值(SFINAE 安全,requires 里直接写 std::format 会踩未特化 formatter 的 static_assert) | | `D2X_YOUR_ANSWER` | **纯约定,无定义**。裸标识符的编译报错正好指着要填的位置 | -| `d2x::wait()` | 路障;记录后继续执行(后续断言照跑) | +| `d2x::wait()` | 路障;记录后继续执行(后续检查点照跑) | | `d2x::dont_delete_this(expr)` | 恒等透传(旧 D2X_DONT_DELETE_THIS) | -| 退出码 | 首次调用注册 atexit:有失败断言或未拆 wait → `_Exit(1)`。裸 `mcpp test` 因此天然能判对错 | +| 退出码 | 首次调用注册 atexit:有失败检查点或未拆 wait → `_Exit(1)`。裸 `mcpp test` 因此天然能判对错 | | 侧信道 v2 | `{"v":2,"kind":"assert","ok":…,"what":…,"expected":…,"actual":…,"file":…,"line":…}` / `{"v":2,"kind":"wait",…}`;`D2X_RESULT_FILE` 未设置时不写文件 | +**可见输出(去 HONLY 后的日志标识——emoji 即标识,无框架前缀)**: + +``` +✅ | a == a1 (1 == 1) 绿 +❌ | a == b (1 == 2) --> src/cpp11/tests/.../0.cpp:38 红 +🚧 | Delete the d2x::wait() to continue --> : 黄 +``` + +失败/路障行带定位;路径经 cwd 剥离尽量以仓库相对形式展示(定位用的 +绝对路径仍走侧信道,展示归展示、定位归定位)。 + **判定顺序(注意与设计稿 §5 的一处刻意调整)**:失败断言 → fail;`wait` → blocked; 退出码非 0 → fail;无侧信道 → 退出码 0 即 pass。`wait` 判定**先于**退出码—— 新 harness 里 wait 会把退出码顶成 1,按旧顺序 blocked 会全部误判成 fail。 diff --git a/src/intro/tests/hello-mcpp.cpp b/src/intro/tests/hello-mcpp.cpp index 87e4493..9bd4696 100644 --- a/src/intro/tests/hello-mcpp.cpp +++ b/src/intro/tests/hello-mcpp.cpp @@ -34,17 +34,17 @@ import d2x; int main() { - std::cout << "hello, mcpp!" << std::endl; // 0.修复这个编译错误 + std::cout << "hello, mcpp!" << std:endl; // 0.修复这个编译错误 - double a = 1.1; // 1.修复这个运行时错误, 修改int为double, 通过检查 + int a = 1.1; // 1.修复这个运行时错误, 修改int为double, 通过检查 d2x::check_eq(a, 1.1, "a == 1.1"); // 2.运行时检查点, 需要修复代码通过所有检查点(不能直接删除检查点代码) - int b = a; + D2X_YOUR_ANSWER b = a; // 3.修复这个编译错误, 给b一个合适的类型 d2x::check_eq(b, 1, "b == 1"); // 4.运行时检查点2 - //d2x::wait(); // 5.删除或注释掉这一行, 进入下一个练习(项目正式代码练习) + d2x::wait(); // 5.删除或注释掉这一行, 进入下一个练习(项目正式代码练习) return 0; } From 845faea9e3b1ebb19ef386a54ed4f20e825751cf Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 24 Jul 2026 00:23:48 +0800 Subject: [PATCH 21/25] =?UTF-8?q?refactor:=20Provider=20=E6=8B=8D=E5=B9=B3?= =?UTF-8?q?=E5=88=B0=20d2x/buildtools(=E5=8E=BB=E6=8E=89=E5=86=97=E4=BD=99?= =?UTF-8?q?=E7=9A=84=20mcpp=20=E7=9B=AE=E5=BD=95=E5=B1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/2026-07-23-exercises-as-tests-reference.md | 6 +++--- .agents/skills/d2mcpp-authoring/references/anatomy.md | 8 ++++---- .d2x.json | 2 +- .github/workflows/dslings-ref-ci.yml | 4 ++-- book/en/src/base/chapter_1.md | 4 ++-- book/src/base/chapter_1.md | 4 ++-- d2x/buildtools/{mcpp => }/mcpp.toml | 2 +- d2x/buildtools/{mcpp => }/src/discovery.cppm | 0 d2x/buildtools/{mcpp => }/src/emit.cppm | 0 d2x/buildtools/{mcpp => }/src/main.cpp | 2 +- d2x/buildtools/{mcpp => }/src/runner.cppm | 0 d2x/buildtools/{mcpp => }/tests/e2e.sh | 8 ++++---- d2x/src/d2x.cppm | 10 +++++----- mcpp.toml | 2 +- src/intro/tests/hello-mcpp.cpp | 2 +- 15 files changed, 27 insertions(+), 27 deletions(-) rename d2x/buildtools/{mcpp => }/mcpp.toml (83%) rename d2x/buildtools/{mcpp => }/src/discovery.cppm (100%) rename d2x/buildtools/{mcpp => }/src/emit.cppm (100%) rename d2x/buildtools/{mcpp => }/src/main.cpp (98%) rename d2x/buildtools/{mcpp => }/src/runner.cppm (100%) rename d2x/buildtools/{mcpp => }/tests/e2e.sh (93%) diff --git a/.agents/docs/2026-07-23-exercises-as-tests-reference.md b/.agents/docs/2026-07-23-exercises-as-tests-reference.md index fcf81aa..9719576 100644 --- a/.agents/docs/2026-07-23-exercises-as-tests-reference.md +++ b/.agents/docs/2026-07-23-exercises-as-tests-reference.md @@ -11,9 +11,9 @@ ## 1. 布局(第二轮重排后:库入驻 d2x/、课程收进 src/) ``` -mcpp.toml workspace = [d2x, src/*, src/en/*, d2x/buildtools/mcpp] +mcpp.toml workspace = [d2x, src/*, src/en/*, d2x/buildtools] d2x/ d2x 练习库(纯模块 `import d2x;`,零宏) -d2x/buildtools/mcpp/ Provider(C++26) +d2x/buildtools/ Provider(C++26) src/intro/tests/hello-mcpp.cpp src/cpp11/tests//.cpp 49 题 src/cpp14/tests/00-generic-lambdas/{0,1}.cpp @@ -79,7 +79,7 @@ Provider 只是把 mcpp 的 JSON 记录 × 侧信道合并成协议 verdict。 | 项 | 结果 | |---|---| -| e2e(`d2x/buildtools/mcpp/tests/e2e.sh all`) | zh 52/52、en 52/52 答案全绿;pristine 双向 0-pass;Provider 冒烟 ✓ | +| e2e(`d2x/buildtools/tests/e2e.sh all`) | zh 52/52、en 52/52 答案全绿;pristine 双向 0-pass;Provider 冒烟 ✓ | | d2x 端到端 | checker 显示 0/52 → 改对自动推进 → state.json 兼容旧 id ✓ | | d2x 仓库改动 | **零**(协议边界承诺兑现) | | hello-mcpp | 答案已补,进入 e2e,不再 SKIP | diff --git a/.agents/skills/d2mcpp-authoring/references/anatomy.md b/.agents/skills/d2mcpp-authoring/references/anatomy.md index 233e7fd..e40daa0 100644 --- a/.agents/skills/d2mcpp-authoring/references/anatomy.md +++ b/.agents/skills/d2mcpp-authoring/references/anatomy.md @@ -47,7 +47,7 @@ Features`). **Exercises are tests.** Each `/` is a real mcpp project and exercises are its `tests/`; dropping `/tests/NN-topic/K.cpp` into place is the whole job. `mcpp test -p src/` runs them natively (the report is the progress table), and -the d2x Provider (`d2x/buildtools/mcpp/`) derives the exercise id, order and +the d2x Provider (`d2x/buildtools/`) derives the exercise id, order and chapter from the same path — one chain, no generated manifests, nothing under `.d2x/` but learner progress. @@ -81,7 +81,7 @@ change compile flags. Drop the file at `solutions//NN-topic/K.cpp` (zh/en share one solution). CI swaps it over the exercise and asserts it passes; see -`d2x/buildtools/mcpp/tests/e2e.sh`. +`d2x/buildtools/tests/e2e.sh`. ## SUMMARY registration @@ -114,10 +114,10 @@ Run from the project root; report real output, do not assert success blind: mcpp test -p src/ NN-topic # check it through the Provider path (exactly what `d2x checker` consumes) -mcpp run -q -p d2x/buildtools/mcpp -- check cppNN-NN-topic-0 +mcpp run -q -p d2x/buildtools -- check cppNN-NN-topic-0 # or validate every exercise + solution at once (zh + en) -bash d2x/buildtools/mcpp/tests/e2e.sh all +bash d2x/buildtools/tests/e2e.sh all # drive the auto-checker against the exercise (expects the unsolved exercise to fail, # the solution to pass) — name omits the NN- prefix diff --git a/.d2x.json b/.d2x.json index 4ece6aa..aa6602b 100644 --- a/.d2x.json +++ b/.d2x.json @@ -1,6 +1,6 @@ { "version": "0.1.1", - "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", + "buildtools": "mcpp run -q -p d2x/buildtools --", "lang": "zh", "llm": { "api_key": "", diff --git a/.github/workflows/dslings-ref-ci.yml b/.github/workflows/dslings-ref-ci.yml index 9ebafee..0cc2c07 100644 --- a/.github/workflows/dslings-ref-ci.yml +++ b/.github/workflows/dslings-ref-ci.yml @@ -60,10 +60,10 @@ jobs: xlings install -y - name: Build d2x Provider - run: mcpp build -p d2x/buildtools/mcpp + run: mcpp build -p d2x/buildtools - name: Validate exercises and solutions (zh + en) - run: bash d2x/buildtools/mcpp/tests/e2e.sh all + run: bash d2x/buildtools/tests/e2e.sh all - name: Verify exercises ↔ solutions filename parity run: | diff --git a/book/en/src/base/chapter_1.md b/book/en/src/base/chapter_1.md index 0faefff..ec42037 100644 --- a/book/en/src/base/chapter_1.md +++ b/book/en/src/base/chapter_1.md @@ -163,7 +163,7 @@ Edit the `lang` attribute in the project configuration file `.d2x.json`. `zh` co ```bash { "version": "0.1.1", - "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", + "buildtools": "mcpp run -q -p d2x/buildtools --", "lang": "en", ... } @@ -177,7 +177,7 @@ If you prefer to use Neovim as your editor with LSP (clangd) support, you can co ```json { - "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", + "buildtools": "mcpp run -q -p d2x/buildtools --", "editor": "nvim", ... } diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index 58c617b..644d716 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -161,7 +161,7 @@ Homepage: https://github.com/openxlings/xlings ```bash { "version": "0.1.1", - "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", + "buildtools": "mcpp run -q -p d2x/buildtools --", "lang": "en", < -- 修改这里 ... } @@ -175,7 +175,7 @@ Homepage: https://github.com/openxlings/xlings ```json { - "buildtools": "mcpp run -q -p d2x/buildtools/mcpp --", + "buildtools": "mcpp run -q -p d2x/buildtools --", "editor": "nvim", ... } diff --git a/d2x/buildtools/mcpp/mcpp.toml b/d2x/buildtools/mcpp.toml similarity index 83% rename from d2x/buildtools/mcpp/mcpp.toml rename to d2x/buildtools/mcpp.toml index 5aeb0fb..d271559 100644 --- a/d2x/buildtools/mcpp/mcpp.toml +++ b/d2x/buildtools/mcpp.toml @@ -1,5 +1,5 @@ [package] -name = "d2x-buildtools-mcpp" +name = "d2x-buildtools" version = "0.1.0" description = "d2mcpp 的 d2x Provider:枚举练习、生成 mcpp 清单、构建/运行/判定" license = "Apache-2.0" diff --git a/d2x/buildtools/mcpp/src/discovery.cppm b/d2x/buildtools/src/discovery.cppm similarity index 100% rename from d2x/buildtools/mcpp/src/discovery.cppm rename to d2x/buildtools/src/discovery.cppm diff --git a/d2x/buildtools/mcpp/src/emit.cppm b/d2x/buildtools/src/emit.cppm similarity index 100% rename from d2x/buildtools/mcpp/src/emit.cppm rename to d2x/buildtools/src/emit.cppm diff --git a/d2x/buildtools/mcpp/src/main.cpp b/d2x/buildtools/src/main.cpp similarity index 98% rename from d2x/buildtools/mcpp/src/main.cpp rename to d2x/buildtools/src/main.cpp index 8dc7e19..6b9e229 100644 --- a/d2x/buildtools/mcpp/src/main.cpp +++ b/d2x/buildtools/src/main.cpp @@ -13,7 +13,7 @@ // 走的是完全相同的链路,不存在平行路径。 // // 用法(由 .d2x.json 的 buildtools 字段驱动,从仓库根执行,无需 cd): -// mcpp run -q -p d2x/buildtools/mcpp -- check cpp11-00-auto-and-decltype-0 +// mcpp run -q -p d2x/buildtools -- check cpp11-00-auto-and-decltype-0 import std; diff --git a/d2x/buildtools/mcpp/src/runner.cppm b/d2x/buildtools/src/runner.cppm similarity index 100% rename from d2x/buildtools/mcpp/src/runner.cppm rename to d2x/buildtools/src/runner.cppm diff --git a/d2x/buildtools/mcpp/tests/e2e.sh b/d2x/buildtools/tests/e2e.sh similarity index 93% rename from d2x/buildtools/mcpp/tests/e2e.sh rename to d2x/buildtools/tests/e2e.sh index fda1625..8464a3e 100755 --- a/d2x/buildtools/mcpp/tests/e2e.sh +++ b/d2x/buildtools/tests/e2e.sh @@ -11,11 +11,11 @@ # 练习上再用 git 还原,未提交的改动会被吃掉 # - 防空转: overlay 后 passed 总数为 0 时直接失败,绝不静默绿灯 # -# 用法: bash d2x/buildtools/mcpp/tests/e2e.sh [zh|en|all] (默认 all) +# 用法: bash d2x/buildtools/tests/e2e.sh [zh|en|all] (默认 all) # 依赖: PATH 里的 mcpp 需支持 mcpp test 逐测试隔离/过滤/--message-format json set -uo pipefail -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)" +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" cd "$ROOT" MODE="${1:-all}" @@ -95,13 +95,13 @@ run_lang() { provider_smoke() { # Provider 协议冒烟:枚举数量、check 的关键事件 local n - n=$(mcpp run -q -p d2x/buildtools/mcpp -- exercises | grep -c '"event":"exercise"') + n=$(mcpp run -q -p d2x/buildtools -- exercises | grep -c '"event":"exercise"') if [[ "$n" -lt 52 ]]; then echo "E2E(provider) FAIL: 枚举到 $n 个练习(预期 >= 52)" return 1 fi local out - out=$(mcpp run -q -p d2x/buildtools/mcpp -- check hello-mcpp) + out=$(mcpp run -q -p d2x/buildtools -- check hello-mcpp) echo "$out" | grep -q '"event":"stage","name":"compile"' \ && echo "$out" | grep -q '"event":"verdict"' \ && echo "$out" | grep -q '"outcome":"fail"' \ diff --git a/d2x/src/d2x.cppm b/d2x/src/d2x.cppm index eaf413a..0e1df5f 100644 --- a/d2x/src/d2x.cppm +++ b/d2x/src/d2x.cppm @@ -24,8 +24,8 @@ import std; // // 可见输出的设计(学习者每天面对的三行): // ✅ | ( == ) 绿 -// ❌ | ( == ) --> : 红 -// 🚧 | Delete the d2x::wait() to continue --> : 黄 +// ❌ | ( == ) -> : 红 +// 🚧 | Delete the d2x::wait() to continue -> : 黄 // 标识就是 emoji 本身,不带日志框架前缀;file 尽量以仓库相对路径展示 // (定位用的绝对路径走侧信道,展示归展示、定位归定位)。 @@ -147,7 +147,7 @@ inline bool check(bool ok, std::string_view what = {}, d2x::detail::report_assert(ok, label, "true", ok ? "true" : "false", loc.file_name(), static_cast(loc.line())); if (ok) std::print("{}✅ | {}{}\n", d2x::detail::kGreen, label, d2x::detail::kReset); - else std::print("{}❌ | {} --> {}:{}{}\n", d2x::detail::kRed, label, + else std::print("{}❌ | {} -> {}:{}{}\n", d2x::detail::kRed, label, d2x::detail::show_path(loc.file_name()), loc.line(), d2x::detail::kReset); std::fflush(stdout); @@ -170,7 +170,7 @@ inline bool check_eq(const A& a, const B& b, std::string_view what = {}, if (ok) std::print("{}✅ | {} ({} == {}){}\n", d2x::detail::kGreen, label, sa, sb, d2x::detail::kReset); - else std::print("{}❌ | {} ({} == {}) --> {}:{}{}\n", + else std::print("{}❌ | {} ({} == {}) -> {}:{}{}\n", d2x::detail::kRed, label, sa, sb, d2x::detail::show_path(loc.file_name()), loc.line(), d2x::detail::kReset); @@ -191,7 +191,7 @@ inline void wait(std::source_location loc = std::source_location::current()) { d2x::detail::ensure_hook(); ++d2x::detail::g_waits; d2x::detail::report_wait(loc.file_name(), static_cast(loc.line())); - std::print("{}🚧 | Delete the d2x::wait() to continue --> {}:{}{}\n", + std::print("{}🚧 | Delete the d2x::wait() to continue -> {}:{}{}\n", d2x::detail::kYellow, d2x::detail::show_path(loc.file_name()), loc.line(), d2x::detail::kReset); std::fflush(stdout); diff --git a/mcpp.toml b/mcpp.toml index d4cc89a..6c7e11a 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -6,5 +6,5 @@ members = [ "d2x", "src/intro", "src/cpp11", "src/cpp14", "src/en/intro", "src/en/cpp11", "src/en/cpp14", - "d2x/buildtools/mcpp", + "d2x/buildtools", ] diff --git a/src/intro/tests/hello-mcpp.cpp b/src/intro/tests/hello-mcpp.cpp index 9bd4696..a66abef 100644 --- a/src/intro/tests/hello-mcpp.cpp +++ b/src/intro/tests/hello-mcpp.cpp @@ -40,7 +40,7 @@ int main() { d2x::check_eq(a, 1.1, "a == 1.1"); // 2.运行时检查点, 需要修复代码通过所有检查点(不能直接删除检查点代码) - D2X_YOUR_ANSWER b = a; // 3.修复这个编译错误, 给b一个合适的类型 + int b = a; // 3.修复这个编译错误, 给b一个合适的类型 d2x::check_eq(b, 1, "b == 1"); // 4.运行时检查点2 From 524c7d0f135fb0d1e74c31ff969afebdf8d37ba2 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 24 Jul 2026 00:36:56 +0800 Subject: [PATCH 22/25] =?UTF-8?q?docs:=20=E6=B3=A8=E9=87=8A=E4=B8=8E?= =?UTF-8?q?=E6=95=99=E5=AD=A6=E6=96=87=E6=A1=88=E4=B8=93=E4=B8=9A=E5=8C=96?= =?UTF-8?q?(=E6=B6=88=E9=99=A4=E5=8F=A3=E8=AF=AD=E5=8C=96=E8=A1=A8?= =?UTF-8?q?=E8=BE=BE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/en/src/base/chapter_1.md | 2 +- book/src/base/chapter_1.md | 6 +++--- d2x/buildtools/src/discovery.cppm | 4 ++-- d2x/buildtools/src/runner.cppm | 4 ++-- d2x/buildtools/tests/e2e.sh | 6 +++--- d2x/src/d2x.cppm | 8 ++++---- mcpp.toml | 6 +++--- src/en/intro/tests/hello-mcpp.cpp | 2 +- src/intro/tests/hello-mcpp.cpp | 6 +++--- 9 files changed, 22 insertions(+), 22 deletions(-) diff --git a/book/en/src/base/chapter_1.md b/book/en/src/base/chapter_1.md index ec42037..78a11ae 100644 --- a/book/en/src/base/chapter_1.md +++ b/book/en/src/base/chapter_1.md @@ -86,7 +86,7 @@ After entering the automated code practice environment using `d2x checker`, the // Exercise: Automated Code Practice Tutorial // // Tips: -// This is an "exercises are tests" automated practice project. Two ways to play: +// This is an "exercises are tests" automated practice project. Two modes of use: // // d2x checker guided mode: auto-detects your edits, passing advances you // mcpp test -p src/en/intro native mode: plain mcpp — the test report IS your progress diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index 644d716..a9202d6 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -86,10 +86,10 @@ d2x update // Exercise/练习: 自动化代码练习使用教学 // // Tips/提示: -// 这是一个「练习即测试」的自动化代码练习项目。两种玩法任选: +// 这是一个「练习即测试」的自动化代码练习项目。两种使用方式任选: // -// d2x checker 闯关模式: 自动检测、通过即进入下一题 -// mcpp test -p src/intro 原生模式: 直接用 mcpp 跑, 测试结果就是进度表 +// d2x checker 引导模式: 自动检测,通过后进入下一题 +// mcpp test -p src/intro 原生模式: 直接使用 mcpp 运行, 测试报告即学习进度 // // 你需要根据控制台的报错和提示信息修改代码。约定只有三个: // diff --git a/d2x/buildtools/src/discovery.cppm b/d2x/buildtools/src/discovery.cppm index f1cf454..9f530fd 100644 --- a/d2x/buildtools/src/discovery.cppm +++ b/d2x/buildtools/src/discovery.cppm @@ -33,7 +33,7 @@ export struct Exercise { fs::path file; // 绝对路径 }; -// .d2x.json 里我们只关心 "lang"。写一个完整 JSON 解析器是杀鸡用牛刀, +// .d2x.json 中此处只需读取 "lang"。引入完整 JSON 解析器并无必要, // 但也不能假装解析——这里只做一件明确的事:取出 "lang" 的字符串值。 export std::string read_lang(const fs::path& root) { std::ifstream in(root / ".d2x.json"); @@ -85,7 +85,7 @@ std::string humanize(std::string_view topic) { // 练习 id 直接来自目录/文件名,而它有两个危险去向: // 1. d2x 把它拼进一条 shell 命令(` check `) // 2. 我们把 test 名拼进 `mcpp test ` 命令行 -// 带反引号、引号或换行的名字能在任一处越界。在源头挡住比在下游转义更可靠。 +// 带反引号、引号或换行的名字在任一处都可能越界。在发现阶段拒绝比在下游各自转义更可靠。 export bool valid_id(std::string_view id) { if (id.empty()) return false; return std::ranges::all_of(id, [](unsigned char c) { diff --git a/d2x/buildtools/src/runner.cppm b/d2x/buildtools/src/runner.cppm index b7d19ec..1af14f7 100644 --- a/d2x/buildtools/src/runner.cppm +++ b/d2x/buildtools/src/runner.cppm @@ -185,8 +185,8 @@ export struct RunReport { std::vector failures; }; -// 判定顺序(与代码一致,注意 wait 先于退出码——d2x 库里 wait 会把退出码 -// 顶成 1,若先看退出码,blocked 会全部误判成 fail): +// 判定顺序(与代码一致;wait 必须先于退出码判定——d2x 库在存在未拆除的 +// wait 时会将退出码置为 1,若先判退出码,blocked 会被整体误判为 fail): // 有 ok:false → Fail,每条失败都能转成一个 Diagnostic // 无失败、有 wait → Blocked(答案已对,只差拆路障) // 无失败但退出码非 0 → Fail(纯崩溃 / 练习自己 return 非 0) diff --git a/d2x/buildtools/tests/e2e.sh b/d2x/buildtools/tests/e2e.sh index 8464a3e..14f1c5a 100755 --- a/d2x/buildtools/tests/e2e.sh +++ b/d2x/buildtools/tests/e2e.sh @@ -6,9 +6,9 @@ # # 这是 rustlings `cargo dev check --require-solutions` 的等价物。 # -# 两道防线(各自都咬过人,见 .agents/docs 参考文档): +# 两道防线(均源自历史缺陷,背景见 .agents/docs 参考文档): # - 脏树检查: 练习/答案目录有未提交改动时拒绝运行 —— 脚本要把答案覆盖到 -# 练习上再用 git 还原,未提交的改动会被吃掉 +# 练习上再用 git 还原,未提交的改动会被覆盖丢失 # - 防空转: overlay 后 passed 总数为 0 时直接失败,绝不静默绿灯 # # 用法: bash d2x/buildtools/tests/e2e.sh [zh|en|all] (默认 all) @@ -31,7 +31,7 @@ run_lang() { local dirty dirty=$(git status --porcelain -- "${test_dirs[@]}" solutions/ | head -20) if [[ -n "$dirty" ]]; then - echo "E2E($label) 拒绝运行: 练习/答案目录有未提交改动(脚本的覆盖-还原会吃掉它们):" + echo "E2E($label) 拒绝运行: 练习/答案目录存在未提交改动(脚本的覆盖-还原流程会将其覆盖丢失):" echo "$dirty" return 1 fi diff --git a/d2x/src/d2x.cppm b/d2x/src/d2x.cppm index 0e1df5f..3483c3c 100644 --- a/d2x/src/d2x.cppm +++ b/d2x/src/d2x.cppm @@ -22,7 +22,7 @@ import std; // 把「为什么失败」变成结构化诊断、把 wait 区分成 blocked。 // 未设置时不写文件:学习者直接跑二进制零摩擦。 // -// 可见输出的设计(学习者每天面对的三行): +// 可见输出的设计(学习者直接看到的三类输出行): // ✅ | ( == ) 绿 // ❌ | ( == ) -> : 红 // 🚧 | Delete the d2x::wait() to continue -> : 黄 @@ -104,9 +104,9 @@ inline void report_wait(const char* file, int line) { emit(out); } -// std::formattable 是 SFINAE 安全的探测;直接在 requires 里写 -// std::format("{}", v) 会踩进未特化 std::formatter 的 static_assert -// 硬错误(scoped enum、自定义类型的练习实测中招)。 +// std::formattable 是 SFINAE 安全的探测;若直接在 requires 表达式里写 +// std::format("{}", v),会触发未特化 std::formatter 的 static_assert +// 硬错误(scoped enum、自定义类型的练习中已实际出现)。 inline std::string show_impl(const auto& v) { using T = std::remove_cvref_t; if constexpr (std::formattable) return std::format("{}", v); diff --git a/mcpp.toml b/mcpp.toml index 6c7e11a..0635035 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,6 +1,6 @@ -# d2mcpp 根 workspace。练习即测试:每个 C++ 标准是 src/ 下的一个真实工程, -# 练习是它的 tests/,mcpp test 直接就是进度表;d2x checker 驱动同一条链。 -# d2x/ 是练习库(import d2x)与 Provider 的家。 +# d2mcpp 根 workspace。练习即测试:每个 C++ 标准对应 src/ 下的一个 mcpp 工程, +# 练习即该工程的 tests/,mcpp test 的测试报告即学习进度;d2x checker 复用同一条链路。 +# d2x/ 目录包含练习库(import d2x)与 Provider(buildtools/)。 [workspace] members = [ "d2x", diff --git a/src/en/intro/tests/hello-mcpp.cpp b/src/en/intro/tests/hello-mcpp.cpp index 9f2426f..55aa1bb 100644 --- a/src/en/intro/tests/hello-mcpp.cpp +++ b/src/en/intro/tests/hello-mcpp.cpp @@ -5,7 +5,7 @@ // Exercise: Automated Code Practice Usage Tutorial // // Tips: -// This is an "exercises are tests" automated practice project. Two ways to play: +// This is an "exercises are tests" automated practice project. Two modes of use: // // d2x checker guided mode: auto-detects your edits, passing advances you // mcpp test -p src/en/intro native mode: plain mcpp — the test report IS your progress diff --git a/src/intro/tests/hello-mcpp.cpp b/src/intro/tests/hello-mcpp.cpp index a66abef..2bac4d5 100644 --- a/src/intro/tests/hello-mcpp.cpp +++ b/src/intro/tests/hello-mcpp.cpp @@ -5,10 +5,10 @@ // Exercise/练习: 自动化代码练习使用教学 // // Tips/提示: -// 这是一个「练习即测试」的自动化代码练习项目。两种玩法任选: +// 这是一个「练习即测试」的自动化代码练习项目。两种使用方式任选: // -// d2x checker 闯关模式: 自动检测、通过即进入下一题 -// mcpp test -p src/intro 原生模式: 直接用 mcpp 跑, 测试结果就是进度表 +// d2x checker 引导模式: 自动检测,通过后进入下一题 +// mcpp test -p src/intro 原生模式: 直接使用 mcpp 运行, 测试报告即学习进度 // // 你需要根据控制台的报错和提示信息修改代码。约定只有三个: // From 63dd8b22be44027758df7a87262f44abdcf675a8 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 24 Jul 2026 03:19:31 +0800 Subject: [PATCH 23/25] =?UTF-8?q?chore:=20mcpp=20pin=200.0.99=20->=200.0.1?= =?UTF-8?q?04(=E6=B5=8B=E8=AF=95=E8=83=BD=E5=8A=9B=E6=89=B9=E6=AC=A1?= =?UTF-8?q?=E5=B7=B2=E5=8F=91=E5=B8=83,CI=20=E4=BE=9D=E8=B5=96=E6=BB=A1?= =?UTF-8?q?=E8=B6=B3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/dslings-ref-ci.yml | 6 ++---- .xlings.json | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dslings-ref-ci.yml b/.github/workflows/dslings-ref-ci.yml index 0cc2c07..37279cc 100644 --- a/.github/workflows/dslings-ref-ci.yml +++ b/.github/workflows/dslings-ref-ci.yml @@ -7,9 +7,8 @@ name: Validate exercises and reference solutions # 练习即测试:e2e 走 `mcpp test`,与 `d2x checker` 内部的 Provider 是同一条 # 链路,所以 CI 绿灯等价于学习者本地能跑通,而不是另一条平行的构建路径。 # -# ⚠️ 依赖 mcpp「测试能力批次」(逐测试隔离 / 过滤 / --message-format json / -# tests 吃 [build].flags,本地分支 feat/test-isolation-json)。在 mcpp 发布 -# 携带这些能力的版本并更新安装 pin 之前,本 workflow 会失败 —— 这是预期的。 +# 依赖 mcpp >= 0.0.104(测试能力批次:逐测试隔离 / 过滤 / --list / --timeout / +# --message-format json / tests 吃 [build].flags)。pin 见 .xlings.json。 on: push: @@ -52,7 +51,6 @@ jobs: echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" # 按 .xlings.json 安装 mcpp。mcpp 自带工具链沙箱,无需另装 gcc。 - # 需要携带「测试能力批次」的 mcpp 版本(见文件头注释)。 - name: Install mcpp run: | set -eu diff --git a/.xlings.json b/.xlings.json index c66cdbb..f628ea7 100644 --- a/.xlings.json +++ b/.xlings.json @@ -4,9 +4,9 @@ "mdbook": "0.4.43", "code": "", "mcpp": { - "linux": "0.0.99", - "macosx": "0.0.99", - "windows": "0.0.99" + "linux": "0.0.104", + "macosx": "0.0.104", + "windows": "0.0.104" } } } From 21f014e1ae88a40388f0e679cd8f07b102c8e4a3 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 24 Jul 2026 05:26:11 +0800 Subject: [PATCH 24/25] =?UTF-8?q?chore:=20d2x=20pin=200.1.5=20->=202026.07?= =?UTF-8?q?.24.1(=E5=8D=8F=E8=AE=AE=E5=B1=82=E5=88=86=E5=BA=93/=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=E6=80=A7=E6=89=B9=E6=AC=A1=E5=B7=B2=E5=8F=91=E5=B8=83?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .xlings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.xlings.json b/.xlings.json index f628ea7..8e71f7c 100644 --- a/.xlings.json +++ b/.xlings.json @@ -1,6 +1,6 @@ { "workspace": { - "d2x": "0.1.5", + "d2x": "2026.07.24.1", "mdbook": "0.4.43", "code": "", "mcpp": { From 3d11b78afe473d1fbf83cfae75873901f1f89278 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 24 Jul 2026 07:24:42 +0800 Subject: [PATCH 25/25] =?UTF-8?q?docs:=20=E8=8E=B7=E5=8F=96=E4=B8=8E?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=AF=B9=E9=BD=90=20d2x=202026.07.24.1(statu?= =?UTF-8?q?s/=E5=8E=9F=E7=94=9F=E6=A8=A1=E5=BC=8F=E5=8F=8C=E5=85=A5?= =?UTF-8?q?=E5=8F=A3;d2x=20update=20=E5=B9=BD=E7=81=B5=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20git=20=E8=AF=B4=E6=98=8E;=E4=B8=89?= =?UTF-8?q?=E8=AF=AD=20README=20=E5=BF=AB=E9=80=9F=E5=BC=80=E5=A7=8B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++++--- README.zh.hant.md | 9 ++++++--- README.zh.md | 9 ++++++--- book/en/src/base/chapter_1.md | 21 +++++++++++++++++++-- book/src/base/chapter_1.md | 21 +++++++++++++++++++-- 5 files changed, 56 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a6a8253..ddab1b3 100644 --- a/README.md +++ b/README.md @@ -67,12 +67,15 @@ irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_i ```bash -xlings install d2x -y -d2x install d2mcpp +xlings install d2x -y # exercise framework CLI +d2x install d2mcpp # get the course, environment auto-configured cd d2mcpp -d2x checker +d2x checker # practice loop: edit -> save -> auto-check -> advance +d2x status # progress overview ``` +> Exercises are real mcpp tests — `mcpp test -p src/cpp11` works too (no d2x involved). + **👉 [more details...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** ## Community diff --git a/README.zh.hant.md b/README.zh.hant.md index 97139d0..6a0525f 100644 --- a/README.zh.hant.md +++ b/README.zh.hant.md @@ -67,12 +67,15 @@ irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_i ```bash -xlings install d2x -y -d2x install d2mcpp +xlings install d2x -y # 練習框架 CLI +d2x install d2mcpp # 一鍵獲取課程,環境自動配置 cd d2mcpp -d2x checker +d2x checker # 練習循環: 編輯 -> 保存 -> 自動檢測 -> 推進 +d2x status # 進度總覽 ``` +> 練習就是真實的 mcpp 測試 —— 不經過 d2x,直接 `mcpp test -p src/cpp11` 同樣可用。 + **👉 [更多細節...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** ## 社區 diff --git a/README.zh.md b/README.zh.md index c88aede..8880850 100644 --- a/README.zh.md +++ b/README.zh.md @@ -67,12 +67,15 @@ irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_i ```bash -xlings install d2x -y -d2x install d2mcpp +xlings install d2x -y # 练习框架 CLI +d2x install d2mcpp # 一键获取课程,环境自动配置 cd d2mcpp -d2x checker +d2x checker # 练习循环: 编辑 -> 保存 -> 自动检测 -> 推进 +d2x status # 进度总览 ``` +> 练习就是真实的 mcpp 测试 —— 不经过 d2x,直接 `mcpp test -p src/cpp11` 同样可用。 + **👉 [更多细节...](https://mcpp-community.github.io/d2mcpp/base/chapter_1.html)** ## 社区 diff --git a/book/en/src/base/chapter_1.md b/book/en/src/base/chapter_1.md index 78a11ae..4354f9f 100644 --- a/book/en/src/base/chapter_1.md +++ b/book/en/src/base/chapter_1.md @@ -64,12 +64,29 @@ d2x checker [name] > Note: Exercise names support fuzzy matching +### View Learning Progress + +> Read-only progress overview grouped by chapter, without entering practice mode + +```bash +d2x status +``` + +### Native Mode (optional) + +> Every C++ standard directory is a real mcpp project and the exercises are its tests/ — you can practice with plain mcpp, no d2x involved: + +```bash +mcpp test -p src/en/cpp11 # progress table for the whole cpp11 section +mcpp test -p src/en/cpp11 03-trailing # run only exercises matching the name +``` + ### Sync Latest Practice Code -> Since the project is continuously updated, you can use the following command for automatic synchronization (if synchronization fails, you may need to manually update the project code using git) +> The project is continuously updated; update via git (save/commit your modified exercises first): ```bash -d2x update +git pull ``` ## 2. Automated Detection Program Introduction diff --git a/book/src/base/chapter_1.md b/book/src/base/chapter_1.md index a9202d6..fc7a251 100644 --- a/book/src/base/chapter_1.md +++ b/book/src/base/chapter_1.md @@ -64,12 +64,29 @@ d2x checker [name] > 注: 练习名支持模糊匹配 +### 查看学习进度 + +> 只读的进度总览,按章节聚合,不进入练习模式 + +```bash +d2x status +``` + +### 原生模式(可选) + +> 每个 C++ 标准目录都是真实的 mcpp 工程,练习就是它的 tests/——不经过 d2x,直接用 mcpp 也能练: + +```bash +mcpp test -p src/cpp11 # 整个 cpp11 的进度表(哪题绿哪题红) +mcpp test -p src/cpp11 03-trailing # 只跑名字匹配的练习 +``` + ### 同步最新的练习代码 -> 由于项目处于持续更新阶段, 可以使用下面的命令进行自动同步(如果同步失败, 可能需要手动用git进行更新项目代码) +> 项目处于持续更新阶段,更新使用 git(注意先保存/提交你已修改的练习): ```bash -d2x update +git pull ``` ## 2.自动化检测程序简介