@chengzhao-dev/dsh-cpp-workflow 0.6.13 → 0.6.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/package.json +1 -1
- package/skills/building-cpp/SKILL.md +18 -16
- package/skills/building-cpp/references/static-analysis.md +1 -1
- package/skills/building-cpp/steps/02-tooling.md +2 -2
- package/skills/building-cpp/steps/03-check.md +4 -3
- package/skills/configuring-cmake/SKILL.md +17 -17
- package/skills/configuring-cmake/steps/03-check.md +2 -2
- package/skills/developing-cpp/SKILL.md +13 -13
- package/skills/developing-cpp/examples/comments-declaration.md +22 -27
- package/skills/developing-cpp/examples/comments-implementation.md +1 -1
- package/skills/developing-cpp/examples/exceptions.md +2 -2
- package/skills/developing-cpp/examples/naming.md +1 -8
- package/skills/developing-cpp/examples/structure.md +12 -12
- package/skills/developing-cpp/references/change-model.md +55 -0
- package/skills/developing-cpp/references/comments.md +38 -21
- package/skills/developing-cpp/references/declaration-order.md +1 -1
- package/skills/developing-cpp/references/exceptions.md +4 -4
- package/skills/developing-cpp/references/glossary.md +1 -1
- package/skills/developing-cpp/references/google-cpp-style.md +3 -20
- package/skills/developing-cpp/references/language.md +2 -2
- package/skills/developing-cpp/references/layout.md +4 -13
- package/skills/developing-cpp/references/naming.md +2 -2
- package/skills/developing-cpp/references/performance.md +2 -1
- package/skills/developing-cpp/references/priority.md +3 -1
- package/skills/developing-cpp/references/project-design.md +2 -2
- package/skills/developing-cpp/scripts/checking-comments.mjs +5 -0
- package/skills/developing-cpp/steps/01-behavior.md +10 -10
- package/skills/developing-cpp/steps/02-boundary.md +17 -19
- package/skills/developing-cpp/steps/03-declaration.md +20 -13
- package/skills/developing-cpp/steps/04-implementation.md +16 -16
package/README.en.md
CHANGED
|
@@ -19,13 +19,13 @@ The exception material follows the C++ Core Guidelines' Error handling section a
|
|
|
19
19
|
Pinned npm install:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.
|
|
22
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.14
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
The matching Git tag:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.
|
|
28
|
+
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.14
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
The desktop app owns the `desktop` profile. If the command-line `add` is rejected, add the plugin in the app settings and use the same pinned npm version. A Git tag install uses the prebuilt `lib/` committed in the repository and runs no build on the target machine; the package has no install script that needs approval.
|
package/README.md
CHANGED
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
npm 安装钉死版本:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.
|
|
22
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.14
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
同版本的 Git 标签:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.
|
|
28
|
+
dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.14
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
桌面应用独占 `desktop` profile。命令行 `add` 被拒绝时,在应用设置里添加插件,地址填同一个 npm 版本。Git 标签安装使用仓库内已构建的 `lib/`,不在目标机器上重新构建;包内也不含需要授权执行的安装脚本。
|
package/package.json
CHANGED
|
@@ -21,14 +21,14 @@ metadata:
|
|
|
21
21
|
|
|
22
22
|
## 三种操作
|
|
23
23
|
|
|
24
|
+
一次只选一种,范围(单点、整段)是任务属性。操作与域的判据、边界和交接见 `../developing-cpp/references/change-model.md`;四类改动是域,不是操作。
|
|
25
|
+
|
|
24
26
|
| 操作 | 什么时候用 | 从哪步进 |
|
|
25
27
|
| --- | --- | --- |
|
|
26
28
|
| 写作(起稿) | 改环境轴、入口脚本或构建流程 | 第 1 步;新工程走到第 3 步 |
|
|
27
29
|
| 改写 | 环境轴与 target 契约不变,工具配置位置或流程顺序不对 | 从第 2 步重走,可到第 3 步 |
|
|
28
30
|
| 润色 | 只改诊断措辞或失败分流说明 | 第 3 步 |
|
|
29
31
|
|
|
30
|
-
范围随任务定:单点修正只改一条配置或诊断;整段重构先确认 target 与标准已由 configuring-cmake 写好,再改工具文件,最后跑完整检查。
|
|
31
|
-
|
|
32
32
|
三种操作共用这些约束:不发明项目没有声明的环境轴、命令和 target;只改格式或配置时不改可观察行为;不覆盖已有源码、`CMakeLists.txt` 和 `.clang-*`;不安装工具链;代理侧不写 `wsl` 或 `bash -lc`。后一步发现前一步没做完,退回那一步。拿不准先用单点修正;发现环境轴、编译器、构建目录或 target 契约需要改,退回整段,并转 `configuring-cmake`。
|
|
33
33
|
|
|
34
34
|
## 步骤
|
|
@@ -41,9 +41,11 @@ metadata:
|
|
|
41
41
|
|
|
42
42
|
每步只打开同号的一个步骤文件。判不准才打开 `examples/` 里与该步对应的案例;`references/` 保存完整条文,只在排错、整目录重构,或步骤里明确指向某一条文时按需打开。链接、rpath 与产物布局读 `references/linking.md`;测试设计、断言与 CTest 注册读 `references/testing.md`;CMake 的 target、标准和函数写法读 `../configuring-cmake/references/`,可复用片段在 `../configuring-cmake/assets/cmake/`。
|
|
43
43
|
|
|
44
|
+
入口脚本只做配置、构建、CTest 与最小运行;`clang-format` 与 `clang-tidy` 的手动检查留在定工具配置,不塞进入口脚本。`environment.json` 的三个环境轴与 `standard` 是唯一来源,缺项就停。
|
|
45
|
+
|
|
44
46
|
本轮激活按三条上限走:
|
|
45
47
|
|
|
46
|
-
- 只开当前步对应的一个步骤文件;同一改动域内不同时展开两个技能的步骤。跨域按
|
|
48
|
+
- 只开当前步对应的一个步骤文件;同一改动域内不同时展开两个技能的步骤。跨域按 `源码 → configuring-cmake → building-cpp` 的顺序逐段进入,前一段结论落下再开下一段。
|
|
47
49
|
- 条文只开该步点名的一份;同号案例留到下一次激活。
|
|
48
50
|
- 标准档只打开项目技能声明的那一档,没有声明时用本技能已有的新工程默认,不把其余标准档读进来。
|
|
49
51
|
|
|
@@ -51,20 +53,20 @@ metadata:
|
|
|
51
53
|
|
|
52
54
|
### 四类改动
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
四类改动是操作内部的域:操作定入口,域定层次。四域的定义、边界与次序见 `../developing-cpp/references/change-model.md`;一次改动混了两类时,按「写作 → 布局 → 排版 → 润色」拆开做,前一类落定并经检查再进下一类。
|
|
55
57
|
|
|
56
|
-
| 类型 | 典型请求 |
|
|
57
|
-
| --- | --- | --- |
|
|
58
|
-
| 写作 | 改环境轴、入口脚本或构建流程 |
|
|
59
|
-
| 布局 | 移动工具配置文件、补 `.vscode/` |
|
|
60
|
-
| 排版 | 只改编码、行尾或 clang-format |
|
|
61
|
-
| 润色 | 只改诊断措辞、失败分流说明 |
|
|
58
|
+
| 类型 | 典型请求 | 入口步骤 |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| 写作 | 改环境轴、入口脚本或构建流程 | `steps/01-build.md` |
|
|
61
|
+
| 布局 | 移动工具配置文件、补 `.vscode/` | `steps/02-tooling.md` |
|
|
62
|
+
| 排版 | 只改编码、行尾或 clang-format | `steps/02-tooling.md` |
|
|
63
|
+
| 润色 | 只改诊断措辞、失败分流说明 | `steps/03-check.md` |
|
|
62
64
|
|
|
63
65
|
## 完成判据
|
|
64
66
|
|
|
65
|
-
- 项目 `environment.json` 的三个环境轴齐全且是合法组合,`standard` 在 `c++11`–`c++23` 内;新工程默认 `c++23`,没有靠猜。
|
|
66
|
-
- 配置命令固定 Ninja 和单一编译器,`build/<compiler>/compile_commands.json` 存在,根 `build/compile_commands.json` 指向当前编译器。
|
|
67
|
-
- target 契约已经成立:程序 `app`、库 target、公共 include、标准与输出目录都已由 `configuring-cmake` 写好;本技能没有偷偷改 CMake 来绕过失败。
|
|
68
|
-
- `clang-format --dry-run --Werror` 与 `clang-tidy` 在项目源码上零告警;只扫描项目源码和项目头。
|
|
69
|
-
- CTest 全绿,程序从该编译器的构建目录启动;有测试时注册用 `gtest_discover_tests` 或项目既有机制,运行带 `--output-on-failure --no-tests=error`,失败时报告的是具体角色和原因。
|
|
70
|
-
- 没有改源码语义,没有覆盖已有工程文件,也没有安装或下载任何工具。
|
|
67
|
+
- [ ] 项目 `environment.json` 的三个环境轴齐全且是合法组合,`standard` 在 `c++11`–`c++23` 内;新工程默认 `c++23`,没有靠猜。
|
|
68
|
+
- [ ] 配置命令固定 Ninja 和单一编译器,`build/<compiler>/compile_commands.json` 存在,根 `build/compile_commands.json` 指向当前编译器。
|
|
69
|
+
- [ ] target 契约已经成立:程序 `app`、库 target、公共 include、标准与输出目录都已由 `configuring-cmake` 写好;本技能没有偷偷改 CMake 来绕过失败。
|
|
70
|
+
- [ ] `clang-format --dry-run --Werror` 与 `clang-tidy` 在项目源码上零告警;只扫描项目源码和项目头。
|
|
71
|
+
- [ ] CTest 全绿,程序从该编译器的构建目录启动;有测试时注册用 `gtest_discover_tests` 或项目既有机制,运行带 `--output-on-failure --no-tests=error`,失败时报告的是具体角色和原因。
|
|
72
|
+
- [ ] 没有改源码语义,没有覆盖已有工程文件,也没有安装或下载任何工具。
|
|
@@ -12,7 +12,7 @@ clang-format --dry-run --Werror src/*.cpp include/*.h tests/*.cpp
|
|
|
12
12
|
- 工程必须有根 `.clang-format`;缺失时先复制配置,不要回退到内置风格,回退会掩盖配置缺失。
|
|
13
13
|
- 只扫描项目源码:`src/`、`include/`、`apps/`、`tests/` 等实际存在的目录;不扫描 `build/`、第三方、生成文件。
|
|
14
14
|
- 配置文件在工程根 `.clang-format`;不要依赖用户主目录或编辑器内置配置。
|
|
15
|
-
- `BasedOnStyle: Google`、`Standard: Latest`;clang-format 21/22 不接受 `c++23`,`Latest`
|
|
15
|
+
- `BasedOnStyle: Google`、`Standard: Latest`;clang-format 21/22 不接受 `c++23`,`Latest` 按工具支持的最高标准解析。列宽、缩进与包含顺序以工程根 `.clang-format` 为准;工程显式写 `ColumnLimit` 时按工程值执行,模板资产不写 `ColumnLimit`。
|
|
16
16
|
- 格式化失败就是失败,不要用 `// clang-format off` 绕过;确需关闭时只包住无法排版的宏或表格,并写一句理由。
|
|
17
17
|
|
|
18
18
|
## clang-tidy
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- `.clangd` 的 `CompilationDatabase` 指向 `build/<compiler>`;切换编译器时同步改,不能留旧目录。**不要写 `-std`**:它会覆盖 `compile_commands.json`,把 C++23 工程降成旧标准;标准只由构建声明和编译数据库决定。
|
|
14
14
|
- `--query-driver` 是 clangd 的命令行参数,写在 `.vscode/settings.json` 的 `clangd.arguments` 里,不是 `.clangd` 的 YAML 键。它告诉 clangd 允许执行哪个编译器驱动来取系统头路径;路径写绝对路径,并与 `environment.json` 的编译器一致。
|
|
15
15
|
- 首次复制时按轴替换三处:`compiler=clang` 用 `CompilationDatabase: build/clang`、`--query-driver=/usr/bin/clang++` 和 `cmake.debugConfig` 的 `MIMode: lldb` / `miDebuggerPath: /usr/bin/lldb`;`compiler=gcc` 用 `build/gcc`、`/usr/bin/g++` 和 `MIMode: gdb` / `/usr/bin/gdb`。只复制模板而不改这三处,GCC 模式会指向错误的数据库、驱动或调试器。原工程已有的 `.clangd` 不覆盖;要改先征得同意。
|
|
16
|
-
- `.clang-format` 保留 `Language: Cpp`、`BasedOnStyle: Google` 和 `Standard`:模板资产写 `Latest`;工程按 `environment.json` 声明 C++11–C++20 时,把 `Standard` 改成 `c++NN`(映射见 `references/standards/README.md`)。clang-format 21/22 的 `Standard` 不接受 `c++23`,所以 C++23 工程仍写 `Latest
|
|
16
|
+
- `.clang-format` 保留 `Language: Cpp`、`BasedOnStyle: Google` 和 `Standard`:模板资产写 `Latest`;工程按 `environment.json` 声明 C++11–C++20 时,把 `Standard` 改成 `c++NN`(映射见 `references/standards/README.md`)。clang-format 21/22 的 `Standard` 不接受 `c++23`,所以 C++23 工程仍写 `Latest`。列宽、缩进与包含顺序以工程根 `.clang-format` 为准;工程显式写 `ColumnLimit` 时按工程值执行,模板资产不写 `ColumnLimit`。需要新规则时先改 `references/google-cpp-style.md` 并说明理由。
|
|
17
17
|
- `.clang-tidy` 用 `Checks: '-*, google-*, portability-avoid-pragma-once, readability-identifier-naming'`,保留 `WarningsAsErrors: '*'` 和 `FormatStyle: file`。`google-*` 覆盖 Google 风格能静态检查的部分;`readability-identifier-naming` 只配项目确实统一的命名项:类型/枚举/别名/模板参数 PascalCase,变量/参数/成员 snake_case,类成员尾随 `_`,结构体成员不尾随,常量 `k` + PascalCase。不要设 `FunctionCase`,Google 允许 `count()` / `set_count()` 这类访问器;`portability-avoid-pragma-once` 要打开,因为工程统一用路径式守卫;不要重新打开 `cppcoreguidelines-*` 整包。
|
|
18
18
|
- `.clang-tidy` 关闭项和例外必须写理由;不要借 `-*` 之后再打开 `cppcoreguidelines-*` 这类非 Google 整族检查。
|
|
19
19
|
- `.vscode/extensions.json` 推荐 clangd、CMake Tools 与 C/C++ 扩展;`.vscode/settings.json` 固定 clangd 参数、CMake 构建目录和 `cmake.debugConfig`。配置、构建、运行和调试只从 CMake Tools 进入,不写 `tasks.json` 或 `launch.json`;C/C++ 扩展只作为 CMake 快速调试的后端,IntelliSense 关闭,避免和 clangd 并行。配置注释用 JSONC 的 `//`,贴在被解释的键上方。
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
**GCC 模式**:`clangd` 仍可工作,但系统头来自 GCC;`--query-driver` 指向 `g++`。`clang-format` / `clang-tidy` 仍是 Clang 工具,版本要求不因编译器是 GCC 而降低。
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
**共享库**:静态检查不因链接方式改变;运行时库路径由 `configuring-cmake` 的 target 设置负责,见其 `references/cmake.md`,不写进 clang 配置。
|
|
37
37
|
|
|
38
38
|
## 正反例指针
|
|
39
39
|
|
|
@@ -30,11 +30,12 @@ node "<skill-dir>/scripts/checking-environment.mjs" --project . --live # 活
|
|
|
30
30
|
| 现象 | 先看 | 回到 |
|
|
31
31
|
| --- | --- | --- |
|
|
32
32
|
| 轴为空、组合非法、版本不匹配 | `environment.json`、`--live` 输出 | 定构建,先修声明 |
|
|
33
|
-
| 找不到头文件、链接失败、target 不存在 | `CMakeLists.txt`、`target_include_directories`、`target_link_libraries` |
|
|
34
|
-
| 没有 `compile_commands.json
|
|
33
|
+
| 找不到头文件、链接失败、target 不存在 | `CMakeLists.txt`、`target_include_directories`、`target_link_libraries` | `configuring-cmake` 的定 target;修完回定构建 |
|
|
34
|
+
| 没有 `compile_commands.json` | 顶层 `CMAKE_EXPORT_COMPILE_COMMANDS` 是否由 target 图声明 | `configuring-cmake` 的定 target;生成器与目录回定构建 |
|
|
35
|
+
| clangd 跳转失败、数据库指向旧编译器 | `.clangd` 的 `CompilationDatabase`、`build/compile_commands.json` 链接 | 定工具配置 |
|
|
35
36
|
| 编译错误指向函数体 | 实现 | `developing-cpp` 的定实现;先修语义,再重跑 |
|
|
36
37
|
| CTest 失败 | 测试断言、行为句 | `developing-cpp` 的定行为 / 定实现 |
|
|
37
|
-
| `app` 启动失败、找不到共享库 | 输出目录、RPATH / 运行时库路径 |
|
|
38
|
+
| `app` 启动失败、找不到共享库 | 输出目录、RPATH / 运行时库路径 | `configuring-cmake` 的定 target(输出目录与 RPATH) |
|
|
38
39
|
|
|
39
40
|
分诊只定位,不自动改。一次只改一类问题;改完从失败的那一步重跑,只有行为或边界也变了才回到定行为。
|
|
40
41
|
|
|
@@ -13,18 +13,18 @@ metadata:
|
|
|
13
13
|
|
|
14
14
|
一次改动同时碰到源码和 CMake 时先开 `developing-cpp` 定行为与边界,再用本技能把源文件、库和程序写进 target,最后到 `building-cpp` 跑检查。只改一个 target 的 include、链接或标准时走单点修正;新工程、新组件、换语言标准或抽公共函数时走整段重构。
|
|
15
15
|
|
|
16
|
-
存在项目技能 `cpp-workflow-project` 时先读它的 `SKILL.md`、`environment.json` 和 `stages/stage-table.md`。`environment.json` 的 `standard` 是 target 标准的唯一来源;缺失、非法或与现有 target 不一致时停在定 target,不猜标准。构建目录、编译器和工具版本由 `building-cpp` 的定构建负责,本技能只写 target
|
|
16
|
+
存在项目技能 `cpp-workflow-project` 时先读它的 `SKILL.md`、`environment.json` 和 `stages/stage-table.md`。`environment.json` 的 `standard` 是 target 标准的唯一来源;缺失、非法或与现有 target 不一致时停在定 target,不猜标准。构建目录、编译器和工具版本由 `building-cpp` 的定构建负责,本技能只写 target 契约;target 图与 CMake 写法以本技能为权威,`building-cpp` 只读不修改。
|
|
17
17
|
|
|
18
18
|
## 三种操作
|
|
19
19
|
|
|
20
|
+
一次只选一种,范围(单点、整段)是任务属性。操作与域的判据、边界和交接见 `../developing-cpp/references/change-model.md`;四类改动是域,不是操作。
|
|
21
|
+
|
|
20
22
|
| 操作 | 什么时候用 | 从哪步进 |
|
|
21
23
|
| --- | --- | --- |
|
|
22
24
|
| 写作(起稿) | 新增 target、改公共 include 或链接边界、换语言标准 | 第 1 步;新工程走到第 3 步 |
|
|
23
25
|
| 改写 | target 图与标准不变,模块、函数或目录结构不对 | 从第 2 步重走,可到第 3 步 |
|
|
24
26
|
| 润色 | 只改函数名、注释措辞或错误消息 | 第 2 步 |
|
|
25
27
|
|
|
26
|
-
范围随任务定:单点修正只改一个 target 的一处;整段重构先定 target 图与标准,再定模块与函数,最后跑完整检查。
|
|
27
|
-
|
|
28
28
|
三种操作共用这些约束:不发明项目没有声明的 target、标准、编译器或目录;不覆盖已有 `CMakeLists.txt`;不用全局 `include_directories()`、`add_definitions()`、`CMAKE_CXX_FLAGS` 给所有 target 加料;不写 `file(GLOB ...)`;不把 `main()` 编进库;库与程序不下载依赖,只有测试需要时调用 `LinkSettings_FetchGoogleTest`。后一步发现前一步没做完,退回那一步。
|
|
29
29
|
|
|
30
30
|
## 步骤
|
|
@@ -39,7 +39,7 @@ metadata:
|
|
|
39
39
|
|
|
40
40
|
本轮激活按三条上限走:
|
|
41
41
|
|
|
42
|
-
- 只开当前步对应的一个步骤文件;同一改动域内不同时展开两个技能的步骤。跨域按
|
|
42
|
+
- 只开当前步对应的一个步骤文件;同一改动域内不同时展开两个技能的步骤。跨域按 `源码 → configuring-cmake → building-cpp` 的顺序逐段进入,前一段结论落下再开下一段。
|
|
43
43
|
- 条文只开该步点名的一份;同号案例留到下一次激活。
|
|
44
44
|
- 标准档只打开项目技能声明的那一档,没有声明时用本技能已有的新工程默认,不把其余标准档读进来。
|
|
45
45
|
|
|
@@ -49,20 +49,20 @@ metadata:
|
|
|
49
49
|
|
|
50
50
|
### 四类改动
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
四类改动是操作内部的域:操作定入口,域定层次。四域的定义、边界与次序见 `../developing-cpp/references/change-model.md`;一次改动混了两类时,按「写作 → 布局 → 排版 → 润色」拆开做,前一类落定并经检查再进下一类。
|
|
53
53
|
|
|
54
|
-
| 类型 | 典型请求 |
|
|
55
|
-
| --- | --- | --- |
|
|
56
|
-
| 写作 | 新增 target、改公共 include 或链接边界、换语言标准 |
|
|
57
|
-
| 布局 | 抽函数、拆模块、调导入位置 |
|
|
58
|
-
| 排版 | 只改 2 空格、行宽或注释形态 |
|
|
59
|
-
| 润色 | 只改函数名、注释措辞或错误消息 |
|
|
54
|
+
| 类型 | 典型请求 | 入口步骤 |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| 写作 | 新增 target、改公共 include 或链接边界、换语言标准 | `steps/01-targets.md` |
|
|
57
|
+
| 布局 | 抽函数、拆模块、调导入位置 | `steps/02-modules.md` |
|
|
58
|
+
| 排版 | 只改 2 空格、行宽或注释形态 | `steps/02-modules.md` |
|
|
59
|
+
| 润色 | 只改函数名、注释措辞或错误消息 | `steps/02-modules.md` |
|
|
60
60
|
|
|
61
61
|
## 完成判据
|
|
62
62
|
|
|
63
|
-
- target 图显式:程序 target 叫 `app`,库 target 用组件名,程序链接库,库的公共 include 用 `PUBLIC`;没有全局 include、全局编译选项或 GLOB。
|
|
64
|
-
- 每个 target 的标准来自 `environment.json`;`target_compile_features` 与 `CXX_STANDARD`、`CXX_STANDARD_REQUIRED ON`、`CXX_EXTENSIONS OFF` 同时存在,live 的 `compile_commands.json` 最后一条 `-std=` 与声明一致,不是 `gnu++NN`。
|
|
65
|
-
- 复用的 CMake 函数放在 `cmake/` 模块里,文件名用 UpperCamelCase,函数名用 `<ModuleName>_` 加 PascalCase,有目标与参数校验,根 `CMakeLists.txt` 通过 `CMAKE_MODULE_PATH` 导入;没有重复片段和未使用的旧模块。
|
|
66
|
-
- 公开函数上方写中文 `#` 契约:一句功能、空行、边界,再用 `:param` / `:keyword` 标参数;不写 `#[[.rst:` 与 `.. command::`,完整写法见 `references/cmake.md`;函数组之间用 `#---`,不画 `#===...===#` 方框。
|
|
67
|
-
- `CMakeLists.txt` 与 `*.cmake` 用 UTF-8 无 BOM、LF、2 空格缩进、不用 tab;行宽按团队约定,默认 80 列。
|
|
68
|
-
- configure 与构建通过,构建产物不落在源码目录;没有改源码语义,也没有覆盖已有工程文件。
|
|
63
|
+
- [ ] target 图显式:程序 target 叫 `app`,库 target 用组件名,程序链接库,库的公共 include 用 `PUBLIC`;没有全局 include、全局编译选项或 GLOB。
|
|
64
|
+
- [ ] 每个 target 的标准来自 `environment.json`;`target_compile_features` 与 `CXX_STANDARD`、`CXX_STANDARD_REQUIRED ON`、`CXX_EXTENSIONS OFF` 同时存在,live 的 `compile_commands.json` 最后一条 `-std=` 与声明一致,不是 `gnu++NN`。
|
|
65
|
+
- [ ] 复用的 CMake 函数放在 `cmake/` 模块里,文件名用 UpperCamelCase,函数名用 `<ModuleName>_` 加 PascalCase,有目标与参数校验,根 `CMakeLists.txt` 通过 `CMAKE_MODULE_PATH` 导入;没有重复片段和未使用的旧模块。
|
|
66
|
+
- [ ] 公开函数上方写中文 `#` 契约:一句功能、空行、边界,再用 `:param` / `:keyword` 标参数;不写 `#[[.rst:` 与 `.. command::`,完整写法见 `references/cmake.md`;函数组之间用 `#---`,不画 `#===...===#` 方框。
|
|
67
|
+
- [ ] `CMakeLists.txt` 与 `*.cmake` 用 UTF-8 无 BOM、LF、2 空格缩进、不用 tab;行宽按团队约定,默认 80 列。
|
|
68
|
+
- [ ] configure 与构建通过,构建产物不落在源码目录;没有改源码语义,也没有覆盖已有工程文件。
|
|
@@ -34,6 +34,6 @@ GCC 模式把目录和编译器换成 `build/gcc`、`g++`。每次检查都重
|
|
|
34
34
|
| 函数参数没校验、模块重复导入、旧模块残留 | 第 2 步 |
|
|
35
35
|
| 只是 CTest 或运行失败 | 转 `building-cpp` 的通过检查 |
|
|
36
36
|
|
|
37
|
-
##
|
|
37
|
+
## 交回构建
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
本步的判据是 configure 与构建成功、数据库标准正确、结构规则无命中;工具链、入口脚本、`clang-tidy` 与 CTest 的完整检查见 `building-cpp` 的 `steps/03-check.md`。
|
|
@@ -7,24 +7,24 @@ metadata:
|
|
|
7
7
|
|
|
8
8
|
# developing-cpp
|
|
9
9
|
|
|
10
|
-
## 适用场景
|
|
11
|
-
|
|
12
10
|
本技能处理换一台机器也不会改的源码内容:可观察行为、库与程序的边界、声明、命名、异常和源码注释。文件槽位与守卫以 `references/file-template.md` 为准;排版、命名、头文件与包含顺序以 `references/google-cpp-style.md` 的 Google 基线为准。异常条文取自 C++ Core Guidelines 的 Error handling,只约束开发期的源码,不进入插件运行时。`CMakeLists.txt` 与 `cmake/` 开 `configuring-cmake`;`running-build.sh`、`.clang-*`、`.vscode/` 和 `environment.json` 的维护开 `building-cpp`(本技能只读其中的 `standard`)。只改函数体、命名、异常或注释且入口命令可运行时,定实现后只开 `building-cpp` 的「通过检查」;新增或移动源文件、改库与程序的边界、补测试 target 时,声明和实现可以先写,但完成前必须开 `configuring-cmake` 把文件写进 CMake,再开 `building-cpp` 定工具配置并通过检查。`.qmd`、仓库 README、Python 和非 C++ 文档不打开本技能。
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
## 适用场景
|
|
13
|
+
|
|
14
|
+
存在项目技能 `cpp-workflow-project` 时先读它的 `SKILL.md`:工程名、库与程序的边界、阶段表、术语增补、入口命令与语言档位。项目技能没有写档位时,才读 `environment.json` 的 `standard`;两者都没有时才用本技能的新工程默认 C++23。声明 C++20 的工程里不要写 C++23 才有的设施;没有项目技能时门禁不会放出本技能,不要猜项目约定。
|
|
15
15
|
|
|
16
16
|
源码和构建文件的编码以 `building-cpp` 的 `references/encoding.md` 为准;行尾与 `.clang-format` 的 `LineEnding` 跟仓库根 `.gitattributes` 和 `.editorconfig`。
|
|
17
17
|
|
|
18
18
|
## 三种操作
|
|
19
19
|
|
|
20
|
+
一次只选一种,范围(单点、整段)是任务属性。操作与域的判据、边界和交接见 `references/change-model.md`;四类改动是域,不是操作。
|
|
21
|
+
|
|
20
22
|
| 操作 | 什么时候用 | 从哪步进 |
|
|
21
23
|
| --- | --- | --- |
|
|
22
24
|
| 写作(起稿) | 新增类型、函数或文件;改可观察行为;改库与程序的边界;补构造后置条件与成员事实 | 第 1 步;新工程走到第 4 步 |
|
|
23
25
|
| 改写 | 行为与契约不变,文件位置、声明顺序或函数体组织不对 | 从被打破的步重走,可到第 4 步 |
|
|
24
26
|
| 润色 | 只改命名或注释措辞 | 第 3 步 |
|
|
25
27
|
|
|
26
|
-
范围随任务定:单点修正只改判为不合格的一处,其余保持原样;整段重构第 1、2 步先定删留,不动函数体,第 3、4 步才改代码。
|
|
27
|
-
|
|
28
28
|
三种操作共用这些约束:不发明阶段表和用户请求里没有的行为、类型和命令;选类型与写法时以项目声明的语言标准为上限;只改注释或格式时不改可观察行为;不覆盖已有源码;不安装工具链;代理侧不写 `wsl` 或 `bash -lc`。计划、命名方案或注释措辞与条文、项目技能或用户明确要求冲突时,先列冲突位置、原文与出处,由用户选一条;不自行取舍,也不默认采用计划。后一步发现前一步没做完,退回那一步,不在当前步补结构。拿不准先用单点修正;做到一半发现边界被打破,退回整段重构,从被打破的那一步重走。
|
|
29
29
|
|
|
30
30
|
## 步骤
|
|
@@ -40,7 +40,7 @@ metadata:
|
|
|
40
40
|
|
|
41
41
|
本轮激活按三条上限走:
|
|
42
42
|
|
|
43
|
-
- 只开当前步对应的一个步骤文件;同一改动域内不同时展开两个技能的步骤。跨域按
|
|
43
|
+
- 只开当前步对应的一个步骤文件;同一改动域内不同时展开两个技能的步骤。跨域按 `源码 → configuring-cmake → building-cpp` 的顺序逐段进入,前一段结论落下再开下一段。
|
|
44
44
|
- 条文只开该步点名的一份;同号案例留到下一次激活。
|
|
45
45
|
- 标准档只打开项目技能声明的那一档,没有声明时用本技能已有的新工程默认,不把其余标准档读进来。
|
|
46
46
|
|
|
@@ -48,7 +48,7 @@ metadata:
|
|
|
48
48
|
|
|
49
49
|
### 四类改动
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
四类改动是操作内部的域:操作定入口,域定层次。四域的定义、边界与次序见 `references/change-model.md`;一次改动混了两类时,按「写作 → 布局 → 排版 → 润色」拆开做,前一类落定并经检查再进下一类。
|
|
52
52
|
|
|
53
53
|
| 类型 | 典型请求 | 入口步骤 |
|
|
54
54
|
| --- | --- | --- |
|
|
@@ -63,9 +63,9 @@ metadata:
|
|
|
63
63
|
|
|
64
64
|
## 完成判据
|
|
65
65
|
|
|
66
|
-
- 第 1 步的行为句能被用户或测试验收;有阶段表时只覆盖对应行,没有时只覆盖用户点名的行为。
|
|
67
|
-
- 第 2 步的库 target 不依赖终端、GUI 或脚本;程序 target 只做装配和输入输出;共享抽象有第二个真实使用方。
|
|
68
|
-
- 第 3 步的公共头文件自包含,命名、声明顺序和异常声明符合 `references/` 的条文。
|
|
69
|
-
- 第 4 步的实现只兑现声明;资源由 RAII 管理;失败是正常结果时用返回值,函数无法完成任务时抛异常,不拿异常做正常分支。
|
|
70
|
-
- 源码注释符合 `references/comments.md` 的四节规范,`node "<skill-dir>/scripts/checking-comments.mjs" --root <项目根>` 无输出。
|
|
71
|
-
- 没有改 `CMakeLists.txt`、`.clang-*`、构建脚本,也没有把外部资料路径或项目专属类名写进通用条文。
|
|
66
|
+
- [ ] 第 1 步的行为句能被用户或测试验收;有阶段表时只覆盖对应行,没有时只覆盖用户点名的行为。
|
|
67
|
+
- [ ] 第 2 步的库 target 不依赖终端、GUI 或脚本;程序 target 只做装配和输入输出;共享抽象有第二个真实使用方。
|
|
68
|
+
- [ ] 第 3 步的公共头文件自包含,命名、声明顺序和异常声明符合 `references/` 的条文。
|
|
69
|
+
- [ ] 第 4 步的实现只兑现声明;资源由 RAII 管理;失败是正常结果时用返回值,函数无法完成任务时抛异常,不拿异常做正常分支。
|
|
70
|
+
- [ ] 源码注释符合 `references/comments.md` 的四节规范,`node "<skill-dir>/scripts/checking-comments.mjs" --root <项目根>` 无输出。
|
|
71
|
+
- [ ] 没有改 `CMakeLists.txt`、`.clang-*`、构建脚本,也没有把外部资料路径或项目专属类名写进通用条文。
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
```cpp
|
|
17
17
|
/// @file cell.h
|
|
18
|
-
/// @brief
|
|
18
|
+
/// @brief 格子相关类型定义。
|
|
19
19
|
///
|
|
20
|
-
///
|
|
20
|
+
/// 封装了格子的状态枚举 `CellState` 以及格子实体 `Cell`。
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
文件头只介绍这个文件里有什么,不写作者和变更史,也不把读者指去另一个文件。
|
|
@@ -65,7 +65,7 @@ class Board {
|
|
|
65
65
|
class Game {
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
纯数据结构和枚举只写一句名词短语;类有不变量,且第二段不是重复 `cells_`、`
|
|
68
|
+
纯数据结构和枚举只写一句名词短语;类有不变量,且第二段不是重复 `cells_`、`kRowCount` 这类领域尺寸时,才保留第二段。第二段直接写不变量,不加「限制:」这类标签。函数各自的契约下沉到函数注释,设计理由与分层写进书稿。
|
|
69
69
|
|
|
70
70
|
## 构造函数与析构函数:标签 + 职责
|
|
71
71
|
|
|
@@ -88,7 +88,7 @@ Board() = default;
|
|
|
88
88
|
virtual ~Ai();
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
`= default` 本身不说明成员被初始化成什么;`cells_{}` 把每个 `
|
|
91
|
+
`= default` 本身不说明成员被初始化成什么;`cells_{}` 把每个 `CellState` 初始化成 `kEmpty`,这条后置条件从签名上看不出来。标签说明这是哪一类特殊成员,冒号后写它建立的状态或承担的职责。
|
|
92
92
|
|
|
93
93
|
## 风格理由不进注释
|
|
94
94
|
|
|
@@ -96,31 +96,29 @@ virtual ~Ai();
|
|
|
96
96
|
|
|
97
97
|
```cpp
|
|
98
98
|
/// 检查坐标是否在范围内。函数名用 UpperCamel 而不是 snake_case,与标准库区分。
|
|
99
|
-
[[nodiscard]] static bool IsInRange(
|
|
99
|
+
[[nodiscard]] static bool IsInRange(Position position) noexcept;
|
|
100
100
|
|
|
101
101
|
/// 检查格子是否为空。函数名用 UpperCamel 而不是 snake_case,与标准库区分。
|
|
102
|
-
[[nodiscard]] bool IsCellEmpty(
|
|
102
|
+
[[nodiscard]] bool IsCellEmpty(Position position) const;
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
**好**(只写契约):
|
|
106
106
|
|
|
107
107
|
```cpp
|
|
108
108
|
/// 检查坐标是否落在棋盘下标区间内。
|
|
109
|
-
|
|
110
|
-
/// 只判断几何边界,不检查格子是否为空、对局是否已经结束。
|
|
111
|
-
[[nodiscard]] static bool IsInRange(CellPosition position) noexcept;
|
|
109
|
+
[[nodiscard]] static bool IsInRange(Position position) noexcept;
|
|
112
110
|
```
|
|
113
111
|
|
|
114
112
|
命名与风格约定写进项目文档和读者正文,注释里不解释「为什么用这种命名」。
|
|
115
113
|
|
|
116
|
-
##
|
|
114
|
+
## 第一句:结果、保证或检查项
|
|
117
115
|
|
|
118
116
|
**差**:
|
|
119
117
|
|
|
120
118
|
```cpp
|
|
121
119
|
/// 检查坐标。
|
|
122
120
|
/// 返回格子类型;越界会抛异常。
|
|
123
|
-
|
|
121
|
+
CellState GetState(Position position) const;
|
|
124
122
|
```
|
|
125
123
|
|
|
126
124
|
前两句都没有直接回答“合法时拿到什么、越界时发生什么”,读者要读完三句才拼出合同。
|
|
@@ -132,7 +130,7 @@ CellType GetCellType(CellPosition position) const;
|
|
|
132
130
|
///
|
|
133
131
|
/// @param position 行列坐标。
|
|
134
132
|
/// @throws std::out_of_range 坐标不在棋盘范围内。
|
|
135
|
-
|
|
133
|
+
CellState GetState(Position position) const;
|
|
136
134
|
```
|
|
137
135
|
|
|
138
136
|
第一句就是结果、失败方式或检查项;后面的标签只补参数与异常细节,不再重复这句话。
|
|
@@ -141,14 +139,14 @@ CellType GetCellType(CellPosition position) const;
|
|
|
141
139
|
|
|
142
140
|
```cpp
|
|
143
141
|
/// 比较两个坐标。
|
|
144
|
-
friend bool operator==(const
|
|
142
|
+
friend bool operator==(const Position&, const Position&) = default;
|
|
145
143
|
```
|
|
146
144
|
|
|
147
145
|
**好**:
|
|
148
146
|
|
|
149
147
|
```cpp
|
|
150
148
|
/// 检查两个坐标的行与列是否都相同。
|
|
151
|
-
friend bool operator==(const
|
|
149
|
+
friend bool operator==(const Position&, const Position&) = default;
|
|
152
150
|
```
|
|
153
151
|
|
|
154
152
|
布尔查询和 `operator==` 的第一句都用「检查…是否…」,让读者先看到被检查的条件,而不是一个同义反复的动词。
|
|
@@ -161,7 +159,7 @@ friend bool operator==(const CellPosition&, const CellPosition&) = default;
|
|
|
161
159
|
/// 尝试执行一次落子。
|
|
162
160
|
///
|
|
163
161
|
/// @param move 落子。
|
|
164
|
-
/// @returns 落子执行成功返回 kPlaced,被拒绝返回
|
|
162
|
+
/// @returns 落子执行成功返回 kPlaced,被拒绝返回 kOccupied。
|
|
165
163
|
[[nodiscard]] PlaceResult TryPlace(const Move& move);
|
|
166
164
|
```
|
|
167
165
|
|
|
@@ -175,18 +173,18 @@ friend bool operator==(const CellPosition&, const CellPosition&) = default;
|
|
|
175
173
|
/// 只有本局仍在进行、且落子玩家正是当前玩家时才继续,棋盘、轮次、历史三者一起更新。
|
|
176
174
|
///
|
|
177
175
|
/// @param move 待执行的落子,玩家必须是当前轮到的玩家。
|
|
178
|
-
/// @return 落子写入棋盘并换手时返回 kPlaced;对局已结束或上述任一条件不满足时返回
|
|
176
|
+
/// @return 落子写入棋盘并换手时返回 kPlaced;对局已结束或上述任一条件不满足时返回 kOccupied,
|
|
179
177
|
/// 棋盘和轮次都不变。
|
|
180
178
|
[[nodiscard]] PlaceResult TryPlace(const Move& move);
|
|
181
179
|
```
|
|
182
180
|
|
|
183
|
-
##
|
|
181
|
+
## 布尔查询只写检查什么
|
|
184
182
|
|
|
185
183
|
**差**:
|
|
186
184
|
|
|
187
185
|
```cpp
|
|
188
186
|
/// 检查指定坐标上的棋盘格子是否为空。
|
|
189
|
-
[[nodiscard]] bool IsCellEmpty(
|
|
187
|
+
[[nodiscard]] bool IsCellEmpty(Position position) const;
|
|
190
188
|
```
|
|
191
189
|
|
|
192
190
|
**好**:
|
|
@@ -194,15 +192,12 @@ friend bool operator==(const CellPosition&, const CellPosition&) = default;
|
|
|
194
192
|
```cpp
|
|
195
193
|
/// 检查指定坐标上的棋盘格子是否为空。
|
|
196
194
|
///
|
|
197
|
-
/// 只回答「这一格能不能落子」,不判断对局是否已经结束:对局结束后格子仍是空的,
|
|
198
|
-
/// 但再落子没有意义。是否需要先看对局状态由调用方决定,棋盘不替它下结论。
|
|
199
|
-
///
|
|
200
195
|
/// @param position 待检查的坐标。
|
|
201
196
|
/// @return 坐标在棋盘内且该格为空时返回 true;越界或该格已有棋子时返回 false。
|
|
202
|
-
[[nodiscard]] bool IsCellEmpty(
|
|
197
|
+
[[nodiscard]] bool IsCellEmpty(Position position) const;
|
|
203
198
|
```
|
|
204
199
|
|
|
205
|
-
|
|
200
|
+
布尔查询的第一句写「检查…是否…」,`@return` 写成立与不成立的条件;能力边界不写成否定清单,调用方要满足的条件用正向限定或 `@pre`。
|
|
206
201
|
|
|
207
202
|
## 枚举
|
|
208
203
|
|
|
@@ -235,7 +230,7 @@ enum class GameResult : std::uint8_t {
|
|
|
235
230
|
|
|
236
231
|
```cpp
|
|
237
232
|
Board board_; // 对局棋盘。
|
|
238
|
-
std::array<std::array<
|
|
233
|
+
std::array<std::array<CellState, kColumnCount>, kRowCount> cells_{}; // 存储棋盘格子。
|
|
239
234
|
```
|
|
240
235
|
|
|
241
236
|
前一句把名字翻译了一遍;后一句只复述声明,没有说清两层下标各是什么。
|
|
@@ -244,7 +239,7 @@ std::array<std::array<CellType, kBoardCols>, kBoardRows> cells_{}; // 存储棋
|
|
|
244
239
|
|
|
245
240
|
```cpp
|
|
246
241
|
/// 棋盘格子数组:外层下标是行,内层下标是列。
|
|
247
|
-
std::array<std::array<
|
|
242
|
+
std::array<std::array<CellState, kColumnCount>, kRowCount> cells_{};
|
|
248
243
|
/// 当前玩家:开局由 X 先手。
|
|
249
244
|
Player current_player_ = Player::kCross;
|
|
250
245
|
/// 对局结果:落子被拒时保持原值。
|
|
@@ -253,7 +248,7 @@ GameResult result_ = GameResult::kInProgress;
|
|
|
253
248
|
std::vector<Move> history_;
|
|
254
249
|
```
|
|
255
250
|
|
|
256
|
-
主注释写在声明上一行,类数据成员(含私有)统一用
|
|
251
|
+
主注释写在声明上一行,类数据成员(含私有)统一用 `///`;第一句先说是哪个成员,再用全角冒号补名字和类型看不出、只属于该成员的范围、轴向、哨兵与所有权。类级初值与状态迁移写在类注释,成员注释不重复。行尾不写补充;`///<` 只给枚举项和 `struct` 短字段。名字已经说明存放对象的 `board_` 不写「对局棋盘」。
|
|
257
252
|
|
|
258
253
|
## 完整声明:后置条件与轴向
|
|
259
254
|
|
|
@@ -268,7 +263,7 @@ class Board {
|
|
|
268
263
|
|
|
269
264
|
private:
|
|
270
265
|
/// 棋盘格子数组:外层下标是行,内层下标是列。
|
|
271
|
-
std::array<std::array<
|
|
266
|
+
std::array<std::array<CellState, kColumnCount>, kRowCount> cells_{};
|
|
272
267
|
};
|
|
273
268
|
```
|
|
274
269
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// 遍历所有空格
|
|
11
11
|
for (const auto& position : empty_cells) {
|
|
12
12
|
// 尝试落子
|
|
13
|
-
const bool wins = game.PreviewWin(position,
|
|
13
|
+
const bool wins = game.PreviewWin(position, state) == WinPreviewResult::kWouldWin;
|
|
14
14
|
// 如果能赢
|
|
15
15
|
if (wins) {
|
|
16
16
|
// 返回这一手
|
|
@@ -62,10 +62,10 @@ throw std::out_of_range{row, col};
|
|
|
62
62
|
|
|
63
63
|
```cpp
|
|
64
64
|
// 返回值:失败是可恢复结果,两种模式都返回 false。
|
|
65
|
-
[[nodiscard]] bool TryPlace(
|
|
65
|
+
[[nodiscard]] bool TryPlace(Position position, Player player) noexcept;
|
|
66
66
|
|
|
67
67
|
// 越界:两种模式都抛 std::out_of_range;Debug 消息可带 file:line。
|
|
68
|
-
void Place(
|
|
68
|
+
void Place(Position position, Player player);
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
越界用标准库已有的 `std::out_of_range`,调用方不需要认识项目私有类型;只有调用方必须把它和其它 `out_of_range` 区分开时,才定义派生类并在 `what()` 里补位置信息。
|
|
@@ -2,14 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## 基本规则
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
| --- | --- | --- |
|
|
7
|
-
| `class task_list` | `class TaskList` | 类型 PascalCase |
|
|
8
|
-
| `void add_task()` | `void AddTask()` | 函数 PascalCase |
|
|
9
|
-
| `int TaskCount` | `int task_count` | 变量 snake_case |
|
|
10
|
-
| `std::vector<Task> Tasks` | `std::vector<Task> tasks_` | 成员 snake_case + `_` |
|
|
11
|
-
| `constexpr int MAX = 10` | `constexpr int kMaxTasks = 10` | 常量 `k` 前缀 |
|
|
12
|
-
| `enum class Color { Red }` | `enum class Color { kRed }` | 枚举子 `k` 前缀 |
|
|
5
|
+
大小写、前缀与成员尾随 `_` 的规则表见 `references/naming.md`;下面只收需要语境才能判断的例子。
|
|
13
6
|
|
|
14
7
|
## 访问器
|
|
15
8
|
|
|
@@ -87,8 +87,8 @@ task_list/src/detail.h // 私有头,只被本组件包含
|
|
|
87
87
|
|
|
88
88
|
```cpp
|
|
89
89
|
// 差:用循环现写八条线,坐标藏在控制流里。
|
|
90
|
-
std::vector<std::vector<
|
|
91
|
-
std::vector<std::vector<
|
|
90
|
+
std::vector<std::vector<Position>> BuildLines() {
|
|
91
|
+
std::vector<std::vector<Position>> lines;
|
|
92
92
|
for (int row = 0; row < kRows; ++row) { /* ... */ }
|
|
93
93
|
// 列与两条对角线再各写一段
|
|
94
94
|
return lines;
|
|
@@ -99,7 +99,7 @@ std::vector<std::vector<CellPosition>> BuildLines() {
|
|
|
99
99
|
|
|
100
100
|
```cpp
|
|
101
101
|
// board.cpp 的 namespace tictactoe:行、列、对角线三组,坐标固定。
|
|
102
|
-
constexpr std::array<std::array<
|
|
102
|
+
constexpr std::array<std::array<Position, 3>, 8> kLines = {{
|
|
103
103
|
{{{0, 0}, {0, 1}, {0, 2}}}, // 行 0
|
|
104
104
|
{{{1, 0}, {1, 1}, {1, 2}}}, // 行 1
|
|
105
105
|
{{{2, 0}, {2, 1}, {2, 2}}}, // 行 2
|
|
@@ -119,31 +119,31 @@ constexpr std::array<std::array<CellPosition, 3>, 8> kLines = {{
|
|
|
119
119
|
|
|
120
120
|
```cpp
|
|
121
121
|
Board hypothetical = *this;
|
|
122
|
-
const
|
|
123
|
-
if (placed !=
|
|
122
|
+
const Cell::PlaceResult placed = hypothetical.Place(position, state);
|
|
123
|
+
if (placed != Cell::PlaceResult::kPlaced) {
|
|
124
124
|
return WinPreviewResult::kInvalidPlace;
|
|
125
125
|
}
|
|
126
|
-
return hypothetical.HasLine(
|
|
126
|
+
return hypothetical.HasLine(state) ? WinPreviewResult::kWouldWin
|
|
127
127
|
: WinPreviewResult::kWouldNotWin;
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
**好**:判断「这一格换成某种棋子后会不会成线」不需要第二份棋盘。越界、空格、已占用交给 `CheckPlaceable` 统一返回原因;通过之后只在包含该坐标的线上把这一格算作 `
|
|
130
|
+
**好**:判断「这一格换成某种棋子后会不会成线」不需要第二份棋盘。越界、空格、已占用交给 `CheckPlaceable` 统一返回原因;通过之后只在包含该坐标的线上把这一格算作 `state`,其余格子读原棋盘。
|
|
131
131
|
|
|
132
132
|
```cpp
|
|
133
|
-
WinPreviewResult Board::PreviewWin(
|
|
134
|
-
if (CheckPlaceable(position,
|
|
133
|
+
WinPreviewResult Board::PreviewWin(Position position, CellState state) const {
|
|
134
|
+
if (CheckPlaceable(position, state) != Cell::PlaceResult::kPlaced) {
|
|
135
135
|
return WinPreviewResult::kInvalidPlace;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
const auto matches = [&](
|
|
138
|
+
const auto matches = [&](Position cell) {
|
|
139
139
|
if (cell.row == position.row && cell.col == position.col) {
|
|
140
140
|
return true;
|
|
141
141
|
}
|
|
142
|
-
return cells_.at(cell.row).at(cell.col) ==
|
|
142
|
+
return cells_.at(cell.row).at(cell.col) == state;
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
const bool completes = std::ranges::any_of(kLines, [&](const auto& line) {
|
|
146
|
-
const bool contains = std::ranges::find_if(line, [&](
|
|
146
|
+
const bool contains = std::ranges::find_if(line, [&](Position cell) {
|
|
147
147
|
return cell.row == position.row && cell.col == position.col;
|
|
148
148
|
}) != line.end();
|
|
149
149
|
return contains && std::ranges::all_of(line, matches);
|