@chengzhao-dev/dsh-cpp-workflow 0.3.0 → 0.5.0

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 (64) hide show
  1. package/README.en.md +14 -11
  2. package/README.md +14 -11
  3. package/lib/index.js +4 -4
  4. package/lib/workspace.js +1 -56
  5. package/locale/en.json +1 -1
  6. package/locale/zh.json +1 -1
  7. package/package.json +2 -2
  8. package/skills/building-cpp/SKILL.md +25 -12
  9. package/skills/building-cpp/assets/config/.clang-tidy +1 -0
  10. package/skills/building-cpp/assets/templates/projects/multi/CMakeLists.txt +2 -2
  11. package/skills/building-cpp/assets/templates/projects/shared-library/CMakeLists.txt +1 -1
  12. package/skills/building-cpp/assets/templates/projects/shared-library/greeting/CMakeLists.txt +1 -1
  13. package/skills/building-cpp/assets/templates/projects/single/CMakeLists.txt +1 -1
  14. package/skills/building-cpp/assets/templates/projects/static-library/CMakeLists.txt +1 -1
  15. package/skills/building-cpp/assets/templates/projects/static-library/greeting/CMakeLists.txt +1 -1
  16. package/skills/building-cpp/examples/tooling.md +3 -3
  17. package/skills/building-cpp/references/build-script.md +11 -0
  18. package/skills/building-cpp/references/linking.md +7 -7
  19. package/skills/building-cpp/references/standards/README.md +1 -1
  20. package/skills/building-cpp/references/static-analysis.md +3 -3
  21. package/skills/building-cpp/steps/01-build.md +22 -35
  22. package/skills/building-cpp/steps/02-tooling.md +2 -2
  23. package/skills/configuring-cmake/SKILL.md +57 -0
  24. package/skills/configuring-cmake/assets/cmake/CompileSettings.cmake +126 -0
  25. package/skills/configuring-cmake/assets/cmake/LinkSettings.cmake +48 -0
  26. package/skills/configuring-cmake/examples/cmake.md +224 -0
  27. package/skills/{building-cpp → configuring-cmake}/references/cmake.md +54 -48
  28. package/skills/configuring-cmake/references/cxx-standard.md +67 -0
  29. package/skills/configuring-cmake/references/modules.md +105 -0
  30. package/skills/configuring-cmake/steps/01-targets.md +53 -0
  31. package/skills/configuring-cmake/steps/02-modules.md +37 -0
  32. package/skills/configuring-cmake/steps/03-check.md +39 -0
  33. package/skills/developing-cpp/SKILL.md +17 -3
  34. package/skills/developing-cpp/examples/comments.md +173 -136
  35. package/skills/developing-cpp/examples/exceptions.md +38 -19
  36. package/skills/developing-cpp/examples/structure.md +75 -4
  37. package/skills/developing-cpp/references/comments.md +82 -99
  38. package/skills/developing-cpp/references/declaration-order.md +4 -2
  39. package/skills/developing-cpp/references/exceptions.md +12 -13
  40. package/skills/developing-cpp/references/file-template.md +131 -0
  41. package/skills/developing-cpp/references/glossary.md +3 -3
  42. package/skills/developing-cpp/references/google-cpp-style.md +8 -8
  43. package/skills/developing-cpp/references/language.md +4 -3
  44. package/skills/developing-cpp/references/layout.md +21 -12
  45. package/skills/developing-cpp/references/naming.md +5 -4
  46. package/skills/developing-cpp/references/project-design.md +1 -1
  47. package/skills/developing-cpp/references/standard-library.md +2 -1
  48. package/skills/developing-cpp/references/types.md +1 -2
  49. package/skills/developing-cpp/scripts/checking-comments.mjs +371 -0
  50. package/skills/developing-cpp/steps/01-behavior.md +1 -1
  51. package/skills/developing-cpp/steps/02-boundary.md +4 -4
  52. package/skills/developing-cpp/steps/03-declaration.md +7 -7
  53. package/skills/developing-cpp/steps/04-implementation.md +6 -5
  54. package/skills/building-cpp/assets/cmake/applying-warnings.cmake +0 -10
  55. package/skills/building-cpp/assets/cmake/fetching-googletest.cmake +0 -15
  56. package/skills/building-cpp/assets/cmake/setting-rpath.cmake +0 -8
  57. package/skills/building-cpp/examples/cmake.md +0 -150
  58. /package/skills/building-cpp/assets/templates/projects/multi/src/{greeting.cc → greeting.cpp} +0 -0
  59. /package/skills/building-cpp/assets/templates/projects/multi/src/{main.cc → main.cpp} +0 -0
  60. /package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/{greeting.cc → greeting.cpp} +0 -0
  61. /package/skills/building-cpp/assets/templates/projects/shared-library/src/{main.cc → main.cpp} +0 -0
  62. /package/skills/building-cpp/assets/templates/projects/single/src/{main.cc → main.cpp} +0 -0
  63. /package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/{greeting.cc → greeting.cpp} +0 -0
  64. /package/skills/building-cpp/assets/templates/projects/static-library/src/{main.cc → main.cpp} +0 -0
