@chengzhao-dev/dsh-cpp-workflow 0.2.0 → 0.3.1

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 (49) hide show
  1. package/README.en.md +7 -5
  2. package/README.md +7 -5
  3. package/lib/workspace.js +1 -56
  4. package/package.json +3 -2
  5. package/skills/building-cpp/SKILL.md +4 -2
  6. package/skills/building-cpp/assets/config/.clang-format +2 -0
  7. package/skills/building-cpp/assets/config/.clang-tidy +1 -0
  8. package/skills/building-cpp/assets/templates/projects/multi/CMakeLists.txt +2 -2
  9. package/skills/building-cpp/assets/templates/projects/shared-library/CMakeLists.txt +1 -1
  10. package/skills/building-cpp/assets/templates/projects/shared-library/greeting/CMakeLists.txt +1 -1
  11. package/skills/building-cpp/assets/templates/projects/single/CMakeLists.txt +1 -1
  12. package/skills/building-cpp/assets/templates/projects/static-library/CMakeLists.txt +1 -1
  13. package/skills/building-cpp/assets/templates/projects/static-library/greeting/CMakeLists.txt +1 -1
  14. package/skills/building-cpp/examples/cmake.md +15 -15
  15. package/skills/building-cpp/examples/tooling.md +3 -3
  16. package/skills/building-cpp/references/cmake.md +6 -6
  17. package/skills/building-cpp/references/encoding.md +49 -0
  18. package/skills/building-cpp/references/linking.md +6 -6
  19. package/skills/building-cpp/references/standards/c++23/README.md +1 -1
  20. package/skills/building-cpp/references/static-analysis.md +3 -3
  21. package/skills/building-cpp/scripts/checking-encoding.mjs +241 -0
  22. package/skills/building-cpp/steps/01-build.md +1 -1
  23. package/skills/building-cpp/steps/02-tooling.md +2 -2
  24. package/skills/building-cpp/steps/03-check.md +8 -6
  25. package/skills/developing-cpp/SKILL.md +19 -3
  26. package/skills/developing-cpp/examples/comments.md +161 -119
  27. package/skills/developing-cpp/examples/structure.md +4 -4
  28. package/skills/developing-cpp/references/comments.md +158 -69
  29. package/skills/developing-cpp/references/declaration-order.md +4 -2
  30. package/skills/developing-cpp/references/file-template.md +131 -0
  31. package/skills/developing-cpp/references/glossary.md +3 -3
  32. package/skills/developing-cpp/references/google-cpp-style.md +8 -8
  33. package/skills/developing-cpp/references/language.md +3 -3
  34. package/skills/developing-cpp/references/layout.md +21 -12
  35. package/skills/developing-cpp/references/naming.md +11 -2
  36. package/skills/developing-cpp/references/project-design.md +10 -1
  37. package/skills/developing-cpp/references/standard-library.md +1 -1
  38. package/skills/developing-cpp/scripts/checking-comments.mjs +316 -0
  39. package/skills/developing-cpp/steps/01-behavior.md +1 -1
  40. package/skills/developing-cpp/steps/02-boundary.md +5 -4
  41. package/skills/developing-cpp/steps/03-declaration.md +6 -6
  42. package/skills/developing-cpp/steps/04-implementation.md +3 -3
  43. /package/skills/building-cpp/assets/templates/projects/multi/src/{greeting.cc → greeting.cpp} +0 -0
  44. /package/skills/building-cpp/assets/templates/projects/multi/src/{main.cc → main.cpp} +0 -0
  45. /package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/{greeting.cc → greeting.cpp} +0 -0
  46. /package/skills/building-cpp/assets/templates/projects/shared-library/src/{main.cc → main.cpp} +0 -0
  47. /package/skills/building-cpp/assets/templates/projects/single/src/{main.cc → main.cpp} +0 -0
  48. /package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/{greeting.cc → greeting.cpp} +0 -0
  49. /package/skills/building-cpp/assets/templates/projects/static-library/src/{main.cc → main.cpp} +0 -0
@@ -1,102 +1,169 @@
1
1
  # 注释
2
2
 
3
- 注释分四层:文件头、章节横幅、声明注释、实现注释。配置文件的注释单独一层。每层只写该层该管的事,不互相复制。
3
+ 注释分四层:文件头、类型注释、函数注释、实现注释;配置文件的注释另算。层级越靠外,越讲概念与契约;越靠里,越讲实现理由。同一件事只写一次:外层写过,内层就不再重复。
4
+
5
+ ## 总则
6
+
7
+ - 注释回答「这是什么」和「调用方必须遵守什么」,不回答「为什么这样设计」。设计取舍、方案对比与演进历史写到书稿、README 或提交说明。
8
+ - 用中文散文写完整句子,句号结尾;标识符、标准库类型、`@param` 这类标签保持原文。
9
+ - 篇幅由内容决定:一句话说得清就写一行,有使用约定就把约定写完整。不为了短砍掉契约,也不为了长凑行数。
10
+ - 行宽跟项目 `.clang-format` 的 `ColumnLimit` 走(Google 基线是 80 列,中文按两列计),超了就换行;横幅与列宽同宽,不另设一套数字。
11
+ - 写之前做三问:删掉这句,读者会误解设计或做错事吗?这句只是复述签名或代码吗?这个限制能不能由类型、`const` 或 `[[nodiscard]]` 表达?第一问为否就删,第二问为是就删,第三问为是就改代码,不写注释。
12
+ - 命名、分文件、`enum class`、异常与结果类型这类全项目风格约定只写进项目文档与读者正文;注释不替项目解释风格,也不写「用 X 而不是 Y」的理由。
13
+
14
+ ## `///` 与 `//` 的分工
15
+
16
+ - `///` 只给声明:头文件里的类型、函数、枚举值与公开数据成员(行尾补一句用 `///<`)。调用方和 Doxygen 从这里取契约。
17
+ - `//` 给实现:函数体内的步骤与边界说明、局部变量、文件头横幅、配置文件的说明。写给人读,不进文档索引。
18
+ - 不给局部变量和函数体里的语句写 `///`;文件头也不写成 `///`,否则编辑器会把它挂到紧随其后的第一个声明上。
19
+ - 判断只看一句话写的是「调用方要遵守的承诺」还是「维护者要知道的写法」:前者用 `///`,后者用 `//`。主流指南一致:Doxygen 只为文档化的声明生成条目,Google 与 LLVM 都在声明上用 `///`、在函数体内用 `//`。
20
+
21
+ ## 长度预算
22
+
23
+ | 层 | 行数 | 写什么 |
24
+ | --- | --- | --- |
25
+ | 文件头 | 3–6 | 这个文件是什么、由哪几部分组成、不负责什么 |
26
+ | 类型注释 | 6–14 | 概念、使用约定 |
27
+ | 函数注释 | 3–10 | 承诺、参数与失败方式 |
28
+ | 数据成员 | 行尾 1 句 | 存什么、范围或单位、初值的含义 |
29
+ | 实现注释 | 1–3 | 为什么这样写、边界、代价 |
30
+
31
+ 行数只是防止失控的锚点,不是配额。内容不够就少写,像「检查棋盘是否已满」这种名字已经说清的,一句契约即可。
4
32
 
