@chengzhao-dev/dsh-cpp-workflow 0.1.1 → 0.2.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.
- package/README.en.md +18 -15
- package/README.md +18 -15
- package/lib/index.js +7 -8
- package/lib/workspace.js +14 -14
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +5 -4
- package/skills/building-cpp/SKILL.md +6 -6
- package/skills/building-cpp/assets/config/.clang-format +3 -38
- package/skills/building-cpp/assets/config/.clang-tidy +8 -39
- package/skills/building-cpp/assets/config/.clangd +2 -5
- package/skills/building-cpp/assets/config/.vscode/extensions.json +2 -1
- package/skills/building-cpp/assets/config/.vscode/settings.json +16 -1
- package/skills/building-cpp/assets/templates/projects/common/running-build.sh +29 -18
- package/skills/building-cpp/assets/templates/projects/multi/CMakeLists.txt +9 -5
- package/skills/building-cpp/assets/templates/projects/multi/include/greeting.h +5 -3
- package/skills/building-cpp/assets/templates/projects/multi/src/greeting.cc +18 -0
- package/skills/building-cpp/assets/templates/projects/multi/src/main.cc +8 -0
- package/skills/building-cpp/assets/templates/projects/shared-library/CMakeLists.txt +8 -3
- package/skills/building-cpp/assets/templates/projects/shared-library/greeting/CMakeLists.txt +15 -5
- package/skills/building-cpp/assets/templates/projects/shared-library/greeting/include/greeting.h +5 -3
- package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/greeting.cc +18 -0
- package/skills/building-cpp/assets/templates/projects/shared-library/src/main.cc +8 -0
- package/skills/building-cpp/assets/templates/projects/single/CMakeLists.txt +7 -3
- package/skills/building-cpp/assets/templates/projects/single/src/main.cc +15 -0
- package/skills/building-cpp/assets/templates/projects/static-library/CMakeLists.txt +8 -3
- package/skills/building-cpp/assets/templates/projects/static-library/greeting/CMakeLists.txt +15 -5
- package/skills/building-cpp/assets/templates/projects/static-library/greeting/include/greeting.h +5 -3
- package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/greeting.cc +18 -0
- package/skills/building-cpp/assets/templates/projects/static-library/src/main.cc +8 -0
- package/skills/building-cpp/examples/cmake.md +17 -17
- package/skills/building-cpp/examples/environment.md +4 -3
- package/skills/building-cpp/examples/tooling.md +31 -16
- package/skills/building-cpp/references/build-script.md +2 -2
- package/skills/building-cpp/references/cmake.md +35 -17
- package/skills/building-cpp/references/environment.md +17 -13
- package/skills/building-cpp/references/linking.md +6 -6
- package/skills/building-cpp/references/standards/README.md +165 -0
- package/skills/building-cpp/references/standards/c++11/README.md +50 -0
- package/skills/building-cpp/references/standards/c++11/cmake-snippet.cmake +7 -0
- package/skills/building-cpp/references/standards/c++14/README.md +48 -0
- package/skills/building-cpp/references/standards/c++14/cmake-snippet.cmake +7 -0
- package/skills/building-cpp/references/standards/c++17/README.md +49 -0
- package/skills/building-cpp/references/standards/c++17/cmake-snippet.cmake +7 -0
- package/skills/building-cpp/references/standards/c++20/README.md +49 -0
- package/skills/building-cpp/references/standards/c++20/cmake-snippet.cmake +7 -0
- package/skills/building-cpp/references/standards/c++23/README.md +50 -0
- package/skills/building-cpp/references/standards/c++23/cmake-snippet.cmake +7 -0
- package/skills/building-cpp/references/static-analysis.md +27 -15
- package/skills/building-cpp/scripts/checking-environment.mjs +21 -21
- package/skills/building-cpp/steps/01-build.md +10 -8
- package/skills/building-cpp/steps/02-tooling.md +9 -7
- package/skills/building-cpp/steps/03-check.md +4 -4
- package/skills/developing-cpp/SKILL.md +6 -6
- package/skills/developing-cpp/examples/comments.md +10 -8
- package/skills/developing-cpp/examples/exceptions.md +64 -45
- package/skills/developing-cpp/examples/naming.md +2 -2
- package/skills/developing-cpp/examples/structure.md +12 -9
- package/skills/developing-cpp/references/comments.md +10 -8
- package/skills/developing-cpp/references/declaration-order.md +7 -4
- package/skills/developing-cpp/references/exceptions.md +83 -46
- package/skills/developing-cpp/references/glossary.md +6 -6
- package/skills/developing-cpp/references/google-cpp-style.md +27 -37
- package/skills/developing-cpp/references/language.md +4 -4
- package/skills/developing-cpp/references/layout.md +14 -14
- package/skills/developing-cpp/references/naming.md +7 -6
- package/skills/developing-cpp/references/project-design.md +3 -3
- package/skills/developing-cpp/references/standard-library.md +1 -1
- package/skills/developing-cpp/steps/02-boundary.md +7 -7
- package/skills/developing-cpp/steps/03-declaration.md +9 -8
- package/skills/developing-cpp/steps/04-implementation.md +11 -7
- package/templates/cpp-workflow-project/SKILL.md +1 -1
- package/templates/cpp-workflow-project/environment.json +2 -1
- package/skills/building-cpp/assets/templates/projects/multi/src/greeting.cpp +0 -13
- package/skills/building-cpp/assets/templates/projects/multi/src/main.cpp +0 -8
- package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/greeting.cpp +0 -13
- package/skills/building-cpp/assets/templates/projects/shared-library/src/main.cpp +0 -8
- package/skills/building-cpp/assets/templates/projects/single/src/main.cpp +0 -9
- package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/greeting.cpp +0 -13
- package/skills/building-cpp/assets/templates/projects/static-library/src/main.cpp +0 -8
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# 静态分析与语言服务
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
三件语言工具各管一段:`clang-format` 排版、`clang-tidy` 诊断、`clangd` 提供补全/跳转/诊断。它们都读同一份编译数据库,但职责不重叠。编辑器侧只保留 CMake Tools 作为配置、构建、运行和调试入口;C/C++ 扩展只做 `CMake: Debug` 的调试后端,不参与语言服务。
|
|
4
4
|
|
|
5
5
|
## clang-format
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
clang-format --dry-run --Werror src/*.
|
|
8
|
+
clang-format --dry-run --Werror src/*.cc include/*.h tests/*.cc
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
- 检查用 `--dry-run --Werror`;需要自动修复时单独跑 `clang-format -i`,不要在构建脚本里悄悄改源码。
|
|
12
12
|
- 工程必须有根 `.clang-format`;缺失时先复制配置,不要回退到内置风格,回退会掩盖配置缺失。
|
|
13
13
|
- 只扫描项目源码:`src/`、`include/`、`apps/`、`tests/` 等实际存在的目录;不扫描 `build/`、第三方、生成文件。
|
|
14
14
|
- 配置文件在工程根 `.clang-format`;不要依赖用户主目录或编辑器内置配置。
|
|
15
|
-
- `
|
|
15
|
+
- `BasedOnStyle: Google`、`Standard: Latest`;clang-format 21/22 不接受 `c++23`,`Latest` 按工具支持的最高标准解析。列宽 80、2 空格、包含顺序都取 Google 预设,不另写 `ColumnLimit` 或 `IncludeCategories`。
|
|
16
16
|
- 格式化失败就是失败,不要用 `// clang-format off` 绕过;确需关闭时只包住无法排版的宏或表格,并写一句理由。
|
|
17
17
|
|
|
18
18
|
## clang-tidy
|
|
@@ -21,7 +21,7 @@ clang-format --dry-run --Werror src/*.cpp include/*.h tests/*.cpp
|
|
|
21
21
|
clang-tidy -p build/clang \
|
|
22
22
|
--header-filter="^$(pwd -P)/(include|src|apps|tests)/" \
|
|
23
23
|
--warnings-as-errors='*' \
|
|
24
|
-
src/*.
|
|
24
|
+
src/*.cc apps/*.cc tests/*.cc
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
- `-p` 指向当前编译器的构建目录,读取 `compile_commands.json`。
|
|
@@ -30,7 +30,7 @@ clang-tidy -p build/clang \
|
|
|
30
30
|
- 系统头默认被排除,不要加 `--system-headers`。
|
|
31
31
|
- 只扫描项目源文件;不要把 `build/` 或第三方源码传给 clang-tidy。
|
|
32
32
|
- `--warnings-as-errors='*'` 把警告当失败;修代码,不要新增 `NOLINT`。确需抑制时写 `// NOLINT(检查名): 理由`,理由必须具体。
|
|
33
|
-
- 配置在工程根 `.clang-tidy
|
|
33
|
+
- 配置在工程根 `.clang-tidy`;默认集合是 `google-*` 加 `readability-identifier-naming`。关闭某条时逐条写理由,不整族关闭;命名规则只配项目确实统一的项,不设 `FunctionCase`。
|
|
34
34
|
- 没有编译数据库时先回到 `building-cpp` 的定构建生成;不要用 `--` 手工拼编译参数绕过。
|
|
35
35
|
- `clang-tidy` 的诊断可能指向头文件;修头文件里的声明或实现,不要只改调用点。
|
|
36
36
|
|
|
@@ -41,17 +41,13 @@ clang-tidy -p build/clang \
|
|
|
41
41
|
```yaml
|
|
42
42
|
CompileFlags:
|
|
43
43
|
CompilationDatabase: build/clang
|
|
44
|
-
Add:
|
|
45
|
-
- -std=c++20
|
|
46
|
-
- -Wall
|
|
47
|
-
- -Wextra
|
|
48
44
|
Diagnostics:
|
|
49
45
|
Suppress:
|
|
50
46
|
- pp_file_not_found
|
|
51
47
|
```
|
|
52
48
|
|
|
53
49
|
- `CompilationDatabase` 指向当前编译器目录;GCC 模式改 `build/gcc`。
|
|
54
|
-
- `
|
|
50
|
+
- 不要在 `.clangd` 写 `-std`:它会覆盖 `compile_commands.json`,把 C++23 工程降成旧标准。标准只由构建声明和编译数据库决定。
|
|
55
51
|
- 静态检查规则只在 `.clang-tidy` 里维护;clangd 读取同一份配置,不要在这里复制检查列表。
|
|
56
52
|
- `Suppress` 只放确实无法避免的解析噪声(如 `pp_file_not_found`),并写理由。
|
|
57
53
|
|
|
@@ -82,7 +78,11 @@ Diagnostics:
|
|
|
82
78
|
|
|
83
79
|
```json
|
|
84
80
|
{
|
|
85
|
-
"recommendations": [
|
|
81
|
+
"recommendations": [
|
|
82
|
+
"llvm-vs-code-extensions.vscode-clangd",
|
|
83
|
+
"ms-vscode.cmake-tools",
|
|
84
|
+
"ms-vscode.cpptools"
|
|
85
|
+
]
|
|
86
86
|
}
|
|
87
87
|
```
|
|
88
88
|
|
|
@@ -96,13 +96,25 @@ Diagnostics:
|
|
|
96
96
|
"--background-index"
|
|
97
97
|
],
|
|
98
98
|
"cmake.buildDirectory": "${workspaceFolder}/build/clang",
|
|
99
|
-
"cmake.generator": "Ninja"
|
|
99
|
+
"cmake.generator": "Ninja",
|
|
100
|
+
"cmake.buildBeforeRun": true,
|
|
101
|
+
"cmake.exportCompileCommandsFile": true,
|
|
102
|
+
"cmake.debugConfig": {
|
|
103
|
+
"type": "cppdbg",
|
|
104
|
+
"request": "launch",
|
|
105
|
+
"MIMode": "lldb",
|
|
106
|
+
"miDebuggerPath": "/usr/bin/lldb",
|
|
107
|
+
"stopAtEntry": false
|
|
108
|
+
},
|
|
109
|
+
"C_Cpp.intelliSenseEngine": "disabled"
|
|
100
110
|
}
|
|
101
111
|
```
|
|
102
112
|
|
|
113
|
+
- 配置、构建、运行和调试只从 CMake Tools 进入;不要同时维护 `tasks.json` 或 `launch.json`,否则会出现第二套构建或启动路径。
|
|
114
|
+
- C/C++ 扩展只作为 CMake 快速调试的 `cppdbg` 后端;IntelliSense 关闭,补全、跳转、诊断和格式化只由 clangd 提供。
|
|
103
115
|
- 不推荐与 clangd 冲突的 C++ 智能提示插件;只保留一个语言服务。
|
|
104
116
|
- 不把 `compile_commands.json` 提交进仓库;它是构建产物。
|
|
105
|
-
- 切换编译器时同时改 `.clangd`、`settings.json` 和构建目录;三处不一致会让诊断指向旧代码。
|
|
117
|
+
- 切换编译器时同时改 `.clangd`、`settings.json` 和构建目录;三处不一致会让诊断指向旧代码。GCC 模式把 `MIMode` 改为 `gdb`、`miDebuggerPath` 改为 `/usr/bin/gdb`。
|
|
106
118
|
|
|
107
119
|
## 门禁分级
|
|
108
120
|
|
|
@@ -110,9 +122,9 @@ Diagnostics:
|
|
|
110
122
|
|
|
111
123
|
| 级别 | 典型检查 | 处理 |
|
|
112
124
|
| --- | --- | --- |
|
|
113
|
-
| 失败 | `
|
|
125
|
+
| 失败 | `google-*`、`readability-identifier-naming`、项目声明的致命集 | 修代码;不允许用 NOLINT 掩盖 |
|
|
114
126
|
| 失败 | 编译诊断、链接诊断、格式检查 | 直接失败 |
|
|
115
|
-
| 观察 |
|
|
127
|
+
| 观察 | 迁移期暂时无法修完的个别 `google-*` 子项 | 在项目技能里列清单、写负责人和退出条件;不默认全仓关闭 |
|
|
116
128
|
|
|
117
129
|
- 致命集宁可小、可解释,也不要把所有检查一刀切。
|
|
118
130
|
- 观察项不是免死金牌:新代码不应新增观察项,已有项只能减少。
|
|
@@ -15,6 +15,8 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
|
15
15
|
export const HOSTS = ['windows', 'linux', 'macos']
|
|
16
16
|
export const BUILD_HOSTS = ['native', 'wsl']
|
|
17
17
|
export const COMPILERS = ['clang', 'gcc']
|
|
18
|
+
export const STANDARDS = ['c++11', 'c++14', 'c++17', 'c++20', 'c++23']
|
|
19
|
+
export const DEFAULT_STANDARD = 'c++23'
|
|
18
20
|
const PROJECT_SKILL_DIR = path.join('.agents', 'skills', 'cpp-workflow-project')
|
|
19
21
|
const MAX_ASCENT = 64
|
|
20
22
|
|
|
@@ -34,8 +36,9 @@ function normalize(value) {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
|
-
* Validate the three axes and their combination.
|
|
38
|
-
*
|
|
39
|
+
* Validate the three environment axes, the language standard, and their combination.
|
|
40
|
+
* Pure: no filesystem or PATH.
|
|
41
|
+
* @returns {{ok: boolean, issues: string[], notes: string[], axes: {host?: string, build_host?: string, compiler?: string, standard?: string}}}
|
|
39
42
|
*/
|
|
40
43
|
export function validateEnvironment(value) {
|
|
41
44
|
const issues = []
|
|
@@ -53,6 +56,18 @@ export function validateEnvironment(value) {
|
|
|
53
56
|
host: normalize(value.host),
|
|
54
57
|
build_host: normalize(value.build_host),
|
|
55
58
|
compiler: normalize(value.compiler),
|
|
59
|
+
standard: normalize(value.standard),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (axes.standard === undefined || axes.standard === null || axes.standard === '') {
|
|
63
|
+
axes.standard = DEFAULT_STANDARD
|
|
64
|
+
notes.push(`standard 未声明或为空;按默认 ${DEFAULT_STANDARD} 处理,请在 environment.json 中显式写出`)
|
|
65
|
+
} else if (Array.isArray(axes.standard)) {
|
|
66
|
+
issues.push(`standard 只能声明一个值,当前是数组 [${axes.standard.join(', ')}]`)
|
|
67
|
+
} else if (typeof axes.standard !== 'string') {
|
|
68
|
+
issues.push(`standard 必须是字符串,当前是 ${typeof axes.standard}`)
|
|
69
|
+
} else if (!STANDARDS.includes(axes.standard)) {
|
|
70
|
+
issues.push(`standard 的值 "${axes.standard}" 非法;允许:${STANDARDS.join(' / ')}`)
|
|
56
71
|
}
|
|
57
72
|
|
|
58
73
|
for (const [key, allowed] of [
|
|
@@ -85,12 +100,12 @@ export function validateEnvironment(value) {
|
|
|
85
100
|
}
|
|
86
101
|
|
|
87
102
|
if (axes.host === 'windows' && axes.build_host === 'native') {
|
|
88
|
-
|
|
103
|
+
issues.push('本版本不能在 Windows 原生壳里构建')
|
|
89
104
|
}
|
|
90
105
|
|
|
91
106
|
for (const key of Object.keys(value)) {
|
|
92
|
-
if (!['host', 'build_host', 'compiler'].includes(key)) {
|
|
93
|
-
notes.push(`忽略未知键 "${key}";环境只认 host / build_host / compiler`)
|
|
107
|
+
if (!['host', 'build_host', 'compiler', 'standard'].includes(key)) {
|
|
108
|
+
notes.push(`忽略未知键 "${key}";环境只认 host / build_host / compiler / standard`)
|
|
94
109
|
}
|
|
95
110
|
}
|
|
96
111
|
|
|
@@ -245,25 +260,10 @@ export function checkLive(declaration, options = {}) {
|
|
|
245
260
|
}
|
|
246
261
|
const { host, build_host: buildHost, compiler } = validation.axes
|
|
247
262
|
|
|
248
|
-
if (host === 'windows' && buildHost === 'native') {
|
|
249
|
-
return {
|
|
250
|
-
ok: false,
|
|
251
|
-
issues: [
|
|
252
|
-
'Windows 原生构建缺少 POSIX 壳;本版本没有 PowerShell 构建脚本。缺的是壳,不是编译器。请改用 WSL 并把 build_host 改成 wsl,或补一个 PowerShell 版本。',
|
|
253
|
-
],
|
|
254
|
-
notes: validation.notes,
|
|
255
|
-
checks,
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
263
|
if (buildHost === 'wsl') {
|
|
260
264
|
if (platform !== 'linux' || !isWsl(env)) {
|
|
261
265
|
issues.push('声明 build_host=wsl,但当前进程不在 WSL 内;请在 WSL 里运行 --live')
|
|
262
266
|
}
|
|
263
|
-
} else if (host === 'windows') {
|
|
264
|
-
if (platform !== 'win32') {
|
|
265
|
-
issues.push(`声明 host=windows + build_host=native,但当前平台是 ${platform};请在 Windows 上运行 --live`)
|
|
266
|
-
}
|
|
267
267
|
} else if (host === 'linux' && platform !== 'linux') {
|
|
268
268
|
issues.push(`声明 host=linux,但当前平台是 ${platform};请在 Linux 上运行 --live`)
|
|
269
269
|
} else if (host === 'macos' && platform !== 'darwin') {
|
|
@@ -358,7 +358,7 @@ export function checkLive(declaration, options = {}) {
|
|
|
358
358
|
['clang-tidy', tidyVersion],
|
|
359
359
|
]) {
|
|
360
360
|
if (version && version.major < 16) {
|
|
361
|
-
issues.push(`${role} ${versionText(version)}
|
|
361
|
+
issues.push(`${role} ${versionText(version)} 过旧;本工作流要求 >= 16`)
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
364
|
|
|
@@ -16,28 +16,29 @@ build/
|
|
|
16
16
|
|
|
17
17
|
## 通用情况
|
|
18
18
|
|
|
19
|
-
先读 `environment.json
|
|
19
|
+
先读 `environment.json`。源文件清单来自 `developing-cpp` 的定边界和定实现;定构建只把它们写进 target,不重新决定库与程序的边界。三个环境轴加语言标准分别表示:
|
|
20
20
|
|
|
21
21
|
| 轴 | 取值 | 含义 |
|
|
22
22
|
| --- | --- | --- |
|
|
23
23
|
| `host` | `windows` / `linux` / `macos` | 编辑器与代理壳所在系统 |
|
|
24
|
-
| `build_host` | `native` / `wsl` | 执行编译和运行产物的环境;`native` 表示 `host` 本机,`wsl` 表示 Windows 的 WSL,且只允许 `host=windows` |
|
|
24
|
+
| `build_host` | `native` / `wsl` | 执行编译和运行产物的环境;`native` 表示 `host` 本机,`wsl` 表示 Windows 的 WSL,且只允许 `host=windows`;`windows` + `native` 本版本不支持 |
|
|
25
25
|
| `compiler` | `clang` / `gcc` | 用哪个编译器;一次只配一个 |
|
|
26
|
+
| `standard` | `c++11`–`c++23` | 一个工程一个语言标准;新工程默认 `c++23`,已有工程按原声明 |
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
环境轴为空、`standard` 非法或组合非法(如 `host=linux` + `build_host=wsl`、`host=windows` + `build_host=native`)时,停在定构建,先让用户把 `environment.json` 填对。不要替用户选 WSL、Clang 或构建目录。`windows` + `native` 只给一句「本版本不能在 Windows 原生壳里构建」,不要建议改 WSL、不要提出补 PowerShell 脚本。
|
|
28
29
|
|
|
29
30
|
构建规则:
|
|
30
31
|
|
|
31
|
-
- CMake 最低 3.20;`project(... LANGUAGES CXX)`;只有确有 C 源时才写 `LANGUAGES C CXX
|
|
32
|
+
- CMake 最低 3.20;`project(... LANGUAGES CXX)`;只有确有 C 源时才写 `LANGUAGES C CXX`。新工程默认标准 C++23;已有工程按 `environment.json` 的 `standard` 或现有 target 声明,不擅自升降。
|
|
32
33
|
- 生成器固定单配置 Ninja,不用 Ninja Multi-Config:同一源文件会写多条命令,clangd 默认只取第一条,容易读错数据库(clangd#2026);构建目录固定 `build/clang` 或 `build/gcc`。
|
|
33
34
|
- 一个 configure 只用一个编译器:`-DCMAKE_CXX_COMPILER=clang++` 或 `g++`。
|
|
34
35
|
- 程序 target 固定叫 `app`;库 target 用组件名,程序链接库。库与程序分开,不把 `main()` 编进库。
|
|
35
36
|
- 用 `target_include_directories`、`target_compile_features`、`target_link_libraries` 表达依赖;禁止全局 `include_directories()`、`add_definitions()`、`CMAKE_CXX_FLAGS` 污染所有 target。
|
|
36
|
-
- 库的公共 include 根用 `PUBLIC`;实现私有头用 `PRIVATE`;测试 target
|
|
37
|
+
- 库的公共 include 根用 `PUBLIC`;实现私有头用 `PRIVATE`;测试 target 只链接被测库的公共接口。头文件按 Google 规则从仓库根写完整路径,构建时把仓库根加入搜索路径,不要改成组件的 `include/` 根后再写裸文件名。
|
|
37
38
|
- `set(CMAKE_EXPORT_COMPILE_COMMANDS ON)`;Ninja 生成器下会产出 `compile_commands.json`。
|
|
38
39
|
- 输出目录统一:可执行文件到 `build/<compiler>/bin`,库到 `build/<compiler>/lib`;不要用源码目录当输出目录。
|
|
39
40
|
- 开启 `CMAKE_EXPORT_COMPILE_COMMANDS` 后,把 `build/<compiler>/compile_commands.json` 符号链接到 `build/compile_commands.json`,让 clangd 默认能找到当前数据库。本插件固定产出 `build/<compiler>/` 并在 `build/` 下维护链接,不生成 `out/build/`,也不把两套构建根混用。
|
|
40
|
-
- 目标标准写在 target 上:`target_compile_features(app PRIVATE
|
|
41
|
+
- 目标标准写在 target 上:`target_compile_features(app PRIVATE cxx_std_23)`;库用 `PUBLIC` 传播标准。再加 `set_target_properties(... CXX_STANDARD 23 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)`:meta-feature 只保证“至少”,`CXX_STANDARD_REQUIRED` 防 decay,`CXX_EXTENSIONS OFF` 把 `-std=gnu++23` 收成 `-std=c++23`。configure 后读 `compile_commands.json` 的最后一个 `-std=`,必须与 `environment.json` 的 `standard` 一致。
|
|
41
42
|
- 工程确有 C 源时才写 `LANGUAGES C CXX`,并且 `CMAKE_C_COMPILER` 与 `CMAKE_CXX_COMPILER` 必须来自同一套编译器(`clang`/`clang++` 或 `gcc`/`g++`);没有 C 源时只启用 CXX。
|
|
42
43
|
- 不写 `file(GLOB ...)`;源文件显式列出,新增文件时改 CMake。
|
|
43
44
|
- 不加 `-march=native`、`-ffast-math` 等改变语义或可移植性的选项;警告选项按团队约定统一加。
|
|
@@ -59,11 +60,11 @@ GCC 模式把目录和编译器换成 `build/gcc`、`g++`。工程确有 C 源
|
|
|
59
60
|
|
|
60
61
|
## 情况
|
|
61
62
|
|
|
62
|
-
**新工程**:从 `assets/templates/projects/` 选形态:`single` 单文件、`multi` 多文件单程序、`static-library` 静态库 + 程序、`shared-library` 动态库 + 程序。只复制目标工程缺的文件;已有的 `CMakeLists.txt`、`.clang-*`、源码不覆盖。模板里的 `{{PROJECT_NAME}}`
|
|
63
|
+
**新工程**:从 `assets/templates/projects/` 选形态:`single` 单文件、`multi` 多文件单程序、`static-library` 静态库 + 程序、`shared-library` 动态库 + 程序。只复制目标工程缺的文件;已有的 `CMakeLists.txt`、`.clang-*`、源码不覆盖。模板里的 `{{PROJECT_NAME}}` 换成真实工程名,默认标准 C++23;四套模板已按 `__cplusplus` 条件使用 `std::format`,把 `standard`、`cxx_std_*`、`CXX_STANDARD` 和 `.clang-format` 的解析档位一起降到 C++11–C++20 后仍应能构建,降完跑 `references/standards/README.md` 的五档矩阵。模板源码是 `.cc`,头文件用路径式 `#define` 守卫;新增文件沿用 `.cc` / `.h`,已有工程保持原有扩展名。
|
|
63
64
|
|
|
64
65
|
**已有工程**:先读现有 `CMakeLists.txt`,找出全局设置、GLOB、输出目录和 target 命名;只改与当前任务相关的一处。大改前先让通过检查的项目入口跑通,保留可回退点。
|
|
65
66
|
|
|
66
|
-
**库 + 程序**:库 target 的 `PUBLIC` include
|
|
67
|
+
**库 + 程序**:库 target 的 `PUBLIC` include 根按项目约定设置;本工作流把仓库根作为源码根,程序按 `#include "component/include/name.h"` 包含公共头,不直接包含库的 `src/`。静态库与动态库的接口相同;动态库额外设置运行时库路径。
|
|
67
68
|
|
|
68
69
|
**测试**:`enable_testing()` 放在顶层,`add_subdirectory(tests)` 在 `add_subdirectory(<component>)` 之后;测试 target 用 `add_test(NAME ... COMMAND ...)` 注册。没有测试时不要为了「看起来完整」加空测试。
|
|
69
70
|
|
|
@@ -74,3 +75,4 @@ GCC 模式把目录和编译器换成 `build/gcc`、`g++`。工程确有 C 源
|
|
|
74
75
|
- 全局 include、GLOB、把 `main()` 编进库:`examples/cmake.md`。
|
|
75
76
|
- 正确的最小库 + 程序:`examples/cmake.md`「最小可用形状」。
|
|
76
77
|
- target 与目录的完整说明:`references/cmake.md`。
|
|
78
|
+
- 五档标准的下限、降档替换和工具映射:`references/standards/README.md`。
|
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
- 只复制缺失的文件。已有配置先读再改,不覆盖;需要合并时逐项说明理由。
|
|
11
11
|
- 工具角色固定:`clang-format` 管排版,`clang-tidy` 管静态诊断,`clangd` 管补全/跳转/诊断;无论 `compiler` 是 clang 还是 gcc,这三件都用 Clang 工具链。
|
|
12
12
|
- 版本要求见 `references/environment.md`:Clang 模式要求 `clang++`、`clang-format`、`clang-tidy`、`clangd` 同一 LLVM 主版本;GCC 模式要求 `clang-format` / `clang-tidy` ≥ 16,`clangd` 只要求可用。
|
|
13
|
-
- `.clangd` 的 `CompilationDatabase` 指向 `build/<compiler
|
|
13
|
+
- `.clangd` 的 `CompilationDatabase` 指向 `build/<compiler>`;切换编译器时同步改,不能留旧目录。**不要写 `-std`**:它会覆盖 `compile_commands.json`,把 C++23 工程降成旧标准;标准只由构建声明和编译数据库决定。
|
|
14
14
|
- `--query-driver` 是 clangd 的命令行参数,写在 `.vscode/settings.json` 的 `clangd.arguments` 里,不是 `.clangd` 的 YAML 键。它告诉 clangd 允许执行哪个编译器驱动来取系统头路径;路径写绝对路径,并与 `environment.json` 的编译器一致。
|
|
15
|
-
-
|
|
16
|
-
- `.clang-format`
|
|
17
|
-
- `.clang-tidy`
|
|
18
|
-
- `.
|
|
15
|
+
- 首次复制时按轴替换三处:`compiler=clang` 用 `CompilationDatabase: build/clang`、`--query-driver=/usr/bin/clang++` 和 `cmake.debugConfig` 的 `MIMode: lldb` / `miDebuggerPath: /usr/bin/lldb`;`compiler=gcc` 用 `build/gcc`、`/usr/bin/g++` 和 `MIMode: gdb` / `/usr/bin/gdb`。只复制模板而不改这三处,GCC 模式会指向错误的数据库、驱动或调试器。原工程已有的 `.clangd` 不覆盖;要改先征得同意。
|
|
16
|
+
- `.clang-format` 只保留 `Language: Cpp`、`BasedOnStyle: Google`、`Standard: Latest`(clang-format 21/22 不接受 `c++23`)。列宽 80、每级 2 空格、不用 tab、包含顺序都取 Google 预设;不要覆盖 `ColumnLimit`、`TabWidth`、`IncludeCategories` 或指针对齐。需要新规则时先改 `references/google-cpp-style.md` 并说明理由。
|
|
17
|
+
- `.clang-tidy` 用 `Checks: '-*, google-*, readability-identifier-naming'`,保留 `WarningsAsErrors: '*'` 和 `FormatStyle: file`。`google-*` 覆盖 Google 风格能静态检查的部分;`readability-identifier-naming` 只配项目确实统一的命名项:类型/枚举/别名/模板参数 PascalCase,变量/参数/成员 snake_case,类成员尾随 `_`,结构体成员不尾随,常量 `k` + PascalCase。不要设 `FunctionCase`,Google 允许 `count()` / `set_count()` 这类访问器;不要重新打开 `cppcoreguidelines-*` 整包或 `portability-avoid-pragma-once`。
|
|
18
|
+
- `.clang-tidy` 关闭项和例外必须写理由;不要借 `-*` 之后再打开 `cppcoreguidelines-*` 这类非 Google 整族检查。
|
|
19
|
+
- `.vscode/extensions.json` 推荐 clangd、CMake Tools 与 C/C++ 扩展;`.vscode/settings.json` 固定 clangd 参数、CMake 构建目录和 `cmake.debugConfig`。配置、构建、运行和调试只从 CMake Tools 进入,不写 `tasks.json` 或 `launch.json`;C/C++ 扩展只作为 CMake 快速调试的后端,IntelliSense 关闭,避免和 clangd 并行。配置注释用 JSONC 的 `//`,贴在被解释的键上方。
|
|
19
20
|
- 配置注释写「为什么这样设」,不写「这一项是什么」;不写策略头、不指向文档路径。
|
|
20
21
|
- 格式化与静态检查只扫描项目源码:`src/`、`include/`、`apps/`、`tests/` 等实际存在的源码根;不扫描 `build/`、第三方目录、生成文件。
|
|
21
22
|
- `clang-tidy` 用 `-p build/<compiler>` 读取编译数据库,`--header-filter` 只列项目根下的顶层目录,避免把第三方头纳入;不要加 `--system-headers`,也不要用 clang-tidy 19 才加入、16 没有的 `--exclude-header-filter`。
|
|
@@ -24,9 +25,9 @@
|
|
|
24
25
|
|
|
25
26
|
## 情况
|
|
26
27
|
|
|
27
|
-
**新工程**:从 `assets/config/` 复制 `.clang-format`、`.clang-tidy`、`.clangd` 和 `.vscode/`
|
|
28
|
+
**新工程**:从 `assets/config/` 复制 `.clang-format`、`.clang-tidy`、`.clangd` 和 `.vscode/` 下的 `extensions.json`、`settings.json`,按编译器的映射改 `.clangd` 的编译数据库目录、`.vscode/settings.json` 的 `--query-driver` 路径和 `cmake.debugConfig` 的调试后端。不要复制 `build/`。
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
**已有工程**:新工程默认 C++23;已有工程按 `environment.json` 的 `standard` 对齐 CMake target 与 `.clang-format`,不要只改一处。先跑一次 `clang-format --dry-run --Werror` 和 `clang-tidy`,看现有配置实际报什么;只改导致当前失败或与声明环境冲突的项。不要借机重排全工程格式。
|
|
30
31
|
|
|
31
32
|
**换编译器**:改 `.clangd` 的 `CompilationDatabase`,改 `--query-driver` 指向的编译器,清理旧构建目录;不要同时保留两套 clangd 配置。
|
|
32
33
|
|
|
@@ -39,3 +40,4 @@
|
|
|
39
40
|
- `.clangd` 指错编译数据库、`query-driver` 写进 YAML:`examples/tooling.md`。
|
|
40
41
|
- 配置文件堆策略头、关闭检查不写理由:`examples/tooling.md`「配置注释」。
|
|
41
42
|
- 工具调用与头文件过滤:`references/static-analysis.md`。
|
|
43
|
+
- 每个标准对应的 clang-format 档位与 `.clangd` 读库规则:`references/standards/README.md`。
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
按固定顺序跑,前一步失败就停,不要带着失败继续:
|
|
10
10
|
|
|
11
|
-
1. **校验环境声明(不带 `--live`)**:读 `environment.json
|
|
12
|
-
2. **活动校验**:在声明的 build host 上跑 `--live`,核对 PATH
|
|
13
|
-
3. **跑项目入口命令**:项目技能里写的入口命令(如 `./running-build.sh clang
|
|
11
|
+
1. **校验环境声明(不带 `--live`)**:读 `environment.json`,检查三个环境轴、`standard` 和组合是否合法。环境轴为空、`standard` 非法或组合非法时,停在这里,不找可执行文件。
|
|
12
|
+
2. **活动校验**:在声明的 build host 上跑 `--live`,核对 PATH 中的角色和版本。`windows` + `native` 在声明校验阶段就返回失败,不会进入这一步。
|
|
13
|
+
3. **跑项目入口命令**:项目技能里写的入口命令(如 `./running-build.sh clang`)。入口命令仍是 `<entry-command>` 或空白时,停在这里,不要猜命令;是真实命令才跑。入口内部依次 configure → build → format → tidy → ctest → run;每一步失败都保留原始输出。
|
|
14
14
|
4. **核对产物**:`build/<compiler>/compile_commands.json` 存在;`build/compile_commands.json` 指向当前编译器;`app` 可执行文件在 `build/<compiler>/bin`。
|
|
15
15
|
5. **记录结果**:通过时记录编译器版本、构建目录、测试数量;失败时记录命令、退出码、第一条根因诊断。
|
|
16
16
|
|
|
@@ -49,7 +49,7 @@ node "<skill-dir>/scripts/checking-environment.mjs" --project . --live # 活
|
|
|
49
49
|
|
|
50
50
|
**没有测试**:CTest 显示没有测试是合法结果;不要为了「全绿」造空测试。行为验收由定行为的验收句负责。
|
|
51
51
|
|
|
52
|
-
**Windows
|
|
52
|
+
**Windows 原生**:本版本不支持 `host=windows` + `build_host=native`。声明校验会返回 `ok: false`,`issues` 只有一句「本版本不能在 Windows 原生壳里构建」。停在这里,不要伪造 PowerShell 脚本,不要建议改 WSL 来绕过,也不要让代理改 `environment.json` 过关。
|
|
53
53
|
|
|
54
54
|
**macOS**:`/usr/bin/clang++` 通常是 Apple Clang;`.clangd` 不写死 Homebrew 路径,clangd 要能经 xcrun 找到 SDK。`compiler=clang` 时 `--live` 按同一 LLVM 主版本核对 clang++、clang-format、clang-tidy、clangd;不要混用 Homebrew LLVM 与 Xcode 资源目录来凑版本,也不要为了通过而改 `environment.json` 的编译器。
|
|
55
55
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: developing-cpp
|
|
3
|
-
description: 编写或修改 C++
|
|
3
|
+
description: 编写或修改 C++11–C++23 源码、头文件、命名、异常与源码注释时使用;新工程默认 C++23;按定行为、定边界、定声明、定实现四步推进。一次改动同时碰到源码和构建文件时,先打开本技能。不用于只改文档、Python、CMake、构建脚本或与 C++ 无关的文件。
|
|
4
4
|
metadata:
|
|
5
|
-
short-description: C++
|
|
5
|
+
short-description: C++11–C++23 源码四步
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# developing-cpp
|
|
9
9
|
|
|
10
10
|
## 适用场景
|
|
11
11
|
|
|
12
|
-
管 `.h` / `.cpp`
|
|
12
|
+
管 `.h` / `.cc` 里换一台机器也不会改的内容:可观察行为、库与程序的边界、声明、命名、异常和源码注释。排版、命名、头文件与包含顺序以 `references/google-cpp-style.md` 的 Google 基线为准。异常条文采用 C++ Core Guidelines 的 Error handling,作为开发阶段的源码指导;它不进入插件运行时。`CMakeLists.txt`、`cmake/`、`running-build.sh`、`.clang-*`、`.vscode/` 和 `environment.json` 只开 `building-cpp`。只改函数体、命名、异常或注释且入口命令可运行时,定实现后只开 `building-cpp` 的「通过检查」;新增或移动源文件、改库与程序的边界、补测试 target 时,声明和实现可以先写,但完成前必须开 `building-cpp` 的定构建,把文件写进 CMake,再决定是否定工具配置,最后通过检查。`.qmd`、仓库 README、Python 和非 C++ 文档不打开本技能。
|
|
13
13
|
|
|
14
14
|
存在项目技能 `cpp-workflow-project` 时先读它的 `SKILL.md`:工程名、库与程序的边界、阶段表、术语增补、入口命令。没有项目技能时门禁不会放出本技能,不要猜项目约定。
|
|
15
15
|
|
|
@@ -29,7 +29,7 @@ metadata:
|
|
|
29
29
|
| 1 | 定行为 | `steps/01-behavior.md` | 把这一处要能观察到的结果写成验收句 |
|
|
30
30
|
| 2 | 定边界 | `steps/02-boundary.md` | 决定哪些放库、哪些放程序,依赖朝哪个方向 |
|
|
31
31
|
| 3 | 定声明 | `steps/03-declaration.md` | 写命名、声明顺序、头文件契约和声明注释 |
|
|
32
|
-
| 4 | 定实现 | `steps/04-implementation.md` | 写 `.
|
|
32
|
+
| 4 | 定实现 | `steps/04-implementation.md` | 写 `.cc` 的操作顺序、分组和实现注释 |
|
|
33
33
|
|
|
34
34
|
每步只打开同号的一个步骤文件。判不准才打开 `examples/` 里与该步对应的案例;`references/` 保存完整条文,只在排错、整目录重构,或步骤里明确指向某一条文时按需打开。
|
|
35
35
|
|
|
@@ -37,6 +37,6 @@ metadata:
|
|
|
37
37
|
|
|
38
38
|
- 第 1 步的行为句能被用户或测试验收;有阶段表时只覆盖对应行,没有时只覆盖用户点名的行为。
|
|
39
39
|
- 第 2 步的库 target 不依赖终端、GUI 或脚本;程序 target 只做装配和输入输出;共享抽象有第二个真实使用方。
|
|
40
|
-
- 第 3
|
|
41
|
-
- 第 4 步的实现只兑现声明;资源由 RAII
|
|
40
|
+
- 第 3 步的公共头文件自包含,命名、声明顺序和异常声明符合 `references/` 的条文。
|
|
41
|
+
- 第 4 步的实现只兑现声明;资源由 RAII 管理;失败是正常结果时用返回值,函数无法完成任务时抛异常,不拿异常做正常分支。
|
|
42
42
|
- 没有改 `CMakeLists.txt`、`.clang-*`、构建脚本,也没有把外部资料路径或项目专属类名写进通用条文。
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
**差**:
|
|
6
6
|
|
|
7
7
|
```cpp
|
|
8
|
-
// task_list.
|
|
8
|
+
// task_list.cc
|
|
9
9
|
// 作者:张三
|
|
10
10
|
// 创建:2024-01-01
|
|
11
11
|
// 修改:2024-02-01 修复 bug
|
|
@@ -43,11 +43,11 @@ Task GetTask(int index) const;
|
|
|
43
43
|
///
|
|
44
44
|
/// @param index 任务下标,必须在 [0, tasks_.size()) 内
|
|
45
45
|
/// @return 该位置的任务
|
|
46
|
-
/// @throws
|
|
46
|
+
/// @throws TaskIndexError index 越界
|
|
47
47
|
[[nodiscard]] Task GetTask(int index) const;
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
契约完整:范围、返回、异常声明。
|
|
51
51
|
|
|
52
52
|
## 实现注释
|
|
53
53
|
|
|
@@ -121,11 +121,13 @@ ColumnLimit: 100
|
|
|
121
121
|
**好**:
|
|
122
122
|
|
|
123
123
|
```yaml
|
|
124
|
-
#
|
|
125
|
-
|
|
124
|
+
# 列宽、缩进、包含顺序都取 Google 预设,不逐项覆盖。
|
|
125
|
+
Language: Cpp
|
|
126
|
+
BasedOnStyle: Google
|
|
127
|
+
Standard: Latest
|
|
126
128
|
```
|
|
127
129
|
|
|
128
|
-
|
|
130
|
+
解释为什么选预设,不写策略头,不指文档路径。
|
|
129
131
|
|
|
130
132
|
## 横幅
|
|
131
133
|
|
|
@@ -197,8 +199,8 @@ void SetPriority(TaskId id, int priority);
|
|
|
197
199
|
///
|
|
198
200
|
/// @param id 任务编号,必须已存在
|
|
199
201
|
/// @param priority 新优先级,必须在 [0, 9] 内
|
|
200
|
-
/// @throws
|
|
201
|
-
/// @throws
|
|
202
|
+
/// @throws TaskIndexError 编号不在当前任务表内
|
|
203
|
+
/// @throws InvalidPriorityError priority 越界
|
|
202
204
|
void SetPriority(TaskId id, int priority);
|
|
203
205
|
```
|
|
204
206
|
|
|
@@ -1,53 +1,95 @@
|
|
|
1
1
|
# 正反例:异常
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
这些例子只用于开发阶段讨论源码写法。插件运行时不编译它们,也不执行它们。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 什么时候抛
|
|
6
|
+
|
|
7
|
+
**差**:用户输入格式错误是调用方每天都要处理的正常结果,却用异常表达,调用方被迫用 `try/catch` 做分支。
|
|
6
8
|
|
|
7
9
|
```cpp
|
|
8
|
-
Task ParseTask(std::string_view line); //
|
|
10
|
+
Task ParseTask(std::string_view line); // 任何格式错误都抛
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
**好**:结果类型表达可预期失败,合同违反才抛。
|
|
13
|
+
**好**:接口把「解析可能失败」写进返回类型;只有函数无法完成自己的任务、继续执行没有意义时才抛。
|
|
14
14
|
|
|
15
15
|
```cpp
|
|
16
|
-
enum class
|
|
16
|
+
enum class ParseStatus { kOk, kEmptyLine, kMissingPriority, kBadPriority };
|
|
17
17
|
|
|
18
18
|
struct ParseResult {
|
|
19
19
|
std::optional<Task> task;
|
|
20
|
-
|
|
20
|
+
ParseStatus status = ParseStatus::kEmptyLine;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
[[nodiscard]] ParseResult TryParseTask(std::string_view line);
|
|
24
24
|
|
|
25
|
-
/// @throws
|
|
25
|
+
/// @throws TaskIndexError index 不在 [0, size()) 内。
|
|
26
26
|
[[nodiscard]] Task GetTask(int index) const;
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
`TryParseTask`
|
|
29
|
+
`TryParseTask` 把失败作为正常结果返回。`GetTask` 的前提是调用方给出有效下标;下标无效时函数无法返回一个任务,于是抛出带领域语义的 `TaskIndexError`。如果越界其实是正常输入,应该改成返回 `std::optional<Task>`,而不是让调用方捕获异常。
|
|
30
30
|
|
|
31
|
-
##
|
|
31
|
+
## 异常类型与消息
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
**差**:抛内建类型或裸字符串,调用方只能捕获一切。
|
|
34
34
|
|
|
35
35
|
```cpp
|
|
36
|
-
throw
|
|
36
|
+
throw "bad index";
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
**好**:定义有语义的类型;确实没有更多信息时,才直接用标准库通用类型。
|
|
40
40
|
|
|
41
41
|
```cpp
|
|
42
|
-
|
|
42
|
+
class TaskIndexError : public std::out_of_range {
|
|
43
|
+
public:
|
|
44
|
+
explicit TaskIndexError(const std::string& message)
|
|
45
|
+
: std::out_of_range(message) {}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
throw TaskIndexError{
|
|
43
49
|
std::format("GetTask: index={} 超出 [0, {})", index, tasks_.size())};
|
|
44
50
|
```
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
消息写操作和关键值,不写「出错了」;`std::format` 只是构造消息的方式,不强制固定模板。
|
|
53
|
+
|
|
54
|
+
## 多语义类型:Boost.Exception 风格
|
|
55
|
+
|
|
56
|
+
一个错误可以同时属于多个类别。类型只做 `catch` 的标签,数据由基类或错误信息容器携带;多继承时用 virtual 基类,避免 `catch (const std::exception&)` 二义性。
|
|
57
|
+
|
|
58
|
+
```cpp
|
|
59
|
+
struct exception_base : virtual std::exception, virtual boost::exception {};
|
|
60
|
+
struct io_error : virtual exception_base {};
|
|
61
|
+
struct file_error : virtual io_error {};
|
|
62
|
+
struct read_error : virtual io_error {};
|
|
63
|
+
struct file_read_error : virtual file_error, virtual read_error {};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
这只是设计典范,不要求引入 Boost。项目自己的异常层级也可以照这个形状写。
|
|
67
|
+
|
|
68
|
+
## 捕获
|
|
69
|
+
|
|
70
|
+
**差**:捕获一切后静默返回,状态可能已经半坏。
|
|
71
|
+
|
|
72
|
+
```cpp
|
|
73
|
+
try {
|
|
74
|
+
Load(path);
|
|
75
|
+
} catch (...) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**好**:只捕获当前层能处理的类型;不能处理的继续传播。`catch` 从派生到基类排序,`catch (...)` 只能最后兜底。
|
|
81
|
+
|
|
82
|
+
```cpp
|
|
83
|
+
try {
|
|
84
|
+
Load(path);
|
|
85
|
+
} catch (const FileReadError& error) {
|
|
86
|
+
return LoadResult::kNotFound;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
47
89
|
|
|
48
90
|
## 边界
|
|
49
91
|
|
|
50
|
-
|
|
92
|
+
**差**:库代码捕获后打印并吞掉异常。
|
|
51
93
|
|
|
52
94
|
```cpp
|
|
53
95
|
void AddTask(Task task) {
|
|
@@ -59,10 +101,9 @@ void AddTask(Task task) {
|
|
|
59
101
|
}
|
|
60
102
|
```
|
|
61
103
|
|
|
62
|
-
|
|
104
|
+
**好**:库让异常向上传播,入口统一翻译成用户可读的诊断。
|
|
63
105
|
|
|
64
106
|
```cpp
|
|
65
|
-
// main.cpp
|
|
66
107
|
int main() {
|
|
67
108
|
try {
|
|
68
109
|
return Run(std::cin, std::cout);
|
|
@@ -73,29 +114,7 @@ int main() {
|
|
|
73
114
|
}
|
|
74
115
|
```
|
|
75
116
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
**差**:
|
|
79
|
-
|
|
80
|
-
```cpp
|
|
81
|
-
try {
|
|
82
|
-
Load(path);
|
|
83
|
-
} catch (...) {
|
|
84
|
-
return; // 不知道发生了什么,状态可能半坏
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
**好**:
|
|
89
|
-
|
|
90
|
-
```cpp
|
|
91
|
-
try {
|
|
92
|
-
Load(path);
|
|
93
|
-
} catch (const std::filesystem::filesystem_error& error) {
|
|
94
|
-
return LoadResult::kNotFound;
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
只捕获能处理的类型;其他异常继续抛。
|
|
117
|
+
退出码由项目入口合同决定;这里只示范「捕获、打印、非零返回」。
|
|
99
118
|
|
|
100
119
|
## 析构
|
|
101
120
|
|
|
@@ -121,13 +140,13 @@ try {
|
|
|
121
140
|
|
|
122
141
|
## `std::format` 与 `std::print`
|
|
123
142
|
|
|
124
|
-
C++
|
|
143
|
+
C++23 且标准库支持 `std::print` 时,程序入口或 CLI 可以写:
|
|
125
144
|
|
|
126
145
|
```cpp
|
|
127
|
-
std::print("任务: {}\n", task.name);
|
|
146
|
+
std::print("任务: {}\n", task.name);
|
|
128
147
|
```
|
|
129
148
|
|
|
130
|
-
|
|
149
|
+
库代码、以及需要兼容 C++11–C++20 的代码仍写:
|
|
131
150
|
|
|
132
151
|
```cpp
|
|
133
152
|
std::cout << std::format("任务: {}\n", task.name);
|
|
@@ -103,12 +103,12 @@ namespace string_utils { // 一组自由函数
|
|
|
103
103
|
**好**:
|
|
104
104
|
|
|
105
105
|
```cpp
|
|
106
|
-
[[nodiscard]] const Task& GetTask(std::string_view name) const; // 找不到抛
|
|
106
|
+
[[nodiscard]] const Task& GetTask(std::string_view name) const; // 找不到抛 TaskNotFoundError
|
|
107
107
|
[[nodiscard]] const Task* FindTask(std::string_view name) const; // 找不到返回 nullptr
|
|
108
108
|
[[nodiscard]] bool HasTask(std::string_view name) const; // 只回答存在性
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
`Get`
|
|
111
|
+
`Get` 表示结果一定存在(否则抛出声明过的领域异常),`Find` 表示可能找不到,`Has` / `Contains` 只回答是否存在。不要用 `Find` 返回 `bool`。
|
|
112
112
|
|
|
113
113
|
## 谓词与 Lambda
|
|
114
114
|
|