@@ -1,143 +1,126 @@
1
1
  # 注释
2
2
 
3
- 注释分四层:文件头、章节横幅、声明注释、实现注释。配置文件的注释单独一层。每层只写该层该管的事,不互相复制。
3
+ 注释分四层:文件头、类型注释、函数注释、实现注释;配置文件的注释另算。层级越靠外,越讲概念与契约;越靠里,越讲实现理由。同一件事只写一次。
4
4
 
5
- ## 文件头
5
+ ## 总则
6
6
 
7
- 每个手写源文件、脚本、配置文件开头写一段文件头:这个文件负责什么、边界在哪里、什么时候需要读它。不要写版权年份、作者、变更日志。
7
+ - **第一句先写结果或限制**:类型给调用方什么,函数正常返回什么、失败时返回什么或抛什么,调用方必须守住哪条边界。第二句以后才写条件、参数与实现理由。
8
+ - 公共注释回答「这是什么」和「调用方必须遵守什么」;实现注释回答「为什么这样写」和「边界在哪里」。设计取舍、方案对比与演进历史写到书稿、README 或提交说明。
9
+ - 用中文散文写完整句子,句号结尾;标识符、标准库类型、`@param` 这类标签保持原文。
10
+ - 行宽跟项目 `.clang-format` 的 `ColumnLimit` 走(Google 基线是 80 列,中文按两列计);横幅与列宽同宽,不另设数字。
11
+ - 写之前三问:删掉这句,读者会误解设计或做错事吗?这句只是复述签名或代码吗?这个限制能不能由类型、`const` 或 `[[nodiscard]]` 表达?第一问为否就删,第二问为是就删,第三问为是就改代码。
12
+ - 命名、分文件、`enum class`、异常与结果类型这类全项目风格约定只写进项目文档与读者正文;注释不替项目解释风格。
8
13
 
9
- ```cpp
10
- //===----------------------------------------------------------------------===//
11
- // TaskTable 的存储、编号检查与状态更新规则。
12
- //
13
- // 不处理终端输入输出;程序层负责把用户输入翻译成 TaskId。
14
- //===----------------------------------------------------------------------===//
15
- ```
14
+ ## `///` 与 `//` 的分工
15
+
16
+ - `///` 只给声明:头文件里的类型、函数、枚举值与公开数据成员(行尾补一句用 `///<`)。调用方和 Doxygen 从这里取契约。
17
+ - `//` 给实现:函数体内的步骤与边界说明、局部变量、文件头横幅、配置文件的说明。
18
+ - 不给局部变量和函数体里的语句写 `///`;文件头也不写成 `///`,否则编辑器会把它挂到紧随其后的第一个声明上。
19
+ - 判断看一句话写的是「调用方要遵守的承诺」还是「维护者要知道的写法」:前者用 `///`,后者用 `//`。
20
+
21
+ ## 长度预算
16
22
 
17
- - C++ 文件用 `//`,CMake 用 `#`,YAML/JSON 用 `#` 或 `//`(按文件类型)。
18
- - 文件头控制在 6 行以内;不写实现步骤,不写外部资料路径或项目专属类名。
19
- - 生成文件、第三方文件、纯数据文件可以不写文件头。
23
+ | 层 | 行数 | 第一句 | 写什么 |
24
+ | --- | --- | --- | --- |
25
+ | 文件头 | 3–6 | 这个文件是什么 | 由哪几部分组成、不负责什么 |
26
+ | 类型注释 | 6–14 | 这个类型给调用方什么 | 概念、使用约定 |
27
+ | 函数注释 | 3–10 | 正常返回什么、失败时返回或抛什么 | 参数与失败方式 |
28
+ | 数据成员 | 行尾 1 句 | 存什么 | 范围或单位、初值的含义 |
29
+ | 实现注释 | 1–3 | 为什么这样写或边界在哪里 | 代价 |
20
30
 