5
33
  ## 文件头
6
34
 
7
- 每个手写源文件、脚本、配置文件开头写一段文件头:这个文件负责什么、边界在哪里、什么时候需要读它。不要写版权年份、作者、变更日志。
35
+ 每个手写源文件开头写一段文件头:这个文件负责什么、分成哪几块、不负责什么。不要写作者、版权年份、变更日志和外部资料路径。
8
36
 
9
37
  ```cpp
10
38
  //===----------------------------------------------------------------------===//
11
- // TaskTable 的存储、编号检查与状态更新规则。
39
+ // board.h - 定义井字棋棋盘。
12
40
  //
13
- // 不处理终端输入输出;程序层负责把用户输入翻译成 TaskId。
41
+ // 棋盘只保存格子状态与规则判断,不含对局流程;对局流程在 game.h。
14
42
  //===----------------------------------------------------------------------===//
15
43
  ```
16
44
 
17
- - C++ 文件用 `//`,CMake 用 `#`,YAML/JSON 用 `#` 或 `//`(按文件类型)。
18
- - 文件头控制在 6 行以内;不写实现步骤,不写外部资料路径或项目专属类名。
19
- - 生成文件、第三方文件、纯数据文件可以不写文件头。
45
+ - C++ 用 `//`,CMake 用 `#`,YAML/JSON 用 `#` 或 `//`。
46
+ - 横幅宽度等于 `ColumnLimit`:`//===` 起头、`===//` 收尾,中间补 `-`。章节横幅同一格式,宽度取当前 `ColumnLimit`(Google 基线 80)。
47
+ - 超过约 200 行的文件用章节横幅分组,组名是读者要跳转的锚点;一个文件不超过 8 个横幅。列宽随项目改,横幅跟着改一次,全文一致。
48
+ - 生成文件、第三方文件、纯数据文件不写文件头。
49
+
50
+ ## 类型注释
51
+
52
+ 对象是类、结构体、枚举和类型别名。写两段,段间空一行:
20
53
 
21
- ## 章节横幅
54
+ 1. 概念:它在领域里表示什么,读者能对上棋规、词义或界面。
55
+ 2. 约定:本类型自己保证什么、调用方必须遵守什么(不变量、取值范围、失败方式、所有权与生命周期)。
22
56
 
23
- 长文件(超过约 200 行)用横幅分组,组名是读者要跳转的锚点。
57
+ 不写设计段落:为什么这样切分职责、为什么不选另一种写法,属于书稿的设计讨论,注释里不出现。分层说明放文件头,类注释只讲本类型。
24
58
 
25
59
  ```cpp
26
- //===----------------------------------------------------------------------===//
27
- // 构造与生命周期
28
- //===----------------------------------------------------------------------===//
60
+ /// 井字棋棋盘。
61
+ ///
62
+ /// 3×3 个格子,每格取空、X 或 O;格子数量不会改变,落子只改变单个格子的取值。
63
+ class Board {
29
64
  ```
30
65
 
31
- - 横幅宽度固定 80 列;`//` 后 3 个 `=`,中间 70 个 `-`,末尾 `===//`。
32
- - 横幅只包住逻辑组,不包住单个函数;函数之间用空行,不用横幅。
33
- - 脚本里用 `#===` 形式,宽度同样 80 列;CMake 用 `#---` 小节即可,不用重横幅。
34
- - 一个文件不超过 8 个横幅;超过说明文件该拆了。
35
- - 80 列换行分三类:整句在标点处断;参数/返回值列表每项一行;代码示例保持原样,宁可横向滚动也不在半句断开。
66
+ 判据:删掉这句,读者会不会做错事、或把它用到别的类型上?读签名、`const`、`[[nodiscard]]` 或成员类型就知道的事,写出来只是重复一遍,删。
67
+
68
+ 按下面的次序取内容,够了就停,不必每段都凑满:
69
+
70
+ 1. 一句话职责:这个类型是什么、给谁用。
71
+ 2. 使用约定:什么时候该用它、失败与前置条件是什么。
72
+ 3. 非显然保证:所有权、生命周期、异常保证、复杂度;签名里看得出的不写。
73
+ 4. 线程规则:只有实例会被多个线程访问时才写,写清哪些操作可以并发、哪些要调用方自己同步。
74
+ 5. 不变式:几条始终成立的条件;能写成 `assert` 或 `static_assert` 的,写断言而不是注释。
75
+
76
+ 禁止事项:
77
+
78
+ - 不写成员清单。`提供检查坐标区间、检查棋盘格子是否为空、……的功能` 这类句子会把接口抄一遍,接口一改就过期,而且没告诉读者怎么用。函数各自的契约写在函数上。
79
+ - 不按声明顺序复述一遍接口,不复制函数注释。
80
+ - 不写跨类分工。`谁该落子、落子被拒后怎么提示留在 Game` 说的是另一个类型,读者既没法验证,重构一次就过期;边界靠类型与所有权表达,注释只写本类型保证什么。
81
+ - 不写类型系统已经表达的性质。`值语义,可以自由拷贝`、`所有查询都是 const` 由类声明与 `const` 直接给出,不写。类型系统表达不了的保证才值得写:深拷贝不共享可变状态、只读并发安全、异常安全等级,而且要能被测试或断言验证。
82
+ - 不写尚未发生的用法。`对局和搜索可以交替读取同一棋盘` 描述的是以后可能出现的调用方;注释只写当前契约,新用法落地时再补它必须遵守的约定。
83
+ - 不写方案对比与取舍论证:`用 X 而不是 Y`、`比……更……`、`复制的代价可以忽略`、`只留一份实现` 这类句子属于设计讨论,不进注释。
84
+ - 不写「本类使用快速排序所以很快」这种广告句;要写清楚代价与适用规模。
85
+
86
+ 枚举与只承载数据的结构体可以短一些:概念一段,`///<` 先写这个取值表示什么,需要时用冒号补一小句读者能核实的补充(条件、动作或结果);两段都要短,不重复名字,不写「因为……所以……」的长句。
87
+
88
+ ```cpp
89
+ /// 对局结果。
90
+ enum class GameResult : std::uint8_t {
91
+ kInProgress = 0, ///< 游戏进行中:还可以落子。
92
+ kCrossWin = 1, ///< X 获胜:达成三连。
93
+ kNoughtWin = 2, ///< O 获胜:达成三连。
94
+ kDraw = 3, ///< 平局:棋盘已满,双方都没有三连。
95
+ };
96
+ ```
97
+
98
+ 冒号后的补充只要是读者扫一眼就能核实的条件就够了;要展开判定过程或推导,留给函数实现注释或正文。
99
+
100
+ ## 函数注释
36
101
 
