@chengzhao-dev/dsh-cpp-workflow 0.6.10 → 0.6.12

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.
Files changed (34) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +1 -1
  4. package/skills/building-cpp/assets/config/.clang-tidy +5 -3
  5. package/skills/building-cpp/assets/templates/projects/multi/CMakeLists.txt +1 -1
  6. package/skills/building-cpp/assets/templates/projects/multi/src/greeting.cpp +1 -1
  7. package/skills/building-cpp/assets/templates/projects/multi/src/main.cpp +1 -1
  8. package/skills/building-cpp/assets/templates/projects/shared-library/CMakeLists.txt +0 -1
  9. package/skills/building-cpp/assets/templates/projects/shared-library/greeting/CMakeLists.txt +1 -1
  10. package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/greeting.cpp +1 -1
  11. package/skills/building-cpp/assets/templates/projects/shared-library/src/main.cpp +1 -1
  12. package/skills/building-cpp/assets/templates/projects/static-library/CMakeLists.txt +0 -1
  13. package/skills/building-cpp/assets/templates/projects/static-library/greeting/CMakeLists.txt +1 -1
  14. package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/greeting.cpp +1 -1
  15. package/skills/building-cpp/assets/templates/projects/static-library/src/main.cpp +1 -1
  16. package/skills/building-cpp/examples/tooling.md +18 -0
  17. package/skills/building-cpp/references/standards/c++17/README.md +2 -1
  18. package/skills/building-cpp/references/standards/c++20/README.md +1 -0
  19. package/skills/configuring-cmake/references/cmake.md +2 -2
  20. package/skills/developing-cpp/examples/comments-declaration.md +5 -5
  21. package/skills/developing-cpp/examples/comments-implementation.md +14 -9
  22. package/skills/developing-cpp/examples/structure.md +1 -1
  23. package/skills/developing-cpp/references/comments.md +34 -22
  24. package/skills/developing-cpp/references/declaration-order.md +3 -2
  25. package/skills/developing-cpp/references/exceptions.md +1 -1
  26. package/skills/developing-cpp/references/file-template.md +3 -3
  27. package/skills/developing-cpp/references/google-cpp-style.md +1 -1
  28. package/skills/developing-cpp/references/layout.md +1 -1
  29. package/skills/developing-cpp/references/project-design.md +1 -0
  30. package/skills/developing-cpp/references/types.md +10 -6
  31. package/skills/developing-cpp/scripts/checking-comments.mjs +2 -8
  32. package/skills/developing-cpp/steps/02-boundary.md +1 -1
  33. package/skills/developing-cpp/steps/03-declaration.md +2 -2
  34. package/skills/developing-cpp/steps/04-implementation.md +5 -3
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.10
22
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.12
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.10
28
+ dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.12
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.10
22
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.12
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.10
28
+ dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.12
29
29
  ```
30
30
 
31
31
  桌面应用独占 `desktop` profile。命令行 `add` 被拒绝时,在应用设置里添加插件,地址填同一个 npm 版本。Git 标签安装使用仓库内已构建的 `lib/`,不在目标机器上重新构建;包内也不含需要授权执行的安装脚本。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chengzhao-dev/dsh-cpp-workflow",
3
- "version": "0.6.10",
3
+ "version": "0.6.12",
4
4
  "description": "写 C++ 源码、配 CMake、用 Clang 构建;头文件用 /// Doxygen 文档注释,函数体用 // 分段说明,文件头标签可在设置里改。",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -54,9 +54,11 @@ CheckOptions:
54
54
  value: CamelCase
55
55
  - key: readability-identifier-naming.GlobalConstantPrefix
56
56
  value: 'k'
57
+ # 局部常量按变量命名:Google 只给静态存储期的常量加 k 前缀。
57
58
  - key: readability-identifier-naming.LocalConstantCase
58
- value: CamelCase
59
- - key: readability-identifier-naming.LocalConstantPrefix
60
- value: 'k'
59
+ value: lower_case
61
60
  - key: readability-identifier-naming.MacroDefinitionCase
62
61
  value: UPPER_CASE
62
+ # 包含守卫按 Google 规则以 _H_ 结尾,UPPER_CASE 会把它判成多余下划线。
63
+ - key: readability-identifier-naming.MacroDefinitionIgnoredRegexp
64
+ value: '^[A-Z][A-Z0-9_]*_H_$'
@@ -10,7 +10,7 @@ add_executable(app
10
10
  src/main.cpp
11
11
  src/greeting.cpp
12
12
  )
13
- target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
13
+ target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
14
14
  target_compile_features(app PRIVATE cxx_std_23)
15
15
  set_target_properties(app PROPERTIES
16
16
  CXX_STANDARD 23
@@ -1,4 +1,4 @@
1
- #include "include/greeting.h"
1
+ #include "greeting.h"
2
2
 
3
3
  #include <string>
4
4
  #if __cplusplus >= 202002L
@@ -1,6 +1,6 @@
1
1
  #include <iostream>
2
2
 
3
- #include "include/greeting.h"
3
+ #include "greeting.h"
4
4
 
5
5
  int main() {
6
6
  std::cout << greeting::MakeGreeting("C++23") << '\n';
@@ -10,7 +10,6 @@ enable_testing()
10
10
  add_subdirectory(greeting)
11
11
 
12
12
  add_executable(app src/main.cpp)
13
- target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
14
13
  target_compile_features(app PRIVATE cxx_std_23)
15
14
  set_target_properties(app PROPERTIES
16
15
  CXX_STANDARD 23
@@ -1,5 +1,5 @@
1
1
  add_library(greeting SHARED src/greeting.cpp)
2
- target_include_directories(greeting PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
2
+ target_include_directories(greeting PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
3
3
  target_compile_features(greeting PUBLIC cxx_std_23)
4
4
  set_target_properties(greeting PROPERTIES
5
5
  CXX_STANDARD 23
@@ -1,4 +1,4 @@
1
- #include "greeting/include/greeting.h"
1
+ #include "greeting.h"
2
2
 
3
3
  #include <string>
4
4
  #if __cplusplus >= 202002L
@@ -1,6 +1,6 @@
1
1
  #include <iostream>
2
2
 
3
- #include "greeting/include/greeting.h"
3
+ #include "greeting.h"
4
4
 
5
5
  int main() {
6
6
  std::cout << greeting::MakeGreeting("C++23") << '\n';
@@ -10,7 +10,6 @@ enable_testing()
10
10
  add_subdirectory(greeting)
11
11
 
12
12
  add_executable(app src/main.cpp)
13
- target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
14
13
  target_compile_features(app PRIVATE cxx_std_23)
15
14
  set_target_properties(app PROPERTIES
16
15
  CXX_STANDARD 23
@@ -1,5 +1,5 @@
1
1
  add_library(greeting STATIC src/greeting.cpp)
2
- target_include_directories(greeting PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
2
+ target_include_directories(greeting PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
3
3
  target_compile_features(greeting PUBLIC cxx_std_23)
4
4
  set_target_properties(greeting PROPERTIES
5
5
  CXX_STANDARD 23
@@ -1,4 +1,4 @@
1
- #include "greeting/include/greeting.h"
1
+ #include "greeting.h"
2
2
 
3
3
  #include <string>
4
4
  #if __cplusplus >= 202002L
@@ -1,6 +1,6 @@
1
1
  #include <iostream>
2
2
 
3
- #include "greeting/include/greeting.h"
3
+ #include "greeting.h"
4
4
 
5
5
  int main() {
6
6
  std::cout << greeting::MakeGreeting("C++23") << '\n';
@@ -168,3 +168,21 @@ clang++ 18.1.8, clang-format 18.1.8, clang-tidy 18.1.8, clangd 18.1.8
168
168
  ```