21
- ## 章节横幅
22
31
 
23
- 长文件(超过约 200 行)用横幅分组,组名是读者要跳转的锚点。
32
+ ## 文件头
33
+
34
+ 每个手写源文件开头写一段文件头:这个文件负责什么、分成哪几块、不负责什么。不写作者、版权年份、变更日志和外部资料路径。
24
35
 
25
36
  ```cpp
26
37
  //===----------------------------------------------------------------------===//
27
- // 构造与生命周期
38
+ // board.h - 定义井字棋棋盘。
39
+ //
40
+ // 棋盘只保存格子状态与规则判断,不含对局流程;对局流程在 game.h。
28
41
  //===----------------------------------------------------------------------===//
29
42
  ```
30
43
 
31
- - 横幅宽度固定 80 列;`//` 后 3 个 `=`,中间 70 个 `-`,末尾 `===//`。
32
- - 横幅只包住逻辑组,不包住单个函数;函数之间用空行,不用横幅。
33
- - 脚本里用 `#===` 形式,宽度同样 80 列;CMake 用 `#---` 小节即可,不用重横幅。
34
- - 一个文件不超过 8 个横幅;超过说明文件该拆了。
35
- - 80 列换行分三类:整句在标点处断;参数/返回值列表每项一行;代码示例保持原样,宁可横向滚动也不在半句断开。
44
+ - C++ 用 `//`,CMake 用 `#`,YAML/JSON 用 `#` 或 `//`。
45
+ - 横幅宽度等于 `ColumnLimit`:`//===` 起头、`===//` 收尾,中间补 `-`;章节横幅同一格式。
46
+ - 超过约 200 行的文件用章节横幅分组,组名是读者要跳转的锚点;一个文件不超过 8 个横幅。
47
+
48
+ ## 类型注释
49
+
50
+ 对象是类、结构体、枚举和类型别名。写两段,段间空一行:
36
51
 
37
- ## 声明注释
52
+ 1. 结果:这个类型是什么、给谁用、保证什么。
53
+ 2. 限制:本类型不变量、调用方必须遵守的条件、失败方式与所有权。
38
54
 
39
- 公共头文件里的类型、函数、常量用 `///` 写契约,调用方读完就知道怎么用。
40
55
 
41
56
  ```cpp
42
- /// 返回任务编号对应的状态。
57
+ /// 井字棋棋盘:保存九格状态,并回答满盘、成线与胜负。
43
58
  ///
44
- /// @param id 任务编号,必须在 [0, kTaskCount) 内
45
- /// @return 该编号的任务状态
46
- /// @throws TaskIndexError id 越界
47
- [[nodiscard]] TaskState GetTaskState(int id) const;
59
+ /// 格子数量固定为 3×3;落子只改变单个格子,不移动其他格子。
60
+ class Board {
48
61
  ```
49
62
 
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` 这类空壳标签;第一行本身就是摘要。
59
63
 
60
- ## 实现注释
64
+ 禁止事项:
61
65
 
62
- `.cc` 里的注释只写「为什么」和「这里有什么坑」,不复述语句。
66
+ - 不写成员清单,不按声明顺序复述接口。函数各自的契约写在函数上。
67
+ - 不写跨类分工。边界靠类型与所有权表达,注释只写本类型保证什么。
68
+ - 不写类型系统已经表达的性质(`值语义`、`所有查询都是 const`)。
69
+ - 不写尚未发生的用法、方案对比与取舍论证、广告句。
70
+ - 枚举与只承载数据的结构体:第一句写这个取值表示什么,需要时用冒号补一小句可核实的条件。
63
71
 
64
- ```cpp
65
- // 先判空再取首元素:空订单列表是合法状态,不能假设至少有一单。
66
- if (orders_.empty()) {
67
- return OrderResult::kInProgress;
68
- }
69
- ```
72
+ ## 函数注释
70
73
 
71
- - 不写「遍历 slots_,检查是否超过阈值」这种把下一行翻译一遍的注释。
72
- - 注释放在被解释语句的上方;只有短小的状态说明可以放行尾,且一行只挂一个语句。
73
- - 不要把注释挂在 `if` 和它的 `else` 中间;分支各自的理由写在分支内部上方。
74
- - 一个注释只解释一件事;解释两件事时拆成两行或两个注释。
75
- - 相邻的单行注释合成一个注释块,一行一个要点;不要把整段说明挤进一个长行。
76
- - 处理溢出、越界回退、平台差异时,注释写「为什么这个分支存在」和「不这样会怎样」,不写「这里做了检查」。
77
- - 不写「这里用了 vector 因为……」「这里改成……更好」这类实现史;设计取舍写进提交说明或文档。
78
- - 注释掉的代码不保留;需要时从版本历史取。
74
+ 两段式,短函数可以只留第一段:
79
75
 
80
- ## TODO
76
+ 1. 结果或限制:这个函数给调用方什么结果;失败时返回什么、抛什么,或声明它不抛。
77
+ 2. 契约:`@param`、`@return`、`@throws`,写范围、单位、所有权和失败方式。
81
78
 
82
- 统一格式:
83
79
 
84
80
  ```cpp