37
- ## 声明注释
102
+ 两段式,短函数可以只留第一段:
38
103
 
39
- 公共头文件里的类型、函数、常量用 `///` 写契约,调用方读完就知道怎么用。
104
+ 1. 承诺:这个函数给调用方什么结果,一句话,不复述函数名。
105
+ 2. 契约:`@param`、`@return`、`@throws`,写范围、单位、所有权和失败方式。
40
106
 
41
107
  ```cpp
42
- /// 返回任务编号对应的状态。
108
+ /// 在指定坐标放置棋子。
43
109
  ///
44
- /// @param id 任务编号,必须在 [0, kTaskCount) 内
45
- /// @return 该编号的任务状态
46
- /// @throws TaskIndexError id 越界
47
- [[nodiscard]] TaskState GetTaskState(int id) const;
110
+ /// 越界、已占或要写空格子都返回 false,不抛异常。
111
+ ///
112
+ /// @param position 目标行列坐标。
113
+ /// @param cell_type 要放置的棋盘格子类型,必须是 kX 或 kO。
114
+ /// @return 放置成功返回 true;坐标越界、要写的是空格子或目标已有棋子时返回 false,棋盘不变。
115
+ [[nodiscard]] bool PlaceCell(CellPosition position, CellType cell_type);
48
116
  ```
49
117
 
50
- - 第一行一句话说清「做什么」,空一行后写参数、返回值、异常。
51
- - `@param` 要么全写,要么全不写;只有一个参数且含义自明时可以省略。
52
- - `@return` 在返回类型自明时省略;`[[nodiscard]]` 已经表达「必须使用」时不要再写一遍。
53
- - `@throws` 必须和 `references/exceptions.md` 的异常声明一致;不抛的函数不要写。
54
- - 禁止 `@todo`、`@fixme`、`@author`、`@since`;待办统一用下面的 TODO 格式。
55
- - 枚举值的尾随注释用 `///<`,全部值都写或全部不写,并对齐;语义结果写在最前,编号或内部值写在后面。
56
- - 私有声明同样写合同:调用者可能是同一个类的另一个成员,不能假设它知道所有前置条件。
57
- - 参数是输出参数时在 `@param` 后标 `[out]`;既读又写时标 `[in,out]`。
58
- - 不写 `@brief`、`@details` 这类空壳标签;第一行本身就是摘要。
118
+ - 布尔查询:`@return` 写「什么情况下为 true」,再补一句「不检查什么」。例如 `IsCellEmpty` 不判断对局是否结束,这句话必须写,否则调用方会替它想。
119
+ - `@param` 要么全写,要么全不写;参数名已经能说明意思的,与其写「索引」,不如写取值范围或所有权。
120
+ - `@return` 在返回类型自明时可以省略;`[[nodiscard]]` 已表达「必须使用」时不再重复。
121
+ - `@throws` 必须与 `references/exceptions.md` 的声明一致;不抛异常的函数不写。
122
+ - 私有函数同样写契约:调用者可能是同一个类的另一个成员,不能默认它知道前置条件。
123
+ - 参数是输出参数时标 `[out]`,既读又写时标 `[in,out]`。
124
+
125
+ ## 数据成员与实现注释
126
+
127
+ 数据成员用行尾注释,写「存什么 + 范围或单位 + 初值的含义」,同类成员对齐。
59
128
 
60
- ## 实现注释
129
+ ```cpp
130
+ Board board_; // 对局棋盘。
131
+ Player current_player_ = Player::kCross; // 当前轮到的玩家,开局时 X 先手。
132
+ ```
61
133
 
62
- `.cc` 里的注释只写「为什么」和「这里有什么坑」,不复述语句。
134
+ 实现注释只写三样东西:为什么这样写、这里的边界条件、这样做的代价。1–3 行,放在被解释语句的上方。
63
135
 
64
136
  ```cpp
65
- // 先判空再取首元素:空订单列表是合法状态,不能假设至少有一单。
66
- if (orders_.empty()) {
67
- return OrderResult::kInProgress;
137
+ // 只有对手能立刻成线时才挡:先看自己的胜手,避免把能赢的一步让给对方。
138
+ if (CanWinNow(game, player)) {
139
+ return win_move;
68
140
  }
69
141
  ```
70
142
 
71
- - 不写「遍历 slots_,检查是否超过阈值」这种把下一行翻译一遍的注释。
72
- - 注释放在被解释语句的上方;只有短小的状态说明可以放行尾,且一行只挂一个语句。
73
- - 不要把注释挂在 `if` 和它的 `else` 中间;分支各自的理由写在分支内部上方。
74
- - 一个注释只解释一件事;解释两件事时拆成两行或两个注释。
75
- - 相邻的单行注释合成一个注释块,一行一个要点;不要把整段说明挤进一个长行。
76
- - 处理溢出、越界回退、平台差异时,注释写「为什么这个分支存在」和「不这样会怎样」,不写「这里做了检查」。
77
- - 不写「这里用了 vector 因为……」「这里改成……更好」这类实现史;设计取舍写进提交说明或文档。
78
- - 注释掉的代码不保留;需要时从版本历史取。
143
+ - 不逐行翻译代码,不在 `if` 与 `else` 之间挂注释,分支理由写进各自分支内部。
144
+ - 允许写局部取舍(为什么这里接受 O(n²)、为什么排序要稳定、为什么缓存这一层);不写变更史(「原来……现在……」)和外部资料路径。
145
+ - 注释掉的代码不保留,需要的从版本历史取。
79
146
 