169
169
 
170
170
  同主版本;`--live` 会核对。Apple Clang 与 Homebrew 工具不同版本时,仍按同一 LLVM 主版本要求处理,不要强行降级或改声明绕过。
171
+
172
+ ## 把标准差异当成检查项
173
+
174
+ **差**:
175
+
176
+ ```yaml
177
+ # .clang-tidy
178
+ Checks: 'google-*,misc-*'
179
+ ```
180
+
181
+ **好**:
182
+
183
+ ```yaml
184
+ # .clang-tidy
185
+ Checks: '-*,google-*,readability-identifier-naming'
186
+ ```
187
+
188
+ 能不能写 `= default` 的比较运算符由语言标准决定,不是检查项的问题。`misc-non-private-member-variables-in-classes` 只在打开 `misc-*` 时出现;工作流基线不开这一族,工程配置要与基线相同,而不是给这条检查加忽略选项。
@@ -28,6 +28,7 @@
28
28
 
29
29
  - 没有 concepts、ranges、`std::format`、`std::span`、`std::bit_cast`。
30
30
  - 没有 `std::print`、`std::expected`、`std::stacktrace`。
31
+ - 没有 `= default` 的比较运算符。
31
32
  - `std::filesystem` 在 GCC 8 需要额外链接 `stdc++fs`,GCC 9 起才稳定;不要把“语言核心达标”当成文件系统库也达标。
32
33
 
33
34
  ## CMake
@@ -42,7 +43,7 @@
42
43
 
43
44
  ## 降档/升级
44
45
 
45
- 从 C++20 降到这里时,把 concepts 改成 `enable_if`/重载,把 ranges 改成迭代器算法,把 `std::format` 改成 `std::ostringstream`。升级到 C++20 后先确认 `std::format` 的库门槛(libstdc++ 13+ / libc++ 14+),不要只看编译器版本。
46
+ 从 C++20 降到这里时,把 concepts 改成 `enable_if`/重载,把 ranges 改成迭代器算法,把 `std::format` 改成 `std::ostringstream`,把 `= default` 的比较运算符改回结构体外的手写自由函数。升级到 C++20 后先确认 `std::format` 的库门槛(libstdc++ 13+ / libc++ 14+),不要只看编译器版本。
46
47
 
47
48
  ## 验证
48
49
 
@@ -22,6 +22,7 @@
22
22
  - C++17 的全部内容。
23
23
  - concepts、requires、ranges、`std::span`、`std::bit_cast`、`std::source_location`。
24
24
  - `std::format`、协程、`std::jthread`、三路比较。
25
+ - 按成员逐项比较时写成 `bool operator==(const T&) const = default`。C++23 沿用,不另写一遍。
25
26
  - 模块语法(工具链支持不完整,模板不使用)。
26
27
 
27
28
  ## 本档不能假设
@@ -54,12 +54,12 @@ target_link_libraries(app PRIVATE greeting)
54
54
  ```cmake
55
55
  # greeting/CMakeLists.txt
56
56
  add_library(greeting STATIC src/greeting.cpp)
57
- target_include_directories(greeting PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
57
+ target_include_directories(greeting PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
58
58
  CompileSettings_SetCxxStandard(greeting PUBLIC c++23)
59
59
  CompileSettings_SetWarnings(greeting PRIVATE LEVEL standard)
60
60
  ```
61
61
 
62
- - 库的 `PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..` 让程序能从仓库根写 `#include "greeting/include/greeting.h"`,不需要程序再写 include 路径。
62
+ - 库的 `PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include` 让程序与链接它的测试写 `#include "greeting.h"` 就能找到公共头,不需要各自再写 include 路径。
63
63
  - 标准用 `CompileSettings_SetCxxStandard`:库用 `PUBLIC` 传播,程序用 `PRIVATE`;函数内部同时写 `target_compile_features` 与 `CXX_STANDARD` 三属性。
64
64
  - `CXX_STANDARD_REQUIRED ON` 防止 CMake 在编译器不支持时静默 decay;`CXX_EXTENSIONS OFF` 把 `-std=gnu++NN` 收成 `-std=c++NN`。configure 后检查 `compile_commands.json` 的最后一条 `-std=`。
65
65
  - 程序 target 固定 `app`;库 target 用组件名。
@@ -14,10 +14,10 @@
14
14
  **好**:
15
15
 
16
16
  ```cpp
17
- /// @file board.h
18
- /// @brief 定义井字棋棋盘、格子坐标与格子类型。
17
+ /// @file cell.h
18
+ /// @brief 定义格子状态与格子。
19
19
  ///
20
- /// 棋盘保存九个棋盘格子的状态,提供查询、落子与清空棋盘。
20
+ /// 包含格子状态 `CellState` 与格子 `Cell`。
21
21
  ```
22
22
 
23
23
  文件头只介绍这个文件里有什么,不写作者和变更史,也不把读者指去另一个文件。