85
- // TODO: 阶段 3 - 支持撤销任务状态更新
86
- // TODO: issue #42 - 把编号检查抽到 TaskTable
81
+ /// 放置成功返回 true;坐标越界、目标已占或要写空格子时返回 false,不抛异常。
82
+ ///
83
+ /// @param position 目标行列坐标。
84
+ /// @param cell_type 要放置的棋盘格子类型,必须是 kX 或 kO。
85
+ [[nodiscard]] bool PlaceCell(CellPosition position, CellType cell_type);
87
86
  ```
88
87
 
89
- - `TODO:` 后先写定位(阶段名或 issue 号),再写一个短横线,再写动作。
90
- - 不写 `TODO(某人)`;人不重要,上下文重要。
91
- - 不写没有动作的 `TODO: 参考 xxx`;要么写清要做什么,要么删掉。
92
- - 提交前搜一遍 `TODO`,能做完的做完,做不完的保留定位。
88
+ - 布尔查询:`@return` 写「什么情况下为 true」,再补一句「不检查什么」。
89
+ - `@param` 要么全写,要么全不写;写范围、单位或所有权,不写「索引」这类同义反复。
90
+ - `@return` 在返回类型自明时可以省略;`[[nodiscard]]` 已表达「必须使用」时不再重复。
91
+ - `@throws` 必须与 `references/exceptions.md` 一致;不抛异常的函数不写。
92
+ - 私有函数同样写契约,不能默认调用者知道前置条件;输出参数标 `[out]`,既读又写标 `[in,out]`。
93
93
 
94
- ## 语言与术语
94
+ ## 数据成员与实现注释
95
95
 
96
- - 注释用中文;标识符、标准库类型、命令、错误消息保持原文。
97
- - 同一个概念只用一种译法;术语表见 `references/glossary.md`。
98
- - 不写「显然」「众所周知」;读者不需要被评判。
99
- - 注释不超过两行连续说明;超过时先问是不是该抽函数或写文档。
96
+ 数据成员用行尾注释,写「存什么 + 范围或单位 + 初值的含义」,同类成员对齐。不复述 `cells_[row][col]` 这类存储布局:数组维度和访问方式由声明表达。
100
97
 
101
- ## 配置注释
98
+ ```cpp
99
+ Board board_; // 对局棋盘。
100
+ Player current_player_ = Player::kCross; // 当前轮到的玩家,开局时 X 先手。
101
+ ```
102
102
 
103
- `.clang-format`、`.clang-tidy`、`.clangd`、CMake 的配置项旁边写「为什么这样设」,不写「这一项是什么」。
103
+ 实现注释只写三样东西:为什么这样写、这里的边界条件、这样做的代价。1–3 行,放在被解释语句上方;第一句先写理由或边界,不复述代码。
104
104
 
105
- ```yaml
106
- # 列宽、缩进、包含顺序都取 Google 预设,不逐项覆盖。
107
- Language: Cpp
108
- BasedOnStyle: Google
109
- Standard: Latest
110
- ```
105
+ `.cpp` / `.cc` 的函数体默认**不写**注释:公开契约已经在头文件的 `///` 里,实现再写一遍只会随代码漂移。大型工程的共识是把接口说明留在声明上——[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html#Implementation_Comments) 只在难懂、不明显的地方写实现注释,并明确不要描述代码本身;[LLVM Coding Standards](https://www.llvm.org/docs/CodingStandards.html#commenting) 同样要求写意图而不是逐步翻译。文件头横幅保留,它对应两家的文件说明,不是函数体解说。
111
106
 