80
- ## TODO
147
+ 下面这些位置**每处都要有一条实现注释**,它们决定读者能不能看懂逻辑,不是可选的润色:
81
148
 
82
- 统一格式:
149
+ - 次序依赖:几项更新必须按某个次序做,或两种判断的先后会改变结果(先判胜再判满、先验坐标再写数组)。
150
+ - 类型收窄:从宽类型读入再检查能否收进窄类型,或 `static_cast` 之前的范围判断。
151
+ - 输入与解析:为什么清失败位、为什么丢弃整行、为什么按 64 位读再收窄。
152
+ - 外部格式:手写 JSON、协议字段顺序、编码处理的约定,以及改动时要同步的文件。
153
+ - 回退路径:`switch` 覆盖全部枚举后仍留的 `return`、断言拦不住的兜底分支、`continue` 而不是报错的场合。
154
+ - 状态不变量:写入后哪些成员已经一致、失败时为什么不需要撤销。
155
+ - 刻意重复或刻意不共享:为了可读性重复一小段判断、为了零依赖不复用库函数。
156
+
157
+ 有这些位置时不受 1–3 行预算约束:先写清为什么,再检查能否删。反过来,循环体里逐行复述 `push_back`、`return` 的注释一律删。
158
+
159
+ ## TODO
83
160
 
84
161
  ```cpp
85
162
  // TODO: 阶段 3 - 支持撤销任务状态更新
86
163
  // TODO: issue #42 - 把编号检查抽到 TaskTable
87
164
  ```
88
165
 
89
- - `TODO:` 后先写定位(阶段名或 issue 号),再写一个短横线,再写动作。
90
- - 不写 `TODO(某人)`;人不重要,上下文重要。
91
- - 不写没有动作的 `TODO: 参考 xxx`;要么写清要做什么,要么删掉。
92
- - 提交前搜一遍 `TODO`,能做完的做完,做不完的保留定位。
93
-
94
- ## 语言与术语
95
-
96
- - 注释用中文;标识符、标准库类型、命令、错误消息保持原文。
97
- - 同一个概念只用一种译法;术语表见 `references/glossary.md`。
98
- - 不写「显然」「众所周知」;读者不需要被评判。
99
- - 注释不超过两行连续说明;超过时先问是不是该抽函数或写文档。
166
+ `TODO:` 后先写定位(阶段名或 issue 号),再写一个短横线,再写动作。不写 `TODO(某人)`,不写没有动作的 TODO。
100
167
 
101
168
  ## 配置注释
102
169
 
@@ -109,17 +176,39 @@ BasedOnStyle: Google
109
176
  Standard: Latest
110
177
  ```
111
178
 
112
- - 配置注释贴在被解释的键上方,不写大段策略头。
113
- - `.clang-format` 按逻辑组分段,每组一句注释说明这一组在管什么;不要逐键写「设置 X 为 Y」。
114
- - `.clangd` 每个键组一句话,说明为什么需要这个编译数据库或参数;不要复制整份 CMake 配置。
115
- - 不在配置里写 `TODO` 或指向某份文档的路径;配置要能独立读懂。
116
- - `.clang-tidy` 的每个关闭项写一句理由;没有理由的关闭项删掉。
117
- - 同一个理由只写一次,不逐项复制。
118
- - 源码注释不写团队政策或流程;那些属于文档,不属于被阅读的代码。
179
+ - 配置注释贴在被解释的键上方,不写策略头和文档路径。
180
+ - `.clang-format` 按逻辑组分段,每组一句说明这一组在管什么,不逐键写「设置 X 为 Y」。
181
+ - `.clangd` 每个键组一句话,说明为什么需要这个数据库或参数。
182
+ - `.clang-tidy` 的每个关闭项写一句理由;没有理由的关闭项删掉。同一个理由只写一次。
183
+
184
+ ## 语言与术语
185
+
186
+ - 注释用中文;标识符、标准库类型、命令、错误消息保持原文。
187
+ - 同一个概念只用一种译法,术语表见 `references/glossary.md`;中文与英文、数字之间留一个空格。
188
+ - 不写「显然」「众所周知」这类评判读者的词,也不写「注意」「重要」这类没有信息量的加重。
189
+
190
+ ## 改写次序
191
+
192
+ 只改注释时仍走第 3、4 步,然后按这个次序回读:
193
+
194
+ 1. 文件头:是什么、由哪几块组成、不负责什么。
195
+ 2. 类型注释:概念、约定;删掉成员清单与设计论证。
196
+ 3. 函数注释:承诺、参数与失败方式;与 `references/exceptions.md` 一致。
197
+ 4. 数据成员:存什么、约束、初值。
198
+ 5. 实现注释:只留为什么和边界,逐行字幕全部删掉。
199
+ 6. 回读:同一概念一种译法;注释里出现的新函数名、新常量是否真的存在。
200
+ 7. 机械化兜底:`node "<skill-dir>/scripts/checking-comments.mjs" --root <项目根>` 检查横幅宽度、禁止标签、注释与声明的贴合、类型成员清单、设计论证、`@param` 完整性与枚举行尾注释;它不判断内容好坏,只守住格式底线。
201
+
202
+ ## 与读者正文的边界
203
+
204
+ - 注释写给维护者,正文写给读者;两边各自写自己的文件,不互相抄。
205
+ - 设计理由、方案比较与算法取舍写在正文或实现注释里,公开接口注释只留契约。
206
+ - `///`、`@param`、横幅不进入 `.qmd`、YAML、CSS。
119
207
 
120
208
  ## 禁止
121
209
 
122
- - 注释里写外部资料路径、项目专属类名或跨仓库引用。
123
210
  - 用注释代替类型、`const`、`[[nodiscard]]` 能表达的限制。