@@ -56,7 +56,7 @@ class Board {
56
56
  class Board {
57
57
  ```
58
58
 
59
- **好**(类有不变量,第二句才写限制):
59
+ **好**(类有不变量,第二段直接写不变量):
60
60
 
61
61
  ```cpp
62
62
  /// 一局井字棋。
@@ -65,7 +65,7 @@ class Board {
65
65
  class Game {
66
66
  ```
67
67
 
68
- 纯数据结构和枚举只写一句名词短语;类有不变量,且第二句不是重复 `cells_`、`kBoardRows` 这类领域尺寸时,才保留第二段。函数各自的契约下沉到函数注释,设计理由与分层写进书稿。
68
+ 纯数据结构和枚举只写一句名词短语;类有不变量,且第二段不是重复 `cells_`、`kBoardRows` 这类领域尺寸时,才保留第二段。第二段直接写不变量,不加「限制:」这类标签。函数各自的契约下沉到函数注释,设计理由与分层写进书稿。
69
69
 
70
70
  ## 构造函数与析构函数:标签 + 职责
71
71
 
@@ -24,19 +24,24 @@ for (const auto& position : empty_cells) {
24
24
  **好**:
25
25
 
26
26
  ```cpp
27
- // 先找能立刻赢的格子:启发式只在「马上赢」和「马上挡」之间做选择,
28
- // 不在这里做更深的前瞻,深搜留给 MinimaxAi。
29
- for (const auto& position : empty_cells) {
30
- if (game.PreviewWin(position, CellType::kX) == WinPreviewResult::kWouldWin) {
31
- return position;
27
+ Cell::PlaceResult Cell::Place(CellState state) {
28
+ // 传入状态为 kEmpty,不更新格子状态。
29
+ if (state == CellState::kEmpty) {
30
+ return PlaceResult::kEmptyState;
32
31
  }
33
- }
34
32
 
35
- // 没有必胜格,交给下一层策略;这里不猜后续几步。
36
- return std::nullopt;
33
+ // 格子已有状态,不进行覆盖。
34
+ if (state_ != CellState::kEmpty) {
35
+ return PlaceResult::kOccupied;
36
+ }
37
+
38
+ // 格子状态为空,更新为传入的状态。
39
+ state_ = state;
40
+ return PlaceResult::kPlaced;
41
+ }
37
42
  ```
38
43
 
39
- 空行分开的阶段各一句,写这一阶段为什么停在这里。空行与注释的边界:
44
+ 每一支的上一行一句,写这一支的后果;`//` 里的枚举值不包反引号。空行与注释的边界:
40
45
 
41
46
  - 多条独立拒绝:每个 `if` 上一行一句,写拒绝后棋盘怎样。
42
47
  - 成功写入:上一行一句,写为什么到这里才改棋盘。
@@ -68,7 +68,7 @@ task_list/src/detail.h // 私有头,只被本组件包含
68
68
  ```
69
69
 
70
70
  ```cpp
71
- #include "task_list/include/task_list.h" // 从源码根写公共头的完整路径
71
+ #include "task_list.h" // 公共头按裸文件名包含
72
72
  ```
73
73
 
74
74
  构建时把仓库根加入 include 搜索路径;不要用 `#include "../task_list/include/task_list.h"` 这种随文件位置漂移的写法。
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## 写作:只写声明里看不出的事实
8
8
 
9
- - **第一句先写结果、限制或检查项**:类型用名词短语写它是什么;普通函数写正常返回什么、失败时返回什么或抛什么;布尔查询与 `operator==` 写「检查…是否…」,不写「返回 true/false」。条件、参数与实现理由放第二句以后。
9
+ - **第一句先写结果、限制或检查项**:类型用名词短语写它是什么;普通函数写正常返回什么、失败时返回什么或抛什么;布尔查询与 `operator==` 写「检查…是否…」。第一句不写「返回 true/false」;`@return` 仍要写明成立时返回 true、不成立时返回 false。条件、参数与实现理由放第二句以后。
10
10
  - 公共注释回答「这是什么」和「调用方必须遵守什么」;实现注释回答「为什么这样写」和「边界在哪里」。设计取舍、方案对比与演进历史写到书稿、README 或提交说明。
11
11
  - 用中文散文写完整句子,句号结尾;标识符、标准库类型、`@param` 这类标签保持原文。
12
12
  - 写之前三问:删掉这句,读者会误解设计或做错事吗?这句只是复述签名或代码吗?这个限制能不能由类型、`const` 或 `[[nodiscard]]` 表达?第一问为否就删,第二问为是就删,第三问为是就改代码。
@@ -17,7 +17,7 @@
17
17
 
18
18
  对象是类、结构体、枚举和类型别名。第一句是名词短语:类写「这个类型是什么」,纯数据结构和枚举写「这个取值表示什么」。
19
19
 
20
- 只有类有不变量,而且不重复领域尺寸时,才加第二段「限制」。纯数据结构和枚举不写第二句。
20
+ 只有类有不变量,且不重复领域尺寸时,才加第二段。第二段直接写不变量,不加「限制:」这类标签,也不写「这一层不负责什么」。纯数据结构和枚举不写第二句。
21
21
 
22
22
  ```cpp
23
23
  /// 井字棋棋盘。
@@ -32,20 +32,18 @@ class Board {
32
32
  - 不写尚未发生的用法、方案对比与取舍论证、广告句。
33
33
  - 纯数据结构不写「只保存…,不拥有…,不含…」这类否定式边界;这些由成员类型和所有权表达。
34
34
  - 领域尺寸不写进类型注释:格子数由 `cells_` 的声明表达,棋盘边长由 `kBoardRows`、`kBoardCols` 表达。
35
- - 枚举与只承载数据的结构体:第一句是名词短语,写这个取值表示什么;行尾 `///<` 的两种句式见「枚举项与 `struct` 短字段:两种句式」。
35
+ - 枚举与只承载数据的结构体:第一句是名词短语,写这个取值表示什么。状态枚举上一行写「格子的状态。」,结果枚举写「表示…结果。」;行尾 `///<` 的两种句式见「枚举项与 `struct` 短字段:两种句式」。
36
36
 
37
37
  ### 构造函数与析构函数:先写标签,再写职责
38
38
 
39
39
  特殊成员用标签开头:`默认构造函数:`、`拷贝构造函数:`、`移动构造函数:`、`析构函数:`;其他构造函数写 `构造函数:`。标签后写构造完成后的后置条件,或析构函数负责什么。`= default` 配合成员初始化器建立的不变量,签名上看不见,必须写成后置条件。只写读者能观察到的事实,不写「创建对象」「初始化成员」这类复述。
40
40
 
41
41
  ```cpp
42
- /// 默认构造函数:得到空棋盘,每个格子都是 kEmpty。
43
- ///
44
- /// 构造后的状态与调用 `Clear()` 相同,不抛异常。
45
- Board() noexcept;
42
+ /// 默认构造函数:每一格都是 `kEmpty`。
43
+ Board() = default;
46
44
  ```
47
45
 
48
- 默认构造、析构和 `void` 且无参数的函数写两段:第一段写后置条件或职责,第二段写调用方还要知道的边界;不写空的 `@param`、`@return`。有参数或有返回值的函数按下一节写标签,构造与析构不写 `@return`。标签说明这是哪一类特殊成员,不重复类名;非默认构造同样写建立的不变量与失败方式。
46
+ 默认构造与析构可以只写一行:标签后直接写后置条件或职责。`= default` 的后置条件写在标签那一行就够,不再另起一段写「不抛异常」,也不为了注释加 `noexcept`。无参数、无返回值的动作只在签名看不出结果时再写一句,不把「不抛异常」当成这句;不写 `@throws` 就表示不抛。不写空的 `@param`、`@return`。有参数或有返回值的函数按下一节写标签,构造与析构不写 `@return`。标签说明这是哪一类特殊成员,不重复类名;非默认构造同样写建立的不变量与失败方式。
49
47
 
50
48
  ### 数据成员:上一行 `///`,先写名词再写约定
51
49
 
@@ -66,10 +64,10 @@ GameResult result_ = GameResult::kInProgress;
66
64
 
67
65
  枚举项与只承载数据的 `struct` 短字段用行尾 `///<`,同一枚举各行的 `///<` 对齐。只写两种句式:
68
66
 
69
- - 语义本身已经完整:`///< 空格子。`
67
+ - 语义本身已经完整,只写一句:`///< 状态为空,尚未设置`
70
68
  - 语义之外还要补一条能核实的条件或结果:`///< 非法参数:传入空棋盘格子类型。`
71
69
 
72
- 冒号用全角 `:`;冒号前写类别,冒号后写说明,两侧都不能为空。禁止空冒号(`///< 落子成功:。`)、禁止用逗号把类别和说明连起来(`///< 传入空棋盘格子类型,非法参数。`)、禁止把说明写在类别前面。同一枚举要么每一项都写 `///<`,要么都不写;都写时两种句式可以混用。
70
+ 两类都不收句号。冒号用全角 `:`;冒号前写类别,冒号后写说明,两侧都不能为空。禁止空冒号(`///< 落子成功:。`)、禁止把说明写在类别前面(`///< 传入空棋盘格子类型,非法参数。`)。无冒号的行尾是一句完整的话,可以有句内逗号;有冒号时不得用逗号代替冒号。同一枚举要么每一项都写 `///<`,要么都不写;都写时两种句式可以混用。
73
71
 
74
72
  ### 函数:两段式契约
75
73
 
@@ -87,15 +85,17 @@ GameResult result_ = GameResult::kInProgress;
87
85
  [[nodiscard]] PlaceCellResult CheckPlaceable(CellPosition position, CellType cell_type) const;
88
86
  ```
89
87
 
90
- - 布尔查询与 `operator==`:第一句写「检查…是否…」;`@return` 只写「什么情况下为 true」,再补一句「不检查什么」。`Is`、`Has` 开头的名字与 `bool` 返回类型已经把成立条件说清时可省略 `@return`,不为了形式补一句同义反复。
88
+ - 布尔查询与 `operator==`:第一句写「检查…是否…」;`@return` 写成「成立时返回 true;不成立时返回 false」,不写「否则」省略后半句。`Is`、`Has` 开头的名字与 `bool` 返回类型已经把成立条件说清时可省略 `@return`,不为了形式补一句同义反复。
91
89
  - `@param` 每个参数都写,写范围、单位或所有权,不写「索引」这类同义反复;没有参数就不写空的 `@param`。
92
90
  - `@return` 默认写,写返回值怎么读或失败时返回什么;返回类型与函数名已把条件说清时可省略,不要用「见上」或重复第一段。
91
+ - 查询函数(`Get` 开头):第一句写「获取…」;`@return` 列出可能取到的值,枚举值在 `///` 里用反引号,例如 `kEmpty`、`kX` 或 `kO`。
92
+ - 更新函数:第一段写动作,第二段写清楚何时更新、什么时候不改;多个结果用 `@retval`,已变更的写在最前,其余按失败次序;标签组之间空一行,`@note` 写判断次序。
93
93
  - `@throws` 必须与 `references/exceptions.md` 一致;不抛异常的函数不写。
94
94
  - 私有函数同样写契约,不能默认调用者知道前置条件;输出参数标 `[out]`,既读又写标 `[in,out]`。
95
95
 
96
96
  ### 实现:阶段边界与为什么
97
97
 
98
- `.cpp` / `.cc` 里,函数体用 `//`,不用 `///`:体内的 `///` 会接到该函数的 Doxygen 详细说明后面,成为误导性文档。重要函数按空行分段,每一段上方写一句 `//`。句子写边界、失败后果或为什么停在这里,不把下一行条件翻译一遍;`IsFull` 这种单表达式函数不配注释。只写删掉会让下一次修改得到错误结果、而代码本身看不出的内容。
98
+ `.cpp` / `.cc` 里,函数体用 `//`,不用 `///`:体内的 `///` 会接到该函数的 Doxygen 详细说明后面,成为误导性文档。重要函数按空行分段,每一段上方写一句 `//`。句子写这一支的后果,可以点明分支处理了哪些枚举值,不把下一行条件逐词翻译一遍;`//` 里的枚举值不包反引号,反引号只留给声明注释;一句赋值或一个表达式不写注释,`IsFull` 这种单表达式函数也不配注释。只写删掉会让下一次修改得到错误结果、而代码本身看不出的内容。
99
99
 
100
100
  什么时候在 `if` 上方加一句:
101
101
 
@@ -121,10 +121,10 @@ GameResult result_ = GameResult::kInProgress;
121
121
 
122
122
  | 层 | 行数 | 第一句 | 写什么 |
123
123
  | --- | --- | --- | --- |
124
- | 文件头 | 3–7 | `@file` 文件名 + `@brief` 一句话 | 本文件由哪几部分组成 |
124
+ | 文件头 | 3–7(实现文件 2) | `@file` 文件名 + `@brief` 一句话 | 本文件由哪几部分组成 |
125
125
  | 类型注释 | 2–14 | 名词短语:这个类型或取值是什么 | 只有类的不变量 |
126
126
  | 特殊成员 | 1–3 | 标签(默认/拷贝/移动构造函数、析构函数) | 后置条件或职责 |
127
- | 函数注释 | 3–10 | 正常返回什么、失败时返回或抛什么、检查什么 | 每个参数、返回值与失败方式 |
127
+ | 函数注释 | 3–10(按标签数放宽) | 正常返回什么、失败时返回或抛什么、检查什么 | 每个参数、返回值与失败方式 |
128
128
  | 无标签函数 | 2 | 两段:后置条件/职责,再补边界 | 仅限构造、析构、`void` 无参数 |
129
129
  | 数据成员 | 上一行 1 句(`/// 名词:约定`) | 名字和类型看不出的范围、初值含义、轴向或哨兵 | 什么时候变 |
130
130
  | 实现注释 | 每个空行阶段 1 句 | 边界、失败后果或为什么停在这里 | `//`,只给重要函数 |
@@ -142,29 +142,29 @@ GameResult result_ = GameResult::kInProgress;
142
142
 
143
143
  ### 文件头
144
144
 
145
- 每个手写源文件开头写一段 doxygen 文件头:第一行写 `@file <文件名>`,第二行写 `@brief` 一句话说明这个文件定义或实现什么,用一行空注释隔开后,只写本文件自己的组成部分或边界。`@file` 的参数必须与文件名逐字相同,检查器会核对。`@brief` 始终写出;`@author`、`@date`、`@copyright`、`@version` 的开关出厂打开,只有填了非空值才写出,空值整条省略;作者、年份和变更史按团队约定填写,不填就不出现在源码里。删掉把读者指去另一个头文件、另一个目录或另一层的句子,也删掉「这一层不负责什么」。不写作者、版权年份、变更日志和外部资料路径。
145
+ 每个手写源文件开头写一段 doxygen 文件头:第一行写 `@file <文件名>`,第二行写 `@brief` 一句话说明这个文件定义或实现什么,用一行空注释隔开后,只写本文件自己的组成部分或边界。`@brief` 在头文件以「定义」起句,在实现文件以「实现」收句。空行后以「包含」起句,逐项写「中文概念 `类型名`」,顺序与声明一致;不写「文件包含」这类标签,也不写行为清单。`@file` 的参数必须与文件名逐字相同,检查器会核对。`@brief` 始终写出;`@author`、`@date`、`@copyright`、`@version` 的开关出厂打开,只有填了非空值才写出,空值整条省略;作者、年份和变更史按团队约定填写,不填就不出现在源码里。删掉把读者指去另一个头文件、另一个目录或另一层的句子,也删掉「这一层不负责什么」。不写作者、版权年份、变更日志和外部资料路径。
146
146
 
147
147
  ```cpp
148
- /// @file board.h
149
- /// @brief 定义井字棋棋盘、格子坐标与格子类型。
148
+ /// @file cell.h
149
+ /// @brief 定义格子状态与格子。
150
150
  ///
151
- /// 棋盘保存九个棋盘格子的状态,提供查询、落子与清空棋盘。
151
+ /// 包含格子状态 `CellState` 与格子 `Cell`。
152
152
  ```
153
153
 
154
154
  选择块注释时,同一段写成:
155
155
 
156
156
  ```cpp
157
157
  /**
158
- * @file board.h
159
- * @brief 定义井字棋棋盘、格子坐标与格子类型。
158
+ * @file cell.h
159
+ * @brief 定义格子状态与格子。
160
160
  *
161
- * 棋盘保存九个棋盘格子的状态,提供查询、落子与清空棋盘。
161
+ * 包含格子状态 `CellState` 与格子 `Cell`。
162
162
  */
163
163
  ```
164
164
 
165
165
  - C++ 源文件用以上格式;CMake 用 `#`,YAML/JSON 用 `#`。
166
166
  - 超过约 200 行的文件用空行分成几节,每节的说明写在一行 `///` 上,不画装饰线。
167
- - 文件头 3–7 行;`@file` 与 `@brief` 各占一行,后面只写本文件的范围与组成,不写实现步骤、作者和变更史。
167
+ - 文件头 3–7 行,实现文件可只有 `@file` 与 `@brief` 两行;`@file` 与 `@brief` 各占一行,后面只写本文件的范围与组成,不写实现步骤、作者和变更史。
168
168
 
169
169
  ## 排版:符号、宽度与标签
170
170
 
@@ -189,3 +189,15 @@ GameResult result_ = GameResult::kInProgress;
189
189
  - 只改注释时仍走第 3、4 步,然后回读:文件头 → 类型(名词短语 + 只有类的不变量)→ 特殊成员(标签 + 后置条件/职责)→ 函数(结果/失败方式/检查项 + 参数)→ 数据成员(只读上一行 `///`:名词 + 全角冒号 + 约定;行尾不应有注释)→ 实现(每个空行阶段的边界或理由)→ 术语与新符号是否真实存在 → 运行 `checking-comments.mjs` 兜底。
190
190
  - 注释写给维护者,正文写给读者;设计理由、方案比较与算法取舍写在正文或实现注释里,公开接口只留契约。
191
191
  - 禁止用注释代替类型、`const` 或 `[[nodiscard]]`。
192
+
193
+ ### 例:格子状态这一对文件的用词
194
+
195
+ `cell.h` 与 `cell.cpp` 用下面这套词,只约束这一对文件,不是全项目禁令:
196
+
197
+ - 说状态与格子状态,不说「取值」。
198
+ - 说尚未设置、状态为空、空白格子,不说「空格子」「尚未放置」「占据」。
199
+ - 说已设置状态、已有状态,不说「已放置」「有棋子」。
200
+ - 说更新,不说「写入」「放置」「落子」。
201
+ - 说未变更、已变更,不说「失败」「成功」。
202
+ - 说「不进行覆盖」「不做修改」「保持不变」;「不进行覆盖」只出现在 `.cpp`。
203
+ - 「设置」只管有没有状态,「更新」只管这次动作,「已有状态」只管拒绝覆盖,三者不互换。
@@ -45,8 +45,9 @@ class TaskTable {
45
45
 
46
46
  Google、LLVM、OpenCV 都没有规定这一组内部的次序;本工作流补上:
47
47
 
48
- - 谓词 `Is` / `Has` / `Can`,然后 `Get`,然后普通函数按使用先后,最后是 `friend` 函数。
49
- - `friend` 不放在这一组开头,也不单放在类的最前面,不强制放进 `private`。
48
+ - 开头是 `operator==`:成员默认比较和 `friend` 都放这里,然后才是谓词 `Is` / `Has` / `Can`、`Get`、普通函数按使用先后。`friend` 不再放到这一组末尾。
49
+ - 比较运算符的签名与 `[[nodiscard]]` 规则见 `references/types.md`,这里不另写一套。
50
+ - 同一类型上的谓词留在类内,不因为实现只转调公开函数就拆成类外自由函数。
50
51
  - `friend` 类型仍跟其他类型放在一起,和 `friend` 函数不是同一组。
51
52
 
52
53
  ## 命名空间作用域的头文件
@@ -81,7 +81,7 @@ int main() {
81
81
 
82
82
  ## 消息
83
83
 
84
- `what()` 是给程序员看的诊断文本,句式是「标签: 条件 (值); 限度」,四个槽位不动。标签是标识符,成员写 `Type::Function`,自由函数只写函数名,冒号后一个空格。先写清哪一步失败、失败条件是什么、手上有什么值,再写允许的限度;语言由项目约定,下面的示例用英文。条件不写 Error / Failed,不写空泛的「失败」;有分号时条件必须是带动词的小句,写 `board position is out of range`,不写无动词片段。括号只放导致失败的实际值,写 `name=value`,逗号后一个空格,等号两侧不空格;没有实际值就不写括号。分号后只放限度,且必须自成小句,如 `board is {}x{}`;没有限度就不写分号。
84
+ `what()` 是给程序员看的诊断文本,句式是「标签: 条件 (值); 限度」,四个槽位不动。标签是标识符,成员写 `Type::Function`,自由函数只写函数名,冒号后一个空格;默认参数用 `std::source_location::current()` 在调用点求值时,标签写 `function_name()`,条件、值与限度三槽不变。先写清哪一步失败、失败条件是什么、手上有什么值,再写允许的限度;语言由项目约定,下面的示例用英文。条件不写 Error / Failed,不写空泛的「失败」;有分号时条件必须是带动词的小句,写 `board position is out of range`,不写无动词片段。括号只放导致失败的实际值,写 `name=value`,逗号后一个空格,等号两侧不空格;没有实际值就不写括号。分号后只放限度,且必须自成小句,如 `board is {}x{}`;没有限度就不写分号。
85
85
 
86
86
  下面的英文示例只用 `:`、`()`、`,`、`=`、`;` 五个 ASCII 符号:冒号紧贴标签、后接一个空格;左括号前一个空格、内侧不空;逗号前不空、后一个空格;等号两侧不空;分号写在右括号外、前不空、后一个空格。不用第二个冒号、句号、问号、感叹号、破折号或 `×`;尺寸写 `3x3`,`out of range` 不加连字符。中文消息按中文标点,但槽位顺序和「值只放实际值」不变。
87
87
 
@@ -57,7 +57,7 @@
57
57
  #include <cstdint>
58
58
  #include <vector>
59
59
 
60
- #include "project/path/dependency.h"
60
+ #include "dependency.h"
61
61
 
62
62
  namespace project {
63
63
 
@@ -101,11 +101,11 @@ class ThingTable {
101
101
  /// @file thing_table.cpp
102
102
  /// @brief 实现 ThingTable 的添加与查询。
103
103
 
104
- #include "project/path/thing.h"
104
+ #include "thing.h"
105
105
 
106
106
  #include <algorithm>
107
107
 
108
- #include "project/path/other.h"
108
+ #include "other.h"
109
109
 
110
110
  namespace project {
111
111
 
@@ -16,7 +16,7 @@
16
16
  - 头文件必须自包含:自己用到的类型、宏、标准库头都直接包含,不依赖调用方的包含顺序。
17
17
  - `.cpp` 第一个包含自己的配对头,然后按下面的顺序包含。
18
18
  - 包含顺序:配对头 → C 系统头 → C++ 标准库头 → 其他库头 → 本项目头。每组之间空一行,组内按字母序;不要依赖 `clang-format` 的 `IncludeCategories` 自定义分组,本项目不再覆盖 Google 预设。
19
- - 本项目头用引号,路径从项目源码根开始,例如 `#include "base/logging.h"`。构建时把源码根加入 include 搜索路径,不要只把组件的 `include/` 设为根再写裸文件名。
19
+ - 本项目头用引号写裸文件名,例如 `#include "logging.h"`。构建时把该组件的 `include/` 加入 include 搜索路径(`target_include_directories(<target> PUBLIC .../include)`),不要写 `"base/logging.h"` 这种从源码根起的长路径。
20
20
  - 公共头文件直接放在组件的 `include/` 下;不要为了「整齐」再加一层命名空间目录。
21
21
  - 跨文件共享的常量用 `inline constexpr`,放在命名空间里并带 `k` 前缀;头文件里不要定义非 inline 的普通全局变量。
22
22
  - 头文件里只放模板、`constexpr`、类内联小函数和声明;普通函数的函数体放 `.cpp`。
@@ -15,7 +15,7 @@
15
15
  <name>_test.cpp 通过公共接口测试
16
16
  ```
17
17
 
18
- - 公共头直接放 `include/` 下;包含时从仓库根写完整路径,例如 `#include "task_list/include/task_list.h"`,不要写裸文件名或 `../`。
18
+ - 公共头直接放 `include/` 下,包含时写裸文件名,例如 `#include "task_list.h"`;不要写 `"task_list/include/task_list.h"` 这种从仓库根起的长路径,也不要写 `../` 相对路径。
19
19
  - 私有头放 `src/`,不进入公共 include 根;测试不得包含私有头。
20
20
  - 一个组件一个职责;组件名用 snake_case,和命名空间一致。
21
21
  - 组件之间通过公共头依赖;不要用 `../other/src/private.h` 跨组件包含私有实现。
@@ -26,6 +26,7 @@
26
26
  - 一次写入能区分的失败都放进结果枚举。传入空取值是结果项,不抛异常。
27
27
  - 只由某一个类型产生的结果枚举嵌在该类型里:格子的写入结果和对局的写入结果取值不同,不合成命名空间里的一个 `PlaceResult`。
28
28
  - 尺寸是某个类型的事实时,写成该类型的 `static constexpr`,例如 `Board::kRowCount`;不要再造一个只做 `static_cast<std::size_t>` 的别名常量。
29
+ - 一个局面的结果只用一个枚举和一个查询。不要把满盘、终局和赢家拆成三个入口,再由调用方拼回去。
29
30
 
30
31
  ## 封闭状态与结果
31
32
 
@@ -31,23 +31,25 @@ int CountWide(const std::vector<int>& values) {
31
31
  }
32
32
  ```
33
33
 
34
- 循环下标与容器大小都用 `std::size_t`,避免混合比较。业务编号另按下面的定宽约定。
34
+ 循环下标与容器大小都用 `std::size_t`,避免混合比较。业务编号与坐标另按下面的约定。
35
35
 
36
36
  ## 项目对整型角色的约定
37
37
 
38
38
  | 角色 | 类型 | 理由 |
39
39
  | --- | --- | --- |
40
- | 业务编号、尺寸、参与边界判定的下标 | `std::int32_t` | 能表达负值,避开 `size_t` 与负数比较的陷阱 |
41
- | 容器长度、`std::array` 长度、由编号换算的索引 | `std::size_t` | 与 STL 和 NTTP 一致 |
42
- | 封闭状态码的底层类型 | `std::uint8_t` | 只表达取值集合和布局,不参与算术 |
40
+ | 不能为负的尺寸、容器长度、`std::array` 长度 | `std::size_t` | 与 STL 和 NTTP 一致 |
41
+ | 坐标等允许表示范围外取值的下标 | `int` | 能表达负值;越界判断交给持有尺寸的类型 |
42
+ | 没有上面两种理由的整数 | `int` | 宽度足够,写起来最短 |
43
+ | 取值很少的 `enum class` 的底层类型 | `std::uint8_t` | 只表达取值集合和布局,不参与算术 |
43
44
 
45
+ - 协议、文件格式与跨平台布局仍用 `<cstdint>` 的定宽整数(如 `std::int32_t`);工程内普通代码不必写定宽类型。
44
46
  - 不用裸 `int` 承担跨平台协议或布局;它的宽度不可预测。
45
47
  - 不用 `std::uint8_t` / `std::uint16_t` 当业务编号;负值无法表达,整数提升还会制造意外。
46
48
  - 有符号乘法进入容器长度前先显式拓宽,否则会触发 `bugprone-implicit-widening-of-multiplication-result`:
47
49
 
48
50
  ```cpp
49
- inline constexpr std::int32_t kBatchSize = 3;
50
- inline constexpr std::int32_t kItemsPerBatch = 3;
51
+ inline constexpr int kBatchSize = 3;
52
+ inline constexpr int kItemsPerBatch = 3;
51
53
  inline constexpr std::size_t kItemCount =
52
54
  static_cast<std::size_t>(kBatchSize) * static_cast<std::size_t>(kItemsPerBatch);
53
55
  std::array<TaskState, kItemCount> items_{};
@@ -115,6 +117,7 @@ class TaskBoard {
115
117
  - 不给所有非 `void` 函数机械添加;标注的价值来自稀缺。
116
118
  - 写法位置:`[[nodiscard]]` 在返回类型前面,然后才是 `static` 或 `constexpr`;参数列表之后才是 `const`、`override`、`noexcept`。
117
119
  - 类内 `friend` 不加 `[[nodiscard]]`:Clang 拒绝 `[[nodiscard]] friend`、`friend [[nodiscard]]` 和参数列表后的 `[[nodiscard]]`。不要再猜下一种写法。
120
+ - 运算符不加 `[[nodiscard]]`,成员与 `friend` 都一样。比较的参数不具名:成员写 `bool operator==(const T&) const = default`,`friend` 写 `friend bool operator==(const T&, const T&);`;注释不给这个不具名参数写 `@param`。
118
121
  - 有意丢弃时写 `static_cast<void>(result)`,不要为此全局关闭警告。
119
122
  - 测试里验证“必须处理结果”时,同时验证忽略能被编译器发现。
120
123
 
@@ -129,6 +132,7 @@ class TaskBoard {
129
132
  | 无下标 | `set`、`unordered_set`、`multimap` | 无 `operator[]` / `at` | — |
130
133
 
131
134
  - 只在调用点已经证明下标合法时用 `[]`;否则用 `at` 或先做边界检查。坐标类型不拥有容器区间;区间判断属于持有尺寸的类型(如 `Board::IsInRange`)。`std::span` 在 C++20 没有 `at`,`span::at` 到 C++26 才进入标准。
135
+ - 已经证明非负的坐标在 `[]` 前收成 `std::size_t`:写 `static_cast<std::size_t>(index)`,不要先转成 `int`,也不要改用 `at()`。
132
136
  - `map::operator[]` 会插入,不是“读取可能缺失的键”;只读查询用 `find` / `contains` / `at`。
133
137
  - 契约失败的消息用 `std::format`,见 `references/exceptions.md`。
134
138
 
@@ -487,14 +487,8 @@ export function checkText(text, options = {}) {
487
487
  if (marker === -1) continue
488
488
  const content = lines[index].slice(marker + 4).trim().replace(/。$/, '').trim()
489
489
  const colon = content.indexOf(':')
490
- if (colon !== -1) {
491
- if (content.slice(colon + 1).trim() === '') {
492
- add(index + 1, 'enum-trailing-empty', '行尾注释冒号后为空:写「类别:说明」,冒号两侧都不能为空')
493
- }
494
- continue
495
- }
496
- if (/[,,]/.test(content)) {
497
- add(index + 1, 'enum-trailing-comma', '行尾注释用逗号连接类别与说明:改成「类别:说明」,类别在前')
490
+ if (colon !== -1 && content.slice(colon + 1).trim() === '') {
491
+ add(index + 1, 'enum-trailing-empty', '行尾注释冒号后为空:写「类别:说明」,冒号两侧都不能为空')
498
492
  }
499
493
  }
500
494
 
@@ -15,7 +15,7 @@ app 只做参数、输入输出和装配
15
15
 
16
16
  - 库 target 不依赖终端、GUI、网络脚本或全局单例;它只接收值、返回值、在无法完成任务时抛异常。
17
17
  - 程序 target 链接库,负责读参数、读标准输入、打印结果、把退出码翻译成进程退出码。
18
- - 公共头文件直接放在组件的 `include/` 下,从仓库根写完整路径包含,例如 `#include "task_list/include/task_list.h"`;不要写裸文件名或 `../` 相对路径。
18
+ - 公共头文件直接放在组件的 `include/` 下,包含时写裸文件名,例如 `#include "task_list.h"`;由构建把该目录加入 include 搜索路径,不要写从仓库根起的长路径或 `../` 相对路径。
19
19
  - 依赖方向从程序指向库。库不反向包含程序的 `cli.h`,也不引用 `main.cpp`。
20
20
  - 测试放 `tests/`,测试 target 链接被测库;测试不复制库的实现。
21
21
  - 只有一个可执行文件、没有复用需求时,可以不分库;一旦出现第二个调用方或测试需要直接调用,再拆库。
@@ -14,7 +14,7 @@
14
14
  - 头文件只就地定义大约 10 行以内的短函数;更长或需要完整定义的普通函数只在 `.cpp` 定义,模板和 `constexpr` 例外。
15
15
  - 失败是调用方的正常结果时,用结果类型、`bool` + 输出参数或 `std::optional` 表达;函数无法完成自己的任务、或构造函数无法建立 invariant 时,才在声明注释里写清抛出的异常类型;越界用 `std::out_of_range`,前置条件被破坏用 `std::logic_error`,只有调用方必须区分时才新增派生类型;Debug/Release 的抛出条件相同。消息句式见 `references/exceptions.md` 的「消息」节。
16
16
  - 一次操作有多种必须区分的正常结果时,用 `enum class` 返回(如落子:成功、越界、类型非法、已占),不用 `bool` 把原因压掉;成功还需要附带数据时再考虑 `std::expected` 或结果结构,成功无数据时不用。纯查询谓词仍用 `bool`,不要把 `IsEmpty` 改成三态。
17
- - 声明注释按 `references/comments.md` 的四节写:写作只写声明里看不出的事实——类型注释的名词短语(纯数据与枚举一句,类只在不变量不重复领域尺寸时加第二段)、特殊成员的标签与后置条件(例如 `默认构造函数:得到空棋盘、每个格子都是 kEmpty`)、函数注释的第一句结果或失败方式或检查项与契约。有参数就每个参数一条 `@param`,有返回值默认写 `@return`(成立条件自明时可省),会抛才写 `@throws`;构造、析构和 `void` 无参数函数写两段后置条件,不写空标签。数据成员的主注释放在声明上一行并用 `///`,先说明这个成员是什么,再用全角冒号补名字和类型看不出的轴向、哨兵、所有权与改写时机,行尾不写补充;布局定文件头、位置与行尾;排版定符号、宽度与标签;润色删复述。类型注释不罗列成员功能、不写领域尺寸、不写方案对比与设计论证;全项目的风格理由写进项目文档,不写进注释。
17
+ - 声明注释按 `references/comments.md` 的四节写:写作只写声明里看不出的事实——类型注释的名词短语(纯数据与枚举一句,类只在不变量不重复领域尺寸时加第二段)、特殊成员的标签与后置条件(默认构造只写一行,如 `默认构造函数:格子的初始状态为 kEmpty`)、函数注释的第一句结果或失败方式或检查项与契约。有参数就每个参数一条 `@param`,有返回值默认写 `@return`(成立条件自明时可省),会抛才写 `@throws`;标签与句号规则以 `references/comments.md` 为准,不写空标签。数据成员的主注释放在声明上一行并用 `///`,先说明这个成员是什么,再用全角冒号补名字和类型看不出的轴向、哨兵、所有权与改写时机,行尾不写补充;布局定文件头、位置与行尾;排版定符号、宽度与标签;润色删复述。类型注释不罗列成员功能、不写领域尺寸、不写方案对比与设计论证;全项目的风格理由写进项目文档,不写进注释。
18
18
  - `std::format` 用于组合消息;声明里不要承诺 `std::print`(C++23)。
19
19
 
20
20
  ## 情况
@@ -25,7 +25,7 @@
25
25
 
26
26
  **异常声明**:构造函数、`Get` 类查询、解析函数最容易抛;在声明注释里写「当……时抛出 `std::out_of_range`」这样的具体类型,不要只写「可能抛异常」,也不要给每个参数错误都新建类型。类型选择见 `references/exceptions.md`。
27
27
 
28
- **注释**:声明注释用 `///` 写中文散文,只写调用方需要知道的;实现细节留给 `.cpp`。`@param` 每个参数都写,`@return` 每个非 `void` 函数都写,`@throws` 会抛才写;构造、析构与 `void` 无参数函数不写空标签。标识符和标签保持英文。文件头默认写 `@file` 与 `@brief` 两行,`@file` 必须与文件名相同;作者、日期、版权、版本开关出厂打开,填了非空值才写出。标签开关在插件设置页,技能正文末尾附带当前配置的快照。写完运行 `node "<skill-dir>/scripts/checking-comments.mjs" --root <项目根>`;关闭 `@brief` 时加 `--no-brief`,配置里打开了隐私标签并填了值时加 `--allow-tag <标签>`。文件头、标签、参数完整性、成员清单与设计论证由脚本兜底。文件头与 TODO 的格式见 `references/comments.md`。
28
+ **注释**:声明注释用 `///` 写中文散文,只写调用方需要知道的;实现细节留给 `.cpp`。`@param` 每个参数都写,`@return` 每个非 `void` 函数都写,`@throws` 会抛才写;构造、析构与 `void` 无参数函数不写空标签。标识符和标签保持英文。文件头写 `@file`、`@brief` 与一句「包含…」的范围,实现文件可只写前两行;`@file` 必须与文件名相同;作者、日期、版权、版本开关出厂打开,填了非空值才写出。标签开关在插件设置页,技能正文末尾附带当前配置的快照。写完运行 `node "<skill-dir>/scripts/checking-comments.mjs" --root <项目根>`;关闭 `@brief` 时加 `--no-brief`,配置里打开了隐私标签并填了值时加 `--allow-tag <标签>`。文件头、标签、参数完整性、成员清单与设计论证由脚本兜底。文件头与 TODO 的格式见 `references/comments.md`。
29
29
 
30
30
  ## 正反例指针
31
31
 
@@ -9,7 +9,7 @@
9
9
  - 函数保持短小;一个函数只做一件事。超过约 40 行时先找可以命名的中间步骤,不要用注释把长函数切成几段。
10
10
  - 按「准备 → 操作 → 收尾」排列语句,用空行分组;组与组之间的空行表达阶段,不用大段叙述。
11
11
  - `.cpp` 的第一个 include 是自己的配对头,然后直接包含本文件用到的标准库头;不要依赖公共头替本文件转发声明。包含顺序见 `references/google-cpp-style.md`;文件槽位见 `references/file-template.md`。
12
- - `.cpp` / `.cc` 的函数体用 `//`,不用 `///`:体内 `///` 会接到该函数的 Doxygen 详细说明后面。重要函数按空行分阶段,每个阶段上方写一句 `//`,写边界、失败后果或为什么停在这里,不把下一行的条件翻译一遍。`IsFull` 这种单表达式函数不配注释;只写删掉会让下一次修改得到错误结果、而代码本身看不出的内容,变更史留给提交说明。
12
+ - `.cpp` / `.cc` 的函数体用 `//`,不用 `///`:体内 `///` 会接到该函数的 Doxygen 详细说明后面。重要函数按空行分阶段,每个阶段上方写一句 `//`,写这一支的后果,可以点明分支处理了哪些枚举值,不把下一行的条件逐词翻译一遍;`//` 里的枚举值不包反引号。`IsFull` 这种单表达式函数不配注释;只写删掉会让下一次修改得到错误结果、而代码本身看不出的内容,变更史留给提交说明。
13
13
  - 什么时候在 `if` 上方加一句:多条独立拒绝(`CheckPlaceable`)每个 `if` 一句;成功写入前一句;只有次序依赖的一对 `if` 在第一个上方说明;循环上方已有阶段说明时,循环里的 `if` 不再加,单表达式谓词不加。
14
14
  - 错误处理:失败是调用方正常结果时返回结果类型、`bool` 或 `std::optional`;函数无法完成自己的任务时抛异常,不拿异常做正常分支。Debug 与 Release 用同一套返回值与抛出条件,只有诊断详略不同。两种模式都要守的 invariant 用 `Check`(项目保留时),只在开发期做的自检用 `DebugCheck`;没有这两个宏就分别用 `assert` 与 `std::terminate`。捕获时按 `const` 引用,只处理能处理的类型,其余继续抛。消息按 `Type::Function: 条件 (值); 限度` 组织,见 `references/exceptions.md` 的「消息」节。
15
15
  - 声明 `noexcept` 前核对函数体:出现 `at()` 或其他可能抛出的调用就去掉。越界用 `at()` 抛 `std::out_of_range`,这个函数就不能标 `noexcept`。
@@ -17,8 +17,8 @@
17
17
  - 不写 C 风格强制转换;用 `static_cast` / `std::bit_cast` 等具名转换。
18
18
  - 所有权用 `std::unique_ptr`;没有共享所有权的理由时不用 `std::shared_ptr`。
19
19
  - 只改注释时,不改可观察行为;只改实现时,不改公共声明。两者同时需要时,先改声明,再改实现。
20
- - 清空成员数组:标准支持 ranges 时用 `std::ranges::fill(cells_ | std::views::join, CellType::kEmpty)`;不支持时用 `for` 循环逐格赋 `kEmpty`。两种都不写 `cells_ = {}`,那依赖枚举零值恰好是 `kEmpty`。
21
- - 默认构造只做「建立初值」且与 `Clear()` 同义时,构造函数调用 `Clear()`,不另设 `Init()`;类内 `cells_{}` 仍保留,让成员先有确定初值。
20
+ - 清空成员数组:元素类型提供收回空取值的函数时(如 `Cell::Clear`),容器的 `Clear` 逐个调用它;没有这种函数时才用 `std::ranges::fill(cells_ | std::views::join, kEmpty)` 或 `for` 循环逐格赋空值。两种都不写 `cells_ = {}`,也不从外面把枚举赋成空值。
21
+ - 默认构造只做「建立初值」时用成员初值(如 `cells_{}`),不在构造函数里再调一遍 `Clear()`;`Clear()` 只负责把已经写入的元素收回空。
22
22
 
23
23
  ## 情况
24
24
 
@@ -37,6 +37,8 @@
37
37
  ## 正反例指针
38
38
 
39
39
  - 实现注释复述代码、分组混乱:`examples/comments-implementation.md`「实现注释」。
40
+ - 已经证明非负的坐标把下标收成 `std::size_t` 再 `[]`,不要转成 `int`:`references/types.md`「下标与越界」。
41
+ - 越界消息的标签取自调用点:`references/exceptions.md`「消息」的 `std::source_location` 一句。
40
42
  - 为了保住 `noexcept` 再包一层只转发下标的私有函数:`references/exceptions.md`「不抛的边界」。
41
43
  - 异常类型与结果类型混用:`examples/exceptions.md`「什么时候抛」。
42
44
  - `std::format` 与流输出混用得当:`examples/exceptions.md`「异常类型与消息」。