112
- - 配置注释贴在被解释的键上方,不写大段策略头。
113
- - `.clang-format` 按逻辑组分段,每组一句注释说明这一组在管什么;不要逐键写「设置 X 为 Y」。
114
- - `.clangd` 每个键组一句话,说明为什么需要这个编译数据库或参数;不要复制整份 CMake 配置。
115
- - 不在配置里写 `TODO` 或指向某份文档的路径;配置要能独立读懂。
116
- - `.clang-tidy` 的每个关闭项写一句理由;没有理由的关闭项删掉。
117
- - 同一个理由只写一次,不逐项复制。
118
- - 源码注释不写团队政策或流程;那些属于文档,不属于被阅读的代码。
107
+ 唯一保留的一类:删掉这句话,下一次修改会得到错误结果,而且代码本身看不出那个结果。句子要写出错误结果,不写「省掉一次扫线」这类口号。典型位置:
119
108
 
120
- ## 润色次序
109
+ - 次序依赖:先判胜再判满,否则最后一子会变成平局。
110
+ - 类型收窄与解析:先按 64 位读再收窄;解析失败后必须丢掉整行。
111
+ - 状态不变量、外部格式、回退路径、刻意重复。
112
+ - 共享方式:`thread_local` 之类的可见性。
121
113
 
122
- 只改注释时,仍走第 3 步和第 4 步,然后按这个次序回读:
114
+ 循环体里逐行复述 `push_back`、`return` 的注释一律删;不写变更史、外部资料路径和注释掉的代码。CMake 的契约注释不在这里,写法见 `../../configuring-cmake/references/cmake.md`。
123
115
 
124
- 1. 文件头:这个文件负责什么、不负责什么,6 行以内。
125
- 2. 章节横幅:文件超过约 200 行才分组。
126
- 3. 声明契约:`///` 写参数、返回值、失败方式;与 `references/exceptions.md` 一致。
127
- 4. 成员行尾:先写保存的对象,再写约束。
128
- 5. 实现注释:只写为什么和边界,不翻译下一行。
129
- 6. 配置注释:按 `building-cpp` 的定工具写;只写为什么这样设。
130
- 7. 回读:同一概念一种译法;不把读者章节的句子抄进注释。
116
+ ## TODO、配置、改写次序与边界
131
117
 
132
- ## 与读者正文的边界
118
+ `TODO:` 后先写定位(阶段名或 issue 号),再写一个短横线,再写动作;不写 `TODO(某人)`,不写没有动作的 TODO。
133
119
 
134
- - 中文、术语稳定、不复述下一行;两边各自写自己的文件。
135
- - 句长、段落、标点、书根 README 不在本插件。
136
- - `///`、横幅、`@param` 不进入 qmd、YAML、CSS。
120
+ `.clang-format`、`.clang-tidy`、`.clangd`、CMake 的配置项旁边写「为什么这样设」,不写「这一项是什么」;配置注释贴在被解释的键上方。
137
121
 
138
- ## 禁止
122
+ 只改注释时仍走第 3、4 步,然后回读:文件头 → 类型(结果/限制 + 约定)→ 函数(结果/失败方式 + 参数)→ 数据成员(存什么/约束/初值)→ 实现(为什么/边界)→ 术语与新符号是否真实存在 → 运行 `checking-comments.mjs` 兜底。
139
123
 
140
- - 注释里写外部资料路径、项目专属类名或跨仓库引用。
141
- - 用注释代替类型、`const`、`[[nodiscard]]` 能表达的限制。
142
- - 大段 ASCII 表格、装饰性图案、表情符号。
143
- - 把注释当提交说明或变更日志。
124
+ - 注释写给维护者,正文写给读者;设计理由、方案比较与算法取舍写在正文或实现注释里,公开接口只留契约。
125
+ - `///`、`@param`、横幅不进入 `.qmd`、YAML、CSS。
126
+ - 禁止:用注释代替类型/`const`/`[[nodiscard]]`;大段表格、装饰图案、表情;外部资料路径、跨仓库引用、团队流程;成员功能清单;注释元叙述(「其余内容见某处」「两处不重复」)。
@@ -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
  ## 空行
