@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.
- package/README.en.md +7 -5
- package/README.md +7 -5
- package/lib/workspace.js +1 -56
- package/package.json +3 -2
- package/skills/building-cpp/SKILL.md +4 -2
- package/skills/building-cpp/assets/config/.clang-format +2 -0
- package/skills/building-cpp/assets/config/.clang-tidy +1 -0
- package/skills/building-cpp/assets/templates/projects/multi/CMakeLists.txt +2 -2
- package/skills/building-cpp/assets/templates/projects/shared-library/CMakeLists.txt +1 -1
- package/skills/building-cpp/assets/templates/projects/shared-library/greeting/CMakeLists.txt +1 -1
- package/skills/building-cpp/assets/templates/projects/single/CMakeLists.txt +1 -1
- package/skills/building-cpp/assets/templates/projects/static-library/CMakeLists.txt +1 -1
- package/skills/building-cpp/assets/templates/projects/static-library/greeting/CMakeLists.txt +1 -1
- package/skills/building-cpp/examples/cmake.md +15 -15
- package/skills/building-cpp/examples/tooling.md +3 -3
- package/skills/building-cpp/references/cmake.md +6 -6
- package/skills/building-cpp/references/encoding.md +49 -0
- package/skills/building-cpp/references/linking.md +6 -6
- package/skills/building-cpp/references/standards/c++23/README.md +1 -1
- package/skills/building-cpp/references/static-analysis.md +3 -3
- package/skills/building-cpp/scripts/checking-encoding.mjs +241 -0
- package/skills/building-cpp/steps/01-build.md +1 -1
- package/skills/building-cpp/steps/02-tooling.md +2 -2
- package/skills/building-cpp/steps/03-check.md +8 -6
- package/skills/developing-cpp/SKILL.md +19 -3
- package/skills/developing-cpp/examples/comments.md +161 -119
- package/skills/developing-cpp/examples/structure.md +4 -4
- package/skills/developing-cpp/references/comments.md +158 -69
- package/skills/developing-cpp/references/declaration-order.md +4 -2
- package/skills/developing-cpp/references/file-template.md +131 -0
- package/skills/developing-cpp/references/glossary.md +3 -3
- package/skills/developing-cpp/references/google-cpp-style.md +8 -8
- package/skills/developing-cpp/references/language.md +3 -3
- package/skills/developing-cpp/references/layout.md +21 -12
- package/skills/developing-cpp/references/naming.md +11 -2
- package/skills/developing-cpp/references/project-design.md +10 -1
- package/skills/developing-cpp/references/standard-library.md +1 -1
- package/skills/developing-cpp/scripts/checking-comments.mjs +316 -0
- package/skills/developing-cpp/steps/01-behavior.md +1 -1
- package/skills/developing-cpp/steps/02-boundary.md +5 -4
- package/skills/developing-cpp/steps/03-declaration.md +6 -6
- package/skills/developing-cpp/steps/04-implementation.md +3 -3
- /package/skills/building-cpp/assets/templates/projects/multi/src/{greeting.cc → greeting.cpp} +0 -0
- /package/skills/building-cpp/assets/templates/projects/multi/src/{main.cc → main.cpp} +0 -0
- /package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/{greeting.cc → greeting.cpp} +0 -0
- /package/skills/building-cpp/assets/templates/projects/shared-library/src/{main.cc → main.cpp} +0 -0
- /package/skills/building-cpp/assets/templates/projects/single/src/{main.cc → main.cpp} +0 -0
- /package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/{greeting.cc → greeting.cpp} +0 -0
- /package/skills/building-cpp/assets/templates/projects/static-library/src/{main.cc → main.cpp} +0 -0
|
@@ -5,229 +5,271 @@
|
|
|
5
5
|
**差**:
|
|
6
6
|
|
|
7
7
|
```cpp
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// 创建:
|
|
11
|
-
// 修改:
|
|
8
|
+
// board.h
|
|
9
|
+
// 作者:chengzhao-dev
|
|
10
|
+
// 创建:2026-01-01
|
|
11
|
+
// 修改:2026-03-02 增加 CompletesLine
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
**好**:
|
|
15
15
|
|
|
16
16
|
```cpp
|
|
17
17
|
//===----------------------------------------------------------------------===//
|
|
18
|
-
//
|
|
18
|
+
// board.h - 定义井字棋棋盘。
|
|
19
19
|
//
|
|
20
|
-
//
|
|
20
|
+
// 棋盘只保存格子状态与规则判断,不含对局流程;对局流程在 game.h。
|
|
21
21
|
//===----------------------------------------------------------------------===//
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
文件头回答「这个文件负责什么、不负责什么」,不写作者和变更史。后两行是这条边界:读的人由此知道该去哪个文件找回合推进。
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## 类型注释:概念与约定,不是功能清单
|
|
27
27
|
|
|
28
28
|
**差**:
|
|
29
29
|
|
|
30
30
|
```cpp
|
|
31
|
-
///
|
|
32
|
-
///
|
|
33
|
-
///
|
|
34
|
-
|
|
31
|
+
/// 井字棋棋盘。
|
|
32
|
+
///
|
|
33
|
+
/// 提供检查坐标区间、检查棋盘格子是否为空、获取棋盘格子类型、放置棋子、清空棋盘和判定终局的功能。
|
|
34
|
+
/// 棋盘为 3 行 3 列。
|
|
35
|
+
class Board {
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
问题有两处:把成员函数抄了一遍,接口一改就过期;「棋盘为 3 行 3 列」被拆成单独一句,读者不知道它在回答什么问题。
|
|
39
|
+
|
|
40
|
+
**差**(把别的类型和以后才发生的事都写进来):
|
|
41
|
+
|
|
42
|
+
```cpp
|
|
43
|
+
/// 井字棋棋盘。
|
|
44
|
+
///
|
|
45
|
+
/// 棋盘只保存 3×3 的格子状态和规则判断,不含回合推进:谁该落子、落子被拒后怎么提示留在 Game。
|
|
46
|
+
///
|
|
47
|
+
/// 棋盘是值语义,可以自由拷贝;所有查询都是 const,对局和搜索可以交替读取同一棋盘。
|
|
48
|
+
class Board {
|
|
35
49
|
```
|
|
36
50
|
|
|
37
|
-
|
|
51
|
+
第二段说的是 `Game` 负责什么,读者读完仍不知道 `Board` 保证什么,重构一次这句话就过期;后两段里的可拷贝、`const` 查询在类声明里已经写清,「搜索要读同一棋盘」还不是现在的调用方。读者真正需要的是这个类型自己的不变量与失败方式。
|
|
38
52
|
|
|
39
53
|
**好**:
|
|
40
54
|
|
|
41
55
|
```cpp
|
|
42
|
-
///
|
|
56
|
+
/// 井字棋棋盘。
|
|
43
57
|
///
|
|
44
|
-
///
|
|
45
|
-
|
|
46
|
-
/// @throws TaskIndexError index 越界
|
|
47
|
-
[[nodiscard]] Task GetTask(int index) const;
|
|
58
|
+
/// 3×3 个格子,每格取空、X 或 O;越界坐标不会写入棋盘。
|
|
59
|
+
class Board {
|
|
48
60
|
```
|
|
49
61
|
|
|
50
|
-
|
|
62
|
+
两句都是本类型能兑现、读代码又不容易一眼看全的事,函数各自的契约下沉到函数注释;设计理由与分层不写进注释,写进书稿。
|
|
51
63
|
|
|
52
|
-
##
|
|
64
|
+
## 风格理由不进注释
|
|
65
|
+
|
|
66
|
+
**差**(在十几个函数上各写一遍):
|
|
67
|
+
|
|
68
|
+
```cpp
|
|
69
|
+
/// 检查坐标是否在范围内。函数名用 UpperCamel 而不是 snake_case,与标准库区分。
|
|
70
|
+
[[nodiscard]] static bool IsInRange(CellPosition position) noexcept;
|
|
71
|
+
|
|
72
|
+
/// 检查格子是否为空。函数名用 UpperCamel 而不是 snake_case,与标准库区分。
|
|
73
|
+
[[nodiscard]] bool IsCellEmpty(CellPosition position) const;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**好**(只写契约):
|
|
77
|
+
|
|
78
|
+
```cpp
|
|
79
|
+
/// 检查坐标是否落在棋盘下标区间内。
|
|
80
|
+
///
|
|
81
|
+
/// 只判断几何边界,不检查格子是否为空、对局是否已经结束。
|
|
82
|
+
[[nodiscard]] static bool IsInRange(CellPosition position) noexcept;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
命名与风格约定写进项目文档和读者正文,注释里不解释「为什么用这种命名」。
|
|
86
|
+
|
|
87
|
+
## 函数契约
|
|
53
88
|
|
|
54
89
|
**差**:
|
|
55
90
|
|
|
56
91
|
```cpp
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// 遍历 tasks_
|
|
63
|
-
for (const auto& task : tasks_) {
|
|
64
|
-
// 比较优先级
|
|
65
|
-
if (task.priority < best.priority) {
|
|
66
|
-
// 更新 best
|
|
67
|
-
best = task;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
92
|
+
/// 尝试执行一次落子。
|
|
93
|
+
///
|
|
94
|
+
/// @param move 落子。
|
|
95
|
+
/// @returns 落子执行成功返回 kPlaced,被拒绝返回 kRejected。
|
|
96
|
+
[[nodiscard]] PlaceResult TryPlace(const Move& move);
|
|
70
97
|
```
|
|
71
98
|
|
|
72
|
-
|
|
99
|
+
`@param move 落子` 是把类型名翻译成中文,没有信息;`@returns` 不是 Doxygen 标签;没有说被拒绝的条件,调用方只能读实现。
|
|
73
100
|
|
|
74
101
|
**好**:
|
|
75
102
|
|
|
76
103
|
```cpp
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
best = task;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
104
|
+
/// 尝试执行一次落子。
|
|
105
|
+
///
|
|
106
|
+
/// 只有本局仍在进行、且落子玩家正是当前玩家时才继续,棋盘、轮次、历史三者一起更新。
|
|
107
|
+
///
|
|
108
|
+
/// @param move 待执行的落子,玩家必须是当前轮到的玩家。
|
|
109
|
+
/// @return 落子写入棋盘并换手时返回 kPlaced;对局已结束或上述任一条件不满足时返回 kRejected,
|
|
110
|
+
/// 棋盘和轮次都不变。
|
|
111
|
+
[[nodiscard]] PlaceResult TryPlace(const Move& move);
|
|
88
112
|
```
|
|
89
113
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
## TODO
|
|
114
|
+
## 布尔查询要写「不检查什么」
|
|
93
115
|
|
|
94
116
|
**差**:
|
|
95
117
|
|
|
96
118
|
```cpp
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// TODO(张三): 优化
|
|
119
|
+
/// 检查指定坐标上的棋盘格子是否为空。
|
|
120
|
+
[[nodiscard]] bool IsCellEmpty(CellPosition position) const;
|
|
100
121
|
```
|
|
101
122
|
|
|
102
123
|
**好**:
|
|
103
124
|
|
|
104
125
|
```cpp
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
/// 检查指定坐标上的棋盘格子是否为空。
|
|
127
|
+
///
|
|
128
|
+
/// 只回答「这一格能不能落子」,不判断对局是否已经结束:对局结束后格子仍是空的,
|
|
129
|
+
/// 但再落子没有意义。是否需要先看对局状态由调用方决定,棋盘不替它下结论。
|
|
130
|
+
///
|
|
131
|
+
/// @param position 待检查的坐标。
|
|
132
|
+
/// @return 坐标在棋盘内且该格为空时返回 true;越界或该格已有棋子时返回 false。
|
|
133
|
+
[[nodiscard]] bool IsCellEmpty(CellPosition position) const;
|
|
107
134
|
```
|
|
108
135
|
|
|
109
|
-
|
|
136
|
+
布尔函数最容易被误用;把「不检查什么」写出来,比多写一个参数说明有用。
|
|
110
137
|
|
|
111
|
-
##
|
|
138
|
+
## 枚举
|
|
112
139
|
|
|
113
140
|
**差**:
|
|
114
141
|
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
142
|
+
```cpp
|
|
143
|
+
enum class Player : std::uint8_t {
|
|
144
|
+
kCross, // X
|
|
145
|
+
kNought // O
|
|
146
|
+
};
|
|
119
147
|
```
|
|
120
148
|
|
|
121
149
|
**好**:
|
|
122
150
|
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
151
|
+
```cpp
|
|
152
|
+
/// 对局结果。
|
|
153
|
+
enum class GameResult : std::uint8_t {
|
|
154
|
+
kInProgress = 0, ///< 游戏进行中:还可以落子。
|
|
155
|
+
kCrossWin = 1, ///< X 获胜:达成三连。
|
|
156
|
+
kNoughtWin = 2, ///< O 获胜:达成三连。
|
|
157
|
+
kDraw = 3, ///< 平局:棋盘已满,双方都没有三连。
|
|
158
|
+
};
|
|
128
159
|
```
|
|
129
160
|
|
|
130
|
-
|
|
161
|
+
行尾注释先写语义,需要时用冒号补一句能核实的条件或结果,两段都短;不重复名字,不写嵌套子句,同一枚举要么全写要么全不写。
|
|
131
162
|
|
|
132
|
-
##
|
|
163
|
+
## 数据成员
|
|
133
164
|
|
|
134
165
|
**差**:
|
|
135
166
|
|
|
136
167
|
```cpp
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// ===== 结束 =====
|
|
168
|
+
Board board_;
|
|
169
|
+
Player current_player_;
|
|
170
|
+
GameResult result_;
|
|
141
171
|
```
|
|
142
172
|
|
|
143
173
|
**好**:
|
|
144
174
|
|
|
145
175
|
```cpp
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
[[nodiscard]] std::size_t Size() const;
|
|
151
|
-
[[nodiscard]] bool IsEmpty() const;
|
|
176
|
+
Board board_; // 对局棋盘。
|
|
177
|
+
Player current_player_ = Player::kCross; // 当前轮到的玩家,开局时 X 先手。
|
|
178
|
+
GameResult result_ = GameResult::kInProgress; // 对局结果;落子被拒时不改写。
|
|
152
179
|
```
|
|
153
180
|
|
|
154
|
-
|
|
181
|
+
行尾写「存什么 + 初值含义 + 什么时候变」,同类成员对齐。
|
|
155
182
|
|
|
156
|
-
##
|
|
183
|
+
## 实现注释
|
|
157
184
|
|
|
158
185
|
**差**:
|
|
159
186
|
|
|
160
187
|
```cpp
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
188
|
+
// 遍历所有空格
|
|
189
|
+
for (const auto& position : empty_cells) {
|
|
190
|
+
// 尝试落子
|
|
191
|
+
const bool wins = game.Board().CompletesLine(position, cell_type);
|
|
192
|
+
// 如果能赢
|
|
193
|
+
if (wins) {
|
|
194
|
+
// 返回这一手
|
|
195
|
+
return position;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
167
198
|
```
|
|
168
199
|
|
|
169
|
-
|
|
200
|
+
每行都在复述代码。
|
|
170
201
|
|
|
171
202
|
**好**:
|
|
172
203
|
|
|
173
204
|
```cpp
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
205
|
+
// 先找能立刻赢的格子:启发式只在「马上赢」和「马上挡」之间做选择,
|
|
206
|
+
// 不在这里做更深的前瞻,深搜留给 MinimaxAi。
|
|
207
|
+
for (const auto& position : empty_cells) {
|
|
208
|
+
if (game.CompletesLine(position, CellType::kX)) {
|
|
209
|
+
return position;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
180
212
|
```
|
|
181
213
|
|
|
182
|
-
|
|
214
|
+
解释「为什么停在这一层」,不翻译语句。
|
|
183
215
|
|
|
184
|
-
##
|
|
216
|
+
## TODO
|
|
185
217
|
|
|
186
218
|
**差**:
|
|
187
219
|
|
|
188
220
|
```cpp
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
/// @param priority 优先级
|
|
192
|
-
void SetPriority(TaskId id, int priority);
|
|
221
|
+
// TODO: 参考 xxx
|
|
222
|
+
// FIXME: 这里可能有问题
|
|
193
223
|
```
|
|
194
224
|
|
|
195
225
|
**好**:
|
|
196
226
|
|
|
197
227
|
```cpp
|
|
198
|
-
|
|
199
|
-
///
|
|
200
|
-
/// @param id 任务编号,必须已存在
|
|
201
|
-
/// @param priority 新优先级,必须在 [0, 9] 内
|
|
202
|
-
/// @throws TaskIndexError 编号不在当前任务表内
|
|
203
|
-
/// @throws InvalidPriorityError priority 越界
|
|
204
|
-
void SetPriority(TaskId id, int priority);
|
|
228
|
+
// TODO: 阶段 5 - 给 MinimaxAi 加置换表,减少重复局面的搜索。
|
|
205
229
|
```
|
|
206
230
|
|
|
207
|
-
|
|
231
|
+
有定位、有动作、能搜索。
|
|
208
232
|
|
|
209
|
-
##
|
|
233
|
+
## 配置注释
|
|
234
|
+
|
|
235
|
+
**差**:
|
|
236
|
+
|
|
237
|
+
```yaml
|
|
238
|
+
# 这是 clang-format 配置
|
|
239
|
+
# 详见某文档
|
|
240
|
+
ColumnLimit: 100
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**好**:
|
|
244
|
+
|
|
245
|
+
```yaml
|
|
246
|
+
# 列宽、缩进、包含顺序都取 Google 预设,不逐项覆盖。
|
|
247
|
+
Language: Cpp
|
|
248
|
+
BasedOnStyle: Google
|
|
249
|
+
Standard: Latest
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
解释为什么选预设,不写策略头,不指文档路径。
|
|
253
|
+
|
|
254
|
+
## 不该进注释的东西
|
|
210
255
|
|
|
211
256
|
**差**:
|
|
212
257
|
|
|
213
258
|
```cpp
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
if (tasks_[Index(id)].status == TaskStatus::kDone) return false;
|
|
219
|
-
tasks_[Index(id)].status = TaskStatus::kActive;
|
|
220
|
-
return true;
|
|
259
|
+
// 2026-03-02 把 Board 从 Game 里拆出来,因为之前 Game 太长。
|
|
260
|
+
// 参见 https://example.com/2026/03/02/refactor
|
|
261
|
+
// 其余成员见下方注释,两处不重复。
|
|
262
|
+
// 本团队要求所有类注释写满五行。
|
|
221
263
|
```
|
|
222
264
|
|
|
223
265
|
**好**:
|
|
224
266
|
|
|
225
267
|
```cpp
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
268
|
+
//===----------------------------------------------------------------------===//
|
|
269
|
+
// board.h - 棋盘状态与规则判断
|
|
270
|
+
//
|
|
271
|
+
// 这一层只有格子状态和胜负判定;回合推进在 game.h,输入输出在 apps/。
|
|
272
|
+
//===----------------------------------------------------------------------===//
|
|
231
273
|
```
|
|
232
274
|
|
|
233
|
-
|
|
275
|
+
变更史进提交说明,外部资料进文档,团队流程进规范文件,跨文件的边界写文件头;类注释只写本类型自己的契约。
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
**差**:库函数直接打印,程序只调用它。
|
|
21
21
|
|
|
22
22
|
```cpp
|
|
23
|
-
// task_list.
|
|
23
|
+
// task_list.cpp(库)
|
|
24
24
|
void TaskList::Print() const {
|
|
25
25
|
for (const auto& task : tasks_) {
|
|
26
26
|
std::cout << task.priority << " " << task.name << "\n";
|
|
@@ -36,7 +36,7 @@ void TaskList::Print() const {
|
|
|
36
36
|
// task_list.h(库)
|
|
37
37
|
[[nodiscard]] std::vector<Task> SortedByPriority() const;
|
|
38
38
|
|
|
39
|
-
// app/cli.
|
|
39
|
+
// app/cli.cpp(程序)
|
|
40
40
|
void PrintTasks(const std::vector<Task>& tasks, std::ostream& out) {
|
|
41
41
|
for (const auto& task : tasks) {
|
|
42
42
|
out << std::format("{} {}\n", task.priority, task.name);
|
|
@@ -63,7 +63,7 @@ src/detail.h // 私有头被测试直接包含
|
|
|
63
63
|
|
|
64
64
|
```text
|
|
65
65
|
task_list/include/task_list.h // 公共头
|
|
66
|
-
task_list/src/task_list.
|
|
66
|
+
task_list/src/task_list.cpp // 实现
|
|
67
67
|
task_list/src/detail.h // 私有头,只被本组件包含
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -75,7 +75,7 @@ task_list/src/detail.h // 私有头,只被本组件包含
|
|
|
75
75
|
|
|
76
76
|
## 单文件何时拆库
|
|
77
77
|
|
|
78
|
-
**先不拆**:一个 `main.
|
|
78
|
+
**先不拆**:一个 `main.cpp`,辅助函数在匿名命名空间,只有程序自己用。
|
|
79
79
|
|
|
80
80
|
**该拆**:测试需要直接调用排序逻辑;或第二个程序要复用同一逻辑;或逻辑已经超过一个屏幕。
|
|
81
81
|
|