124
- - 大段 ASCII 表格、装饰性图案、表情符号。
125
- - 把注释当提交说明或变更日志。
211
+ - 大段 ASCII 表格、装饰图案、表情符号,以及把注释当提交说明或变更日志。
212
+ - 写外部资料路径、跨仓库引用、团队流程;这些属于文档。
213
+ - 写成员功能清单(`提供……、……的功能`)与按声明顺序复述接口。
214
+ - 写注释的元叙述:不交代「其余内容见某处注释」「两处不重复」「成员用法写在行尾注释里」这类注释布局;读者要的事实直接写出来,不写「哪里写了什么」。
@@ -43,6 +43,8 @@ class TaskTable {
43
43
 
44
44
  ## 命名空间作用域的头文件
45
45
 
46
+ 文件槽位与守卫名以 `references/file-template.md` 为准;本节只看命名空间作用域内的排列顺序。
47
+
46
48
  ```cpp
47
49
  #ifndef TASKFLOW_INCLUDE_TASK_TABLE_H_
48
50
  #define TASKFLOW_INCLUDE_TASK_TABLE_H_
@@ -79,11 +81,11 @@ class TaskTable {
79
81
 
80
82
  顺序:包含 → 命名空间 → `inline constexpr` 常量 → `enum class` → `struct`(被依赖的在前)→ 自由函数 → 类。类的公共接口类型放在类内最前。
81
83
 
82
- ## `.cc` 的定义顺序
84
+ ## `.cpp` 的定义顺序
83
85
 
84
86
  - 与头文件声明同序:先类型和常量,再自由函数,再成员函数;成员函数按类内声明顺序。
85
87
  - 文件头注释之后先放本文件的 `namespace taskflow {`,再放公共定义;不需要外部引用的实现放匿名命名空间,或声明 `static`;按是否需要内部链接选择,不要为了「整洁」强行加。
86
- - 构造函数、析构函数、拷贝/移动定义放在成员函数组的最前;`= default` 的定义放 `.cc` 时紧跟声明顺序。
88
+ - 构造函数、析构函数、拷贝/移动定义放在成员函数组的最前;`= default` 的定义放 `.cpp` 时紧跟声明顺序。
87
89
  - 不要为了「先编译」重排定义;C++ 不要求定义顺序,读者顺序优先。
88
90
 
89
91
  ## 空行
@@ -0,0 +1,131 @@
1
+ # 文件模板
2
+
3
+ 本文件是头文件与实现文件槽位的唯一权威。`google-cpp-style.md` 只保留「路径式守卫、不用 `#pragma once`」的决定;`layout.md` 只定文件放在哪;`steps/03-declaration.md` 指向这里。类内声明顺序仍只写在 `declaration-order.md`,本文件不复制。
4
+
5
+ ## 选择扩展名
6
+
7
+ | 扩展名 | 用途 | 是否写守卫 |
8
+ | --- | --- | --- |
9
+ | `.h` | C++ 公共或私有头文件 | 是 |
10
+ | `.hpp` | 与 `.h` 同槽位;一个工程统一选一种头扩展名 | 是 |
11
+ | `.cpp` | 实现文件;新工程与示例一律使用 | 否 |
12
+ | `.cc` | 已有工程常见的实现后缀;新工程与示例不选 | 否 |
13
+ | `.inc` | 稀有文本包含 | 否;本工作流不用 |
14
+
15
+ - 一个工程只选一种头扩展名和一种实现扩展名;新工程与示例一律 `.cpp` / `.h`,已有工程跟随既有后缀,不批量改名。
16
+ - 不写 `#pragma once`,也不与包含守卫并用。Google C++ Style Guide 要求路径式 include guards;标准守卫被编译器优化后与 `#pragma once` 等价,并存只会多一条非标准指令。
17
+ - 新文件先按本模板起骨架,再填内容;不因「先跑起来」省略守卫。
18
+
19
+ ## 守卫名
20
+
21
+ 格式:`<PROJECT>_<PATH>_<FILE>_H_`。
22
+
23
+ - `PROJECT` 是源码树顶的项目名,全大写;`PATH` 是相对源码树顶的目录;`FILE` 是文件名;非字母数字改成下划线。
24
+ - 末尾固定 `_H_`;`#endif` 后两个空格,再写 `//` 和同一个宏。
25
+ - 井字棋源码树顶是 `games/tictactoe/`:
26
+
27
+ | 文件 | 守卫 |
28
+ | --- | --- |
29
+ | `tictactoe/include/board.h` | `TICTACTOE_INCLUDE_BOARD_H_` |
30
+ | `tictactoe/include/game.h` | `TICTACTOE_INCLUDE_GAME_H_` |
31
+ | `tictactoe/include/ai.h` | `TICTACTOE_INCLUDE_AI_H_` |
32
+ | `apps/include/cli.h` | `APPS_INCLUDE_CLI_H_` |
33
+ | `tests/supporting-tests.h` | `TESTS_SUPPORTING_TESTS_H_` |
34
+
35
+ - 通用示例:`taskflow/include/task_table.h` → `TASKFLOW_INCLUDE_TASK_TABLE_H_`。
36
+ - 不用 `TICTACTOE_BOARD_H` 这类省略路径的名字;两个同名头会撞宏。
37
+
38
+ ## `.h` 与 `.hpp`
39
+
40
+ 槽位顺序固定:
41
+
42
+ 1. 文件头注释(格式见 `comments.md`,留在守卫外)
43
+ 2. `#ifndef` / `#define`
44
+ 3. 分组包含(组间空行,组内字母序)
45
+ 4. 命名空间内:常量 → 枚举 → 结构体 → 自由函数 → 类
46
+ 5. `#endif // MACRO`
47
+
48
+ ```cpp
49
+ //===----------------------------------------------------------------------===//
50
+ // 这个文件负责什么;不负责什么。
51
+ //===----------------------------------------------------------------------===//
52
+
53
+ #ifndef PROJECT_PATH_FILE_H_
54
+ #define PROJECT_PATH_FILE_H_
55
+
56
+ #include <cstdint>
57
+ #include <vector>
58
+
59
+ #include "project/path/dependency.h"
60
+
61
+ namespace project {
62
+
63
+ inline constexpr int kThingCount = 3;
64
+
65
+ enum class ThingState { kIdle, kBusy };
66
+
67
+ struct Thing {
68
+ int id;
69
+ };
70
+
71
+ class ThingTable {
72
+ public:
73
+ void Add(const Thing& thing);
74
+
75
+ private:
76
+ std::vector<Thing> things_;
77
+ };
78
+
79
+ } // namespace project
80
+
81
+ #endif // PROJECT_PATH_FILE_H_
82
+ ```
83
+
84
+ - 文件头不写版权、作者或变更日志;守卫外只放文件头。
85
+ - 头文件自包含:自己用到的类型、宏和标准库头都直接包含,不依赖调用方先包含什么。
86
+ - 不把 `using namespace`、非 inline 普通全局变量、匿名命名空间或 `static` 定义放进头文件。
87
+ - 模板、`constexpr` 和类内短函数可以留在头文件;普通函数体放实现文件。
88
+ - `.hpp` 完全套用 `.h` 的槽位,只换扩展名,不换顺序。
89
+
90
+ ## `.cpp`
91
+
92
+ 槽位顺序:
93
+
94
+ 1. 文件头注释
95
+ 2. 配对头在最前(若有)
96
+ 3. 分组包含
97
+ 4. 命名空间与定义
98
+
99
+ ```cpp
100
+ //===----------------------------------------------------------------------===//
101
+ // 这个文件实现什么;边界在哪里。
102
+ //===----------------------------------------------------------------------===//
103
+
104
+ #include "project/path/thing.h"
105
+
106
+ #include <algorithm>
107
+
108
+ #include "project/path/other.h"
109
+
110
+ namespace project {
111
+
112
+ void ThingTable::Add(const Thing& thing) { things_.push_back(thing); }
113
+
114
+ } // namespace project
115
+ ```
116
+
117
+ - 实现文件不写 `#ifndef` / `#define` / `#endif`,也不写 `#pragma once`。
118
+ - 新工程与示例一律 `.cpp`;已有工程沿用它原本的实现后缀(常见 `.cc`),不批量改名。
119
+ - `main` 文件同样只做装配;模板不替项目决定业务结构。
120
+
121
+ ## `.inc`
122
+
123
+ 只用于必须做文本包含的场景。本工作流不新写 `.inc`;第三方 `.inc` 不补守卫,也不为它改本模板。
124
+
125
+ ## 自检
126
+
127
+ - 每个头文件恰好一对 `#ifndef` / `#define`,末尾一个 `#endif // 同名宏`。
128
+ - 守卫名与相对路径一致;`#endif` 注释与宏逐字相同。
129
+ - 头文件能单独编译:只包含它的一个空 `.cpp` 必须通过。
130
+ - 实现文件没有守卫,配对头在第一组。
131
+ - 工程内没有 `#pragma once`,也没有守卫与 pragma 并存。
@@ -6,9 +6,9 @@
6
6
  | --- | --- | --- |
7
7
  | declaration | 声明 | 告诉编译器名字和类型,不分配存储 |
8
8
  | definition | 定义 | 分配存储或给出函数体 |
9
- | translation unit | 翻译单元 | 一个 `.cc` 加上它包含的头,预处理后的整体 |
9
+ | translation unit | 翻译单元 | 一个 `.cpp` 加上它包含的头,预处理后的整体 |
10
10
  | header | 头文件 | `.h` / `.hpp`,放声明和可内联实现 |
11
- | source file | 源文件 | `.cc`,放普通函数体 |
11
+ | source file | 源文件 | `.cpp`,放普通函数体 |
12
12
  | target | 目标 | CMake 的构建单元,如 `app`、`greeting` |
13
13
  | observable behavior | 可观察行为 | 用户或测试能看到的输出、返回值、退出码、文件内容 |
14
14
  | boundary | 边界 | 库与程序、模块与模块之间的职责分界 |
@@ -83,6 +83,6 @@
83
83
 
84
84
  - **声明 vs 定义**:头文件里多是声明;函数体、全局变量初始化是定义。
85
85
  - **参数 vs 实参**:声明里叫参数(parameter),调用点叫实参(argument);中文口语都叫参数时,写文档要区分。
86
- - **接口 vs 实现**:公共头是接口,`.cc` 是实现;不要用「接口」指整个类。
86
+ - **接口 vs 实现**:公共头是接口,`.cpp` 是实现;不要用「接口」指整个类。
87
87
  - **异常 vs 错误**:异常是语言机制,错误是结果;是否用异常表达错误由接口合同和 invariant 决定,不能把两者当同义词。规则见 `references/exceptions.md`。
88
88
  - **格式化 vs 排版**:`std::format` 组合消息;clang-format 排版源码。不要混用「格式化」指代两者。
@@ -12,15 +12,15 @@
12
12
 
13
13
  ## 头文件
14
14
 
15
- - 每个头文件用 `#define` 守卫,符号由项目源码树全路径生成:`<PROJECT>_<PATH>_<FILE>_H_`,例如 `FOO_BAR_BAZ_H_`。不要用 `#pragma once`。
15
+ - 每个头文件用 `#define` 守卫,符号由项目源码树全路径生成:`<PROJECT>_<PATH>_<FILE>_H_`,例如 `FOO_BAR_BAZ_H_`。不要用 `#pragma once`,也不要两者并存;模板见 `references/file-template.md`。
16
16
  - 头文件必须自包含:自己用到的类型、宏、标准库头都直接包含,不依赖调用方的包含顺序。
17
- - `.cc` 第一个包含自己的配对头,然后按下面的顺序包含。
17
+ - `.cpp` 第一个包含自己的配对头,然后按下面的顺序包含。
18
18
  - 包含顺序:配对头 → C 系统头 → C++ 标准库头 → 其他库头 → 本项目头。每组之间空一行,组内按字母序;不要依赖 `clang-format` 的 `IncludeCategories` 自定义分组,本项目不再覆盖 Google 预设。
19
19
  - 本项目头用引号,路径从项目源码根开始,例如 `#include "base/logging.h"`。构建时把源码根加入 include 搜索路径,不要只把组件的 `include/` 设为根再写裸文件名。
20
20
  - 公共头文件直接放在组件的 `include/` 下;不要为了「整齐」再加一层命名空间目录。
21
21
  - 跨文件共享的常量用 `inline constexpr`,放在命名空间里并带 `k` 前缀;头文件里不要定义非 inline 的普通全局变量。
22
- - 头文件里只放模板、`constexpr`、类内联小函数和声明;普通函数的函数体放 `.cc`。
23
- - 头文件里不要用匿名命名空间或 `static` 隐藏定义;内部链接的实现放 `.cc`。
22
+ - 头文件里只放模板、`constexpr`、类内联小函数和声明;普通函数的函数体放 `.cpp`。
23
+ - 头文件里不要用匿名命名空间或 `static` 隐藏定义;内部链接的实现放 `.cpp`。
24
24
 
25
25
  ## 命名
26
26
 
@@ -38,7 +38,7 @@
38
38
 
39
39
  - 一个 API 不混用两套命名。对外函数用 PascalCase,成员变量用 snake_case,不要出现 `get_task` 与 `GetTask` 并存。
40
40
  - 查询函数遵循同一 API 选定的访问器约定:Google 允许 `Count()` 或 `count()`,也允许 `GetCurrentOrder()`;同一 API 内不要混用两套。
41
- - 谓词用 `Is` / `Has`:`Contains()`、`HasStock()`。返回 `bool` 但语义不是判断的函数,换成领域动词。
41
+ - 谓词用 `Is` / `Has` / `Can`,成对规则可用 `Completes` 这类限定式;唯一前缀集与 `Contains` 例外见 `references/naming.md`。返回 `bool` 但语义不是判断的函数,换成领域动词。
42
42
  - 尝试用 `Try`:`TryReserve()` 返回结果类型;确定性动作直接用领域动词:`CompleteTask()`、`Reset()`。
43
43
  - 映射函数的名字要含来源与目标角色:`OrderToReceipt()`、`TaskTableToString()`;禁止裸 `To()` / `Of()`。
44
44
  - 计算函数用动词开头:`ScoreOrder()`、`CollectPendingTasks()`。
@@ -53,18 +53,18 @@
53
53
  - 默认成员初始化写在声明处(NSDMI),不要在构造函数初始化列表里重复。
54
54
  - 默认用花括号初始化:`int count{};`、`std::vector<int> values{1, 2, 3};`。只有需要对比、`auto` 推导或赋值语义时用 `=`;不要用 `()` 初始化普通对象。
55
55
  - 类声明顺序:public → protected → private;类型与别名 → 静态常量 → 工厂 → 构造/赋值/析构 → 其他函数 → 数据成员。
56
- - 用户声明的拷贝/移动/析构函数在头文件声明、在 `.cc` 定义;只有零参数且默认实现足够时才写 `= default`。
56
+ - 用户声明的拷贝/移动/析构函数在头文件声明、在 `.cpp` 定义;只有零参数且默认实现足够时才写 `= default`。
57
57
  - 查询成员函数标尾部 `const`;返回结果对象的 API 加 `[[nodiscard]]`,不要给所有非 `void` 函数机械添加。
58
58
  - 所有权优先 `std::unique_ptr`;没有共享所有权的理由时不用 `std::shared_ptr`。禁止裸 `new` / `delete`。
59
59
  - 禁止 C 风格强制转换;用 `static_cast`、`const_cast`、`reinterpret_cast`、`std::bit_cast`,并让 `clang-tidy` 的 `google-readability-casting` 兜底。
60
- - `.cc` 里不需要外部引用的定义放匿名命名空间或声明 `static`;不要在生产 `.cc` 里为了「整洁」强行用 `static`,按是否需要内部链接决定。
60
+ - `.cpp` 里不需要外部引用的定义放匿名命名空间或声明 `static`;不要在生产 `.cpp` 里为了「整洁」强行用 `static`,按是否需要内部链接决定。
61
61
 
62
62
  ## 函数
63
63
 
64
64
  - 优先返回值,不用输出参数:返回 `std::string`、`std::vector<T>`、结果结构体。
65
65
  - 已有成员按 `const` 引用返回,例如 `GetHistory()`;不要返回内部容器的可变引用。
66
66
  - 函数保持短小;没有硬上限,超过约 40 行时先拆出有名字的步骤,而不是加注释分节。
67
- - 头文件里只把大约 10 行以内、确实适合内联的短函数就地定义;更长或需要完整定义的函数放 `.cc`,避免头文件膨胀和重复编译。
67
+ - 头文件里只把大约 10 行以内、确实适合内联的短函数就地定义;更长或需要完整定义的函数放 `.cpp`,避免头文件膨胀和重复编译。
68
68
  - 虚函数不用默认参数;默认参数只用于非虚的便利接口。
69
69
  - 尾置返回类型只在普通语法不可行或明显更难读时使用,例如返回类型依赖参数;不要为了风格统一全面改写。
70
70
  - 只对自己的类型重载运算符;不重载 `&&`、`||`、`,`、一元 `&`;不定义用户字面量。
@@ -6,7 +6,7 @@
6
6
 
7
7
  - 类型决定允许的操作和表示;对象是某类型的一块存储;值是可以复制、比较、传递的内容。
8
8
  - 变量有名字、类型、存储期和作用域。局部变量在离开作用域时销毁;`static` 局部变量在首次执行到声明时初始化,程序结束时销毁。
9
- - 声明让名字可见,定义分配存储或给出函数体。一个实体可以声明多次,但只能定义一次;头文件只放声明,定义放 `.cc`。
9
+ - 声明让名字可见,定义分配存储或给出函数体。一个实体可以声明多次,但只能定义一次;头文件只放声明,定义放 `.cpp`。
10
10
  - 引用是已有对象的别名,必须初始化,不能重新绑定;指针可以为空,也可以改指向。接口优先用引用表达“必须存在”,用指针表达“可以不存在”。
11
11
 
12
12
  ## 初始化与列表初始化
@@ -72,7 +72,7 @@ auto total = 0.0; // auto 只能用 =,不能用 {}
72
72
  - `struct` 用于公开数据的聚合;`class` 用于维护不变量、隐藏表示。两者只差默认访问权限,按语义选。
73
73
  - 数据成员默认私有;需要暴露只读视图时提供 `const` 成员函数,不返回可变内部引用。
74
74
  - 单参数构造函数用 `explicit`;构造函数失败时抛异常,不留下半初始化对象。
75
- - 特殊成员(构造、析构、拷贝、移动)在头文件声明,在 `.cc` 定义;只有默认实现足够且零参数时才写 `= default`。
75
+ - 特殊成员(构造、析构、拷贝、移动)在头文件声明,在 `.cpp` 定义;只有默认实现足够且零参数时才写 `= default`。
76
76
  - 定义了析构函数、拷贝构造或拷贝赋值之一时,认真考虑其余特殊成员;用 `= delete` 明确禁止不需要的操作。
77
77
  - 多态基类析构函数必须是 `virtual` 或 `protected`;否则通过基类指针删除派生对象是未定义行为。
78
78
  - 不在构造函数里调用虚函数;此时派生部分还没构造完。
@@ -89,7 +89,7 @@ auto total = 0.0; // auto 只能用 =,不能用 {}
89
89
 
90
90
  ## 模板、概念与 CTAD
91
91
 
92
- - 模板定义放头文件;`.cc` 里的模板实例化只对当前翻译单元可见。
92
+ - 模板定义放头文件;`.cpp` 里的模板实例化只对当前翻译单元可见。
93
93
  - C++20 用概念约束模板参数,把错误从实例化深处提前到调用点:
94
94
 
95
95
  ```cpp
@@ -9,10 +9,10 @@
9
9
  include/
10
10
  <name>.h 公共接口,从源码根写完整路径包含
11
11
  src/
12
- <name>.cc 实现,可以包含私有头
12
+ <name>.cpp 实现,可以包含私有头
13
13
  <detail>.h 私有头,只被本组件 src 包含
14
14
  tests/
15
- <name>_test.cc 通过公共接口测试
15
+ <name>_test.cpp 通过公共接口测试
16
16
  ```
17
17
 
18
18
  - 公共头直接放 `include/` 下;包含时从仓库根写完整路径,例如 `#include "task_list/include/task_list.h"`,不要写裸文件名或 `../`。
@@ -27,37 +27,46 @@ app/
27
27
  include/
28
28
  cli.h 输入输出与参数解析的私有接口
29
29
  src/
30
- cli.cc 终端交互
31
- main.cc 只做装配:构造依赖、调用 Run、翻译退出码
30
+ cli.cpp 终端交互
31
+ main.cpp 只做装配:构造依赖、调用 Run、翻译退出码
32
32
  ```
33
33
 
34
- - `main.cc` 不写业务规则;它只解析参数、打开流、调用库、捕获边界异常。
34
+ - `main.cpp` 不写业务规则;它只解析参数、打开流、调用库、捕获边界异常。
35
35
  - 终端输入输出集中在 `app/`;库不包含 `<iostream>`,也不打印。
36
- - 只有一个程序时 `app/` 可以退化成根 `src/main.cc`;一旦需要测试交互逻辑,再拆 `cli.h` / `cli.cc`。
36
+ - 只有一个程序时 `app/` 可以退化成根 `src/main.cpp`;一旦需要测试交互逻辑,再拆 `cli.h` / `cli.cpp`。
37
37
  - 程序可以依赖库;库不能反向依赖程序。
38
38
 
39
39
  ## 单文件与多文件
40
40
 
41
41
  | 形态 | 何时用 | 布局 |
42
42
  | --- | --- | --- |
43
- | 单文件 | 一次性脚本、极小示例 | `src/main.cc`,辅助函数放匿名命名空间 |
44
- | 多文件单程序 | 逻辑开始分块,但还没有复用方 | `src/*.cc` + `include/*.h`,一个 `app` target |
43
+ | 单文件 | 一次性脚本、极小示例 | `src/main.cpp`,辅助函数放匿名命名空间 |
44
+ | 多文件单程序 | 逻辑开始分块,但还没有复用方 | `src/*.cpp` + `include/*.h`,一个 `app` target |
45
45
  | 静态库 + 程序 | 需要测试、需要被多个程序链接 | `<component>/` + `app/`,库 target + `app` target |
46
46
  | 动态库 + 程序 | 需要运行时替换实现、插件式加载 | 同上,库 target 改 `SHARED`,注意运行时库路径 |
47
47
 
48
48
  不要为了「以后可能复用」提前拆库;出现第二个调用方或测试需要直接调用时再拆。拆的时候接口不变,只移动文件。
49
49
 
50
+ ## 文件扩展名与归属
51
+
52
+ - `.h`:公共或私有头文件;公共头放 `include/`,私有头放 `src/`。
53
+ - `.hpp`:与 `.h` 同一套槽位;一个工程只选一种头扩展名,不因扩展名换结构。
54
+ - `.cpp`:实现文件;新工程与示例一律使用。已有工程沿用它原本的实现后缀,不批量改名。
55
+ - `.cc`:已有工程常见的实现后缀;本工作流的示例不选它,但审阅这类工程时按 `.cpp` 同一套规则处理。
56
+ - `.inc`:稀有文本包含;本工作流不新写。
57
+ - 头文件、实现文件的槽位与守卫名以 `references/file-template.md` 为准;本文只定位置,不重复守卫模板。
58
+
50
59
  ## 头文件契约
51
60
 
52
- - 公共头自包含:单独编译一个只包含它的 `.cc` 必须通过。
61
+ - 公共头自包含:单独编译一个只包含它的 `.cpp` 必须通过。
53
62
  - 头文件不定义非 inline 的普通全局变量;共享常量用 `inline constexpr`。
54
63
  - 头文件不写 `using namespace`;不污染全局命名空间。
55
- - 模板和 `constexpr` 的实现可以留在头文件;普通函数体放 `.cc`。
56
- - 头文件不包含 `<iostream>`;需要流时在 `.cc` 或程序层包含。
64
+ - 模板和 `constexpr` 的实现可以留在头文件;普通函数体放 `.cpp`。
65
+ - 头文件不包含 `<iostream>`;需要流时在 `.cpp` 或程序层包含。
57
66
 
58
67
  ## 测试
59
68
 
60
- - 测试文件放组件 `tests/` 或顶层 `tests/`,命名 `<被测>_test.cc`。
69
+ - 测试文件放组件 `tests/` 或顶层 `tests/`,命名 `<被测>_test.cpp`。
61
70
  - 测试只包含公共头;需要测私有逻辑时,先判断它是否应该成为公共行为。
62
71
  - 一个测试文件对应一个行为组;测试名描述行为,不描述实现。
63
72
  - 测试不复制实现,不依赖执行顺序,不读写固定绝对路径。