@@ -37,21 +37,18 @@ class ParseError : public std::runtime_error {
37
37
  };
38
38
  ```
39
39
 
40
- 标准库异常只做两种用途:作为基类,或在确实没有更多信息时直接使用通用类型(如 `std::runtime_error`)。不要为了「看起来规范」而强制每个参数错误都抛 `std::invalid_argument`、每个越界都抛 `std::out_of_range`;有领域语义时就定义自己的类型,让调用方按语义捕获。
40
+ 标准库已经命名的失败直接用标准类型:坐标越界用 `std::out_of_range`,调用时不变量已破坏用 `std::logic_error`,正常输入里的解析失败可以用 `std::runtime_error`。只有调用方必须把这些失败和上述类型区分开时,才新增派生自它们的类型;不要为了「看起来规范」给每个参数错误都建一支。项目若定义自己的异常基类(如 `Error`),先定基类再按领域派生,调用方捕获基类就能接住整族异常。
41
41
 
42
- ### Boost.Exception 的类型层级
42
+ ## 两种构建模式的三层合同
43
43
 
44
- Boost.Exception 展示了「异常类型只做语义标签」的做法:类型不带数据,用多继承表达一个错误同时属于多个类别,数据由 `error_info` 在抛出点或传播途中携带。异常类型之间用 virtual 继承,避免 `catch (const std::exception&)` 在多继承下出现二义性。
44
+ 库的失败方式在 Debug 与 Release 下必须可预测;只有诊断信息的详略可以不同。
45
45
 
46
- ```cpp
47
- struct exception_base : virtual std::exception, virtual boost::exception {};
48
- struct io_error : virtual exception_base {};
49
- struct file_error : virtual io_error {};
50
- struct read_error : virtual io_error {};
51
- struct file_read_error : virtual file_error, virtual read_error {};
52
- ```
46
+ - 返回值:两种模式完全一致。函数用返回值报告可恢复失败时,Debug 不得改成抛异常,Release 也不得把异常改成 false。
47
+ - 标准异常:两种模式的抛出条件相同。越界抛 `std::out_of_range`,前置条件被破坏抛 `std::logic_error`;项目若定义 `Error` 基类,派生类型同时派生自 `std::exception`,Debug 的 `what()` 可带 `file:line`,Release 只留值。
48
+ - `Check`:项目保留这种断言时,两种模式都检查,失败时打印诊断并终止;用于 invariant 已被破坏、继续执行没有意义的场合。没有 `Check` 的项目直接用 `assert` 或 `std::terminate` 表达同一边界,不另写包装。
49
+ - `DebugCheck`:项目保留时只在 Debug 生效,`NDEBUG` 后展开为空;用于代价高、只在开发期值得做的自检。不得用它承担 Release 也必须执行的检查。
53
50
 
54
- 这段代码只作为**设计典范**,不要求工程引入 Boost。若项目已有自己的异常基类,照同样的原则做:语义标签用类型表达,共享数据放在基类或错误信息容器里,多继承时用 virtual 基类。`boost::exception` 不直接派生 `std::exception`,正是为了让用户类型能以 virtual 基类方式同时派生两者;这是 Koenig 的洞察,异常处理路径上的虚继承开销可以忽略。
51
+ `Check` 不是 `[[noreturn]]`:条件成立时正常返回。`DebugCheck` 的参数在 Release 不求值,不能放入有副作用的表达式。
55
52
 
56
53
  ## 抛出与捕获
57
54
 
@@ -72,7 +69,7 @@ E.31:`catch` 子句从派生到基类、从具体到通用排序,`catch (...
72
69
 
73
70
  ## 不抛的边界
74
71
 
75
- E.12:在不可能或不可接受抛异常的地方用 `noexcept`。E.13:直接拥有资源的函数不要抛,资源释放应交给 RAII。E.16:析构、`deallocate`、`swap` 以及异常类型的拷贝/移动构造不得失败;析构和 `swap` 应当是 `noexcept`。
72
+ E.12:在不可能或不可接受抛异常的地方用 `noexcept`。声明前先读实现:函数体里出现 `at()` 或其他可能抛出的调用,就去掉 `noexcept`。正例是只比较两个整数的 `IsInRange`;反例是用 `at()` 写入并可能抛 `std::out_of_range` 的 `PlaceCell`。E.13:直接拥有资源的函数不要抛,资源释放应交给 RAII。E.16:析构、`deallocate`、`swap` 以及异常类型的拷贝/移动构造不得失败;析构和 `swap` 应当是 `noexcept`。
76
73
 
77
74
  E.30:不要写异常规范(`throw(...)` 之类);C++11 之后用 `noexcept` 表达「不抛」。需要无异常构建时,由项目单独决定,不要把这套异常指导改写成两面都对的模糊话。
78
75
 
@@ -113,4 +110,6 @@ Config ReadConfig(const std::filesystem::path& path);
113
110
  - [ ] 析构、`swap`、异常类型的拷贝/移动不抛。
114
111
  - [ ] 没有用异常做正常分支,也没有在每个函数里捕获所有异常。
115
112
  - [ ] 入口有统一捕获,消息可定位,退出码符合项目合同。
116
- - [ ] 公共声明的 `@throws` 与实际抛出类型一致。
113
+ - [ ] 公共声明的 `@throws` 与实际抛出类型一致,且 Debug/Release 的抛出条件相同。
114
+ - [ ] 每个 `noexcept` 函数的函数体不调用 `at()` 或其他会抛的接口。
115
+ - [ ] 项目若保留 `Check` / `DebugCheck`:`Check` 两模式都生效,`DebugCheck` 只在 Debug 生效;没有让 Debug 抛、Release 返回 false。
@@ -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,8 +6,9 @@
6
6
 
7
7
  - 类型决定允许的操作和表示;对象是某类型的一块存储;值是可以复制、比较、传递的内容。
8
8
  - 变量有名字、类型、存储期和作用域。局部变量在离开作用域时销毁;`static` 局部变量在首次执行到声明时初始化,程序结束时销毁。
9
- - 声明让名字可见,定义分配存储或给出函数体。一个实体可以声明多次,但只能定义一次;头文件只放声明,定义放 `.cc`。
9
+ - 声明让名字可见,定义分配存储或给出函数体。一个实体可以声明多次,但只能定义一次;头文件只放声明,定义放 `.cpp`。
10
10
  - 引用是已有对象的别名,必须初始化,不能重新绑定;指针可以为空,也可以改指向。接口优先用引用表达“必须存在”,用指针表达“可以不存在”。
11
+ - `*this` 不用于复制当前对象:要一份副本就直说语义(如“推演落子”只读原数据,或显式写一个复制函数)。`return *this` 仍只属于赋值运算符,井字棋没有这种运算符。
11
12
 
12
13
  ## 初始化与列表初始化
13
14
 
@@ -72,7 +73,7 @@ auto total = 0.0; // auto 只能用 =,不能用 {}
72
73
  - `struct` 用于公开数据的聚合;`class` 用于维护不变量、隐藏表示。两者只差默认访问权限,按语义选。
73
74
  - 数据成员默认私有;需要暴露只读视图时提供 `const` 成员函数,不返回可变内部引用。
74
75
  - 单参数构造函数用 `explicit`;构造函数失败时抛异常,不留下半初始化对象。
75
- - 特殊成员(构造、析构、拷贝、移动)在头文件声明,在 `.cc` 定义;只有默认实现足够且零参数时才写 `= default`。
76
+ - 特殊成员(构造、析构、拷贝、移动)在头文件声明,在 `.cpp` 定义;只有默认实现足够且零参数时才写 `= default`。
76
77
  - 定义了析构函数、拷贝构造或拷贝赋值之一时,认真考虑其余特殊成员;用 `= delete` 明确禁止不需要的操作。
77
78
  - 多态基类析构函数必须是 `virtual` 或 `protected`;否则通过基类指针删除派生对象是未定义行为。
78
79
  - 不在构造函数里调用虚函数;此时派生部分还没构造完。
@@ -89,7 +90,7 @@ auto total = 0.0; // auto 只能用 =,不能用 {}
89
90
 
90
91
  ## 模板、概念与 CTAD
91
92
 
92
- - 模板定义放头文件;`.cc` 里的模板实例化只对当前翻译单元可见。
93
+ - 模板定义放头文件;`.cpp` 里的模板实例化只对当前翻译单元可见。
93
94
  - C++20 用概念约束模板参数,把错误从实例化深处提前到调用点:
94
95
 
95
96
  ```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
  - 测试不复制实现,不依赖执行顺序,不读写固定绝对路径。