@chengzhao-dev/dsh-cpp-workflow 0.1.1 → 0.3.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 (82) hide show
  1. package/README.en.md +18 -15
  2. package/README.md +18 -15
  3. package/lib/index.js +7 -8
  4. package/lib/workspace.js +14 -14
  5. package/locale/en.json +1 -1
  6. package/locale/zh.json +1 -1
  7. package/package.json +7 -5
  8. package/skills/building-cpp/SKILL.md +8 -6
  9. package/skills/building-cpp/assets/config/.clang-format +5 -38
  10. package/skills/building-cpp/assets/config/.clang-tidy +8 -39
  11. package/skills/building-cpp/assets/config/.clangd +2 -5
  12. package/skills/building-cpp/assets/config/.vscode/extensions.json +2 -1
  13. package/skills/building-cpp/assets/config/.vscode/settings.json +16 -1
  14. package/skills/building-cpp/assets/templates/projects/common/running-build.sh +29 -18
  15. package/skills/building-cpp/assets/templates/projects/multi/CMakeLists.txt +9 -5
  16. package/skills/building-cpp/assets/templates/projects/multi/include/greeting.h +5 -3
  17. package/skills/building-cpp/assets/templates/projects/multi/src/greeting.cc +18 -0
  18. package/skills/building-cpp/assets/templates/projects/multi/src/main.cc +8 -0
  19. package/skills/building-cpp/assets/templates/projects/shared-library/CMakeLists.txt +8 -3
  20. package/skills/building-cpp/assets/templates/projects/shared-library/greeting/CMakeLists.txt +15 -5
  21. package/skills/building-cpp/assets/templates/projects/shared-library/greeting/include/greeting.h +5 -3
  22. package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/greeting.cc +18 -0
  23. package/skills/building-cpp/assets/templates/projects/shared-library/src/main.cc +8 -0
  24. package/skills/building-cpp/assets/templates/projects/single/CMakeLists.txt +7 -3
  25. package/skills/building-cpp/assets/templates/projects/single/src/main.cc +15 -0
  26. package/skills/building-cpp/assets/templates/projects/static-library/CMakeLists.txt +8 -3
  27. package/skills/building-cpp/assets/templates/projects/static-library/greeting/CMakeLists.txt +15 -5
  28. package/skills/building-cpp/assets/templates/projects/static-library/greeting/include/greeting.h +5 -3
  29. package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/greeting.cc +18 -0
  30. package/skills/building-cpp/assets/templates/projects/static-library/src/main.cc +8 -0
  31. package/skills/building-cpp/examples/cmake.md +17 -17
  32. package/skills/building-cpp/examples/environment.md +4 -3
  33. package/skills/building-cpp/examples/tooling.md +31 -16
  34. package/skills/building-cpp/references/build-script.md +2 -2
  35. package/skills/building-cpp/references/cmake.md +35 -17
  36. package/skills/building-cpp/references/encoding.md +49 -0
  37. package/skills/building-cpp/references/environment.md +17 -13
  38. package/skills/building-cpp/references/linking.md +6 -6
  39. package/skills/building-cpp/references/standards/README.md +165 -0
  40. package/skills/building-cpp/references/standards/c++11/README.md +50 -0
  41. package/skills/building-cpp/references/standards/c++11/cmake-snippet.cmake +7 -0
  42. package/skills/building-cpp/references/standards/c++14/README.md +48 -0
  43. package/skills/building-cpp/references/standards/c++14/cmake-snippet.cmake +7 -0
  44. package/skills/building-cpp/references/standards/c++17/README.md +49 -0
  45. package/skills/building-cpp/references/standards/c++17/cmake-snippet.cmake +7 -0
  46. package/skills/building-cpp/references/standards/c++20/README.md +49 -0
  47. package/skills/building-cpp/references/standards/c++20/cmake-snippet.cmake +7 -0
  48. package/skills/building-cpp/references/standards/c++23/README.md +50 -0
  49. package/skills/building-cpp/references/standards/c++23/cmake-snippet.cmake +7 -0
  50. package/skills/building-cpp/references/static-analysis.md +27 -15
  51. package/skills/building-cpp/scripts/checking-encoding.mjs +241 -0
  52. package/skills/building-cpp/scripts/checking-environment.mjs +21 -21
  53. package/skills/building-cpp/steps/01-build.md +10 -8
  54. package/skills/building-cpp/steps/02-tooling.md +9 -7
  55. package/skills/building-cpp/steps/03-check.md +9 -7
  56. package/skills/developing-cpp/SKILL.md +8 -6
  57. package/skills/developing-cpp/examples/comments.md +39 -8
  58. package/skills/developing-cpp/examples/exceptions.md +64 -45
  59. package/skills/developing-cpp/examples/naming.md +2 -2
  60. package/skills/developing-cpp/examples/structure.md +12 -9
  61. package/skills/developing-cpp/references/comments.md +28 -8
  62. package/skills/developing-cpp/references/declaration-order.md +7 -4
  63. package/skills/developing-cpp/references/exceptions.md +83 -46
  64. package/skills/developing-cpp/references/glossary.md +6 -6
  65. package/skills/developing-cpp/references/google-cpp-style.md +27 -37
  66. package/skills/developing-cpp/references/language.md +4 -4
  67. package/skills/developing-cpp/references/layout.md +14 -14
  68. package/skills/developing-cpp/references/naming.md +15 -6
  69. package/skills/developing-cpp/references/project-design.md +12 -3
  70. package/skills/developing-cpp/references/standard-library.md +1 -1
  71. package/skills/developing-cpp/steps/02-boundary.md +8 -7
  72. package/skills/developing-cpp/steps/03-declaration.md +9 -8
  73. package/skills/developing-cpp/steps/04-implementation.md +11 -7
  74. package/templates/cpp-workflow-project/SKILL.md +1 -1
  75. package/templates/cpp-workflow-project/environment.json +2 -1
  76. package/skills/building-cpp/assets/templates/projects/multi/src/greeting.cpp +0 -13
  77. package/skills/building-cpp/assets/templates/projects/multi/src/main.cpp +0 -8
  78. package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/greeting.cpp +0 -13
  79. package/skills/building-cpp/assets/templates/projects/shared-library/src/main.cpp +0 -8
  80. package/skills/building-cpp/assets/templates/projects/single/src/main.cpp +0 -9
  81. package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/greeting.cpp +0 -13
  82. package/skills/building-cpp/assets/templates/projects/static-library/src/main.cpp +0 -8
@@ -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
- - 首次复制时按轴替换两个值:`compiler=clang` 用 `CompilationDatabase: build/clang` 和 `--query-driver=/usr/bin/clang++`;`compiler=gcc` 用 `build/gcc` 和 `/usr/bin/g++`。只复制模板而不改这两处,GCC 模式会指向错误的数据库或驱动。原工程已有的 `.clangd` 不覆盖;要改先征得同意。
16
- - `.clang-format` 固定 `BasedOnStyle: Google`、`Standard: c++20`、`ColumnLimit: 100`、`DerivePointerAlignment: false`、`PointerAlignment: Left`;`IncludeCategories` 维持包含顺序。不要逐项手调换行。
17
- - `.clang-tidy` 保留 `bugprone-*`、`cppcoreguidelines-*`、`misc-*`、`modernize-*`、`performance-*`、`portability-*`、`readability-*`、`clang-analyzer-core.*` 和四条 `google-*` 的主体;关闭项必须写理由。不要把整个检查族一关了之。
18
- - `.vscode/extensions.json` 推荐 clangd 与 CMake Tools;`.vscode/settings.json` 固定 clangd 参数和 CMake 构建目录。配置注释用 JSONC 的 `//`,贴在被解释的键上方。
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/` 两个文件,按编译器的映射改 `.clangd` 的编译数据库目录和 `.vscode/settings.json` 的 `--query-driver` 路径。不要复制 `build/`。
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
- **已有工程**:先跑一次 `clang-format --dry-run --Werror` 和 `clang-tidy`,看现有配置实际报什么;只改导致当前失败或与声明环境冲突的项。不要借机重排全工程格式。
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,15 +8,17 @@
8
8
 
9
9
  按固定顺序跑,前一步失败就停,不要带着失败继续:
10
10
 
11
- 1. **校验环境声明(不带 `--live`)**:读 `environment.json`,检查三个轴和组合是否合法。轴为空或组合非法时,停在这里,不找可执行文件。
12
- 2. **活动校验**:在声明的 build host 上跑 `--live`,核对 PATH 中的角色和版本。Windows 原生组合当前没有构建脚本,`--live` 会明确报告「缺的是 shell,不是编译器」。
13
- 3. **跑项目入口命令**:项目技能里写的入口命令(如 `./running-build.sh clang`)。入口内部依次 configure → build → format → tidy → ctest → run;每一步失败都保留原始输出。
14
- 4. **核对产物**:`build/<compiler>/compile_commands.json` 存在;`build/compile_commands.json` 指向当前编译器;`app` 可执行文件在 `build/<compiler>/bin`。
15
- 5. **记录结果**:通过时记录编译器版本、构建目录、测试数量;失败时记录命令、退出码、第一条根因诊断。
11
+ 1. **校验编码与行尾**:在仓库根跑编码检查。默认 UTF-8 无 BOM、LF;MSVC 原生工程加 `--profile msvc`。有问题先按 `references/encoding.md` 修正,不带着 BOM 或混用行尾继续。
12
+ 2. **校验环境声明(不带 `--live`)**:读 `environment.json`,检查三个环境轴、`standard` 和组合是否合法。环境轴为空、`standard` 非法或组合非法时,停在这里,不找可执行文件。
13
+ 3. **活动校验**:在声明的 build host 上跑 `--live`,核对 PATH 中的角色和版本。`windows` + `native` 在声明校验阶段就返回失败,不会进入这一步。
14
+ 4. **跑项目入口命令**:项目技能里写的入口命令(如 `./running-build.sh clang`)。入口命令仍是 `<entry-command>` 或空白时,停在这里,不要猜命令;是真实命令才跑。入口内部依次 configure → build → format → tidy → ctest → run;每一步失败都保留原始输出。
15
+ 5. **核对产物**:`build/<compiler>/compile_commands.json` 存在;`build/compile_commands.json` 指向当前编译器;`app` 可执行文件在 `build/<compiler>/bin`。
16
+ 6. **记录结果**:通过时记录编译器版本、构建目录、测试数量;失败时记录命令、退出码、第一条根因诊断。
16
17
 
17
- 环境校验脚本:
18
+ 编码检查与环境校验脚本:
18
19
 
19
20
  ```bash
21
+ node "<skill-dir>/scripts/checking-encoding.mjs" --profile portable # UTF-8 无 BOM、LF
20
22
  node "<skill-dir>/scripts/checking-environment.mjs" --project . # 声明校验
21
23
  node "<skill-dir>/scripts/checking-environment.mjs" --project . --live # 活动校验
22
24
  ```
@@ -49,7 +51,7 @@ node "<skill-dir>/scripts/checking-environment.mjs" --project . --live # 活
49
51
 
50
52
  **没有测试**:CTest 显示没有测试是合法结果;不要为了「全绿」造空测试。行为验收由定行为的验收句负责。
51
53
 
52
- **Windows 原生**:当前版本没有 PowerShell 构建脚本;`--live` 会失败并说明缺 shell。不要伪造一个临时脚本绕过,也不要把 `build_host` 改成 `wsl` 来「让它过」;先和用户确认是否补脚本。
54
+ **Windows 原生**:本版本不支持 `host=windows` + `build_host=native`。声明校验会返回 `ok: false`,`issues` 只有一句「本版本不能在 Windows 原生壳里构建」。停在这里,不要伪造 PowerShell 脚本,不要建议改 WSL 来绕过,也不要让代理改 `environment.json` 过关。
53
55
 
54
56
  **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
57
 
@@ -1,18 +1,20 @@
1
1
  ---
2
2
  name: developing-cpp
3
- description: 编写或修改 C++20 源码、头文件、命名、异常与源码注释时使用;按定行为、定边界、定声明、定实现四步推进。不用于只改文档、Python、CMake、构建脚本或与 C++ 无关的文件。
3
+ description: 编写或修改 C++11–C++23 源码、头文件、命名、异常与源码注释时使用;新工程默认 C++23;按定行为、定边界、定声明、定实现四步推进。一次改动同时碰到源码和构建文件时,先打开本技能。不用于只改文档、Python、CMake、构建脚本或与 C++ 无关的文件。
4
4
  metadata:
5
- short-description: C++20 源码四步
5
+ short-description: C++11–C++23 源码四步
6
6
  ---
7
7
 
8
8
  # developing-cpp
9
9
 
10
10
  ## 适用场景
11
11
 
12
- 管 `.h` / `.cpp` 里换一台机器也不会改的内容:可观察行为、库与程序的边界、声明、命名、异常和源码注释。`CMakeLists.txt`、`cmake/`、`running-build.sh`、`.clang-*`、`.vscode/` 和 `environment.json` 只开 `building-cpp`;两边都要动时,先做完第 2 步定边界,再开 `building-cpp`。`.qmd`、仓库 README、Python 和非 C++ 文档不打开本技能。
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
 
16
+ 源码和构建文件的编码以 `building-cpp` 的 `references/encoding.md` 为准;行尾与 `.clang-format` 的 `LineEnding` 跟仓库根 `.gitattributes` 和 `.editorconfig`。
17
+
16
18
  ## 两种走法
17
19
 
18
20
  | 走法 | 什么时候用 | 怎么做 |
@@ -29,7 +31,7 @@ metadata:
29
31
  | 1 | 定行为 | `steps/01-behavior.md` | 把这一处要能观察到的结果写成验收句 |
30
32
  | 2 | 定边界 | `steps/02-boundary.md` | 决定哪些放库、哪些放程序,依赖朝哪个方向 |
31
33
  | 3 | 定声明 | `steps/03-declaration.md` | 写命名、声明顺序、头文件契约和声明注释 |
32
- | 4 | 定实现 | `steps/04-implementation.md` | 写 `.cpp` 的操作顺序、分组和实现注释 |
34
+ | 4 | 定实现 | `steps/04-implementation.md` | 写 `.cc` 的操作顺序、分组和实现注释 |
33
35
 
34
36
  每步只打开同号的一个步骤文件。判不准才打开 `examples/` 里与该步对应的案例;`references/` 保存完整条文,只在排错、整目录重构,或步骤里明确指向某一条文时按需打开。
35
37
 
@@ -37,6 +39,6 @@ metadata:
37
39
 
38
40
  - 第 1 步的行为句能被用户或测试验收;有阶段表时只覆盖对应行,没有时只覆盖用户点名的行为。
39
41
  - 第 2 步的库 target 不依赖终端、GUI 或脚本;程序 target 只做装配和输入输出;共享抽象有第二个真实使用方。
40
- - 第 3 步的公共头文件自包含,命名、声明顺序和异常合同符合 `references/` 的条文。
41
- - 第 4 步的实现只兑现声明;资源由 RAII 管理;可预期失败用返回值,合同违反才抛异常。
42
+ - 第 3 步的公共头文件自包含,命名、声明顺序和异常声明符合 `references/` 的条文。
43
+ - 第 4 步的实现只兑现声明;资源由 RAII 管理;失败是正常结果时用返回值,函数无法完成任务时抛异常,不拿异常做正常分支。
42
44
  - 没有改 `CMakeLists.txt`、`.clang-*`、构建脚本,也没有把外部资料路径或项目专属类名写进通用条文。
@@ -5,7 +5,7 @@
5
5
  **差**:
6
6
 
7
7
  ```cpp
8
- // task_list.cpp
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 std::out_of_range index 越界
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
- # 100 列:模板和格式化串在 80 列下换行过碎。
125
- ColumnLimit: 100
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 std::out_of_range 编号不在当前任务表内
201
- /// @throws std::invalid_argument priority 越界
202
+ /// @throws TaskIndexError 编号不在当前任务表内
203
+ /// @throws InvalidPriorityError priority 越界
202
204
  void SetPriority(TaskId id, int priority);
203
205
  ```
204
206
 
@@ -229,3 +231,32 @@ return true;
229
231
  ```
230
232
 
231
233
  一段连续的实现注释只讲一个判断或一个取舍;不是逐行字幕。
234
+
235
+ ## 成员还是同一命名空间的函数
236
+
237
+ **差**:
238
+
239
+ ```cpp
240
+ class TaskTable {
241
+ public:
242
+ std::size_t CountOverdue() const;
243
+ bool HasOverdue() const { return CountOverdue() > 0; }
244
+ };
245
+ ```
246
+
247
+ `HasOverdue` 只组合公开查询,不需要直接访问表示,却做成了成员。
248
+
249
+ **好**:
250
+
251
+ ```cpp
252
+ class TaskTable {
253
+ public:
254
+ std::size_t CountOverdue() const;
255
+ };
256
+
257
+ bool HasOverdue(const TaskTable& table) {
258
+ return table.CountOverdue() > 0;
259
+ }
260
+ ```
261
+
262
+ 函数和类放在同一命名空间;调用方只看到 `HasOverdue(table)`,类不增加只为转发而存在的成员。
@@ -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); // 格式错误时抛 std::runtime_error
10
+ Task ParseTask(std::string_view line); // 任何格式错误都抛
9
11
  ```
10
12
 
11
- 调用方被迫用 `try/catch` 做正常分支,且无法从类型看出失败。
12
-
13
- **好**:结果类型表达可预期失败,合同违反才抛。
13
+ **好**:接口把「解析可能失败」写进返回类型;只有函数无法完成自己的任务、继续执行没有意义时才抛。
14
14
 
15
15
  ```cpp
16
- enum class ParseError { kEmptyLine, kMissingPriority, kBadPriority };
16
+ enum class ParseStatus { kOk, kEmptyLine, kMissingPriority, kBadPriority };
17
17
 
18
18
  struct ParseResult {
19
19
  std::optional<Task> task;
20
- ParseError error = ParseError::kEmptyLine;
20
+ ParseStatus status = ParseStatus::kEmptyLine;
21
21
  };
22
22
 
23
23
  [[nodiscard]] ParseResult TryParseTask(std::string_view line);
24
24
 
25
- /// @throws std::out_of_range index 越界
25
+ /// @throws TaskIndexError index 不在 [0, size()) 内。
26
26
  [[nodiscard]] Task GetTask(int index) const;
27
27
  ```
28
28
 
29
- `TryParseTask` 的失败是调用方每天都要处理的;`GetTask` 越界是调用方违反合同。
29
+ `TryParseTask` 把失败作为正常结果返回。`GetTask` 的前提是调用方给出有效下标;下标无效时函数无法返回一个任务,于是抛出带领域语义的 `TaskIndexError`。如果越界其实是正常输入,应该改成返回 `std::optional<Task>`,而不是让调用方捕获异常。
30
30
 
31
- ## 消息格式
31
+ ## 异常类型与消息
32
32
 
33
- **差**:
33
+ **差**:抛内建类型或裸字符串,调用方只能捕获一切。
34
34
 
35
35
  ```cpp
36
- throw std::out_of_range("bad index");
36
+ throw "bad index";
37
37
  ```
38
38
 
39
- **好**:
39
+ **好**:定义有语义的类型;确实没有更多信息时,才直接用标准库通用类型。
40
40
 
41
41
  ```cpp
42
- throw std::out_of_range{
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
- 消息含操作、关键值、期望范围;用 `std::format`,不拼字符串。
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++20 没有 `std::print`。不要写:
143
+ C++23 且标准库支持 `std::print` 时,程序入口或 CLI 可以写:
125
144
 
126
145
  ```cpp
127
- std::print("任务: {}\n", task.name); // C++23
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; // 找不到抛 out_of_range
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` 表示结果一定存在(或按合同失败),`Find` 表示可能找不到,`Has` / `Contains` 只回答是否存在。不要用 `Find` 返回 `bool`。
111
+ `Get` 表示结果一定存在(否则抛出声明过的领域异常),`Find` 表示可能找不到,`Has` / `Contains` 只回答是否存在。不要用 `Find` 返回 `bool`。
112
112
 
113
113
  ## 谓词与 Lambda
114
114
 
@@ -20,7 +20,7 @@
20
20
  **差**:库函数直接打印,程序只调用它。
21
21
 
22
22
  ```cpp
23
- // task_list.cpp(库)
23
+ // task_list.cc(库)
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.cpp(程序)
39
+ // app/cli.cc(程序)
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);
@@ -50,29 +50,32 @@ void PrintTasks(const std::vector<Task>& tasks, std::ostream& out) {
50
50
 
51
51
  **差**:
52
52
 
53
- ```cpp
54
- #include "task_list/task_list.h" // 公共根下又套一层组件名
55
- ```
56
-
57
53
  ```text
58
54
  src/task_list.h // 公共头藏在 src,测试无法只依赖公共接口
55
+ src/detail.h // 私有头被测试直接包含
56
+ ```
57
+
58
+ ```cpp
59
+ #include "src/task_list.h" // 从仓库根写到了实现目录
59
60
  ```
60
61
 
61
62
  **好**:
62
63
 
63
64
  ```text
64
65
  task_list/include/task_list.h // 公共头
65
- task_list/src/task_list.cpp // 实现
66
+ task_list/src/task_list.cc // 实现
66
67
  task_list/src/detail.h // 私有头,只被本组件包含
67
68
  ```
68
69
 
69
70
  ```cpp
70
- #include "task_list.h" // 相对公共 include 根
71
+ #include "task_list/include/task_list.h" // 从源码根写公共头的完整路径
71
72
  ```
72
73
 
74
+ 构建时把仓库根加入 include 搜索路径;不要用 `#include "../task_list/include/task_list.h"` 这种随文件位置漂移的写法。
75
+
73
76
  ## 单文件何时拆库
74
77
 
75
- **先不拆**:一个 `main.cpp`,辅助函数在匿名命名空间,只有程序自己用。
78
+ **先不拆**:一个 `main.cc`,辅助函数在匿名命名空间,只有程序自己用。
76
79
 
77
80
  **该拆**:测试需要直接调用排序逻辑;或第二个程序要复用同一逻辑;或逻辑已经超过一个屏幕。
78
81
 
@@ -28,11 +28,11 @@
28
28
  //===----------------------------------------------------------------------===//
29
29
  ```
30
30
 
31
- - 横幅宽度固定 100 列;`//` 后 3 个 `=`,中间 90 个 `-`,末尾 `===//`。
31
+ - 横幅宽度固定 80 列;`//` 后 3 个 `=`,中间 70 个 `-`,末尾 `===//`。
32
32
  - 横幅只包住逻辑组,不包住单个函数;函数之间用空行,不用横幅。
33
- - 脚本里用 `#===` 形式,宽度同样 100 列;CMake 用 `#---` 小节即可,不用重横幅。
33
+ - 脚本里用 `#===` 形式,宽度同样 80 列;CMake 用 `#---` 小节即可,不用重横幅。
34
34
  - 一个文件不超过 8 个横幅;超过说明文件该拆了。
35
- - 100 列换行分三类:整句在标点处断;参数/返回值列表每项一行;代码示例保持原样,宁可横向滚动也不在半句断开。
35
+ - 80 列换行分三类:整句在标点处断;参数/返回值列表每项一行;代码示例保持原样,宁可横向滚动也不在半句断开。
36
36
 
37
37
  ## 声明注释
38
38
 
@@ -43,14 +43,14 @@
43
43
  ///
44
44
  /// @param id 任务编号,必须在 [0, kTaskCount) 内
45
45
  /// @return 该编号的任务状态
46
- /// @throws std::out_of_range id 越界
46
+ /// @throws TaskIndexError id 越界
47
47
  [[nodiscard]] TaskState GetTaskState(int id) const;
48
48
  ```
49
49
 
50
50
  - 第一行一句话说清「做什么」,空一行后写参数、返回值、异常。
51
51
  - `@param` 要么全写,要么全不写;只有一个参数且含义自明时可以省略。
52
52
  - `@return` 在返回类型自明时省略;`[[nodiscard]]` 已经表达「必须使用」时不要再写一遍。
53
- - `@throws` 必须和 `references/exceptions.md` 的合同一致;不抛的函数不要写。
53
+ - `@throws` 必须和 `references/exceptions.md` 的异常声明一致;不抛的函数不要写。
54
54
  - 禁止 `@todo`、`@fixme`、`@author`、`@since`;待办统一用下面的 TODO 格式。
55
55
  - 枚举值的尾随注释用 `///<`,全部值都写或全部不写,并对齐;语义结果写在最前,编号或内部值写在后面。
56
56
  - 私有声明同样写合同:调用者可能是同一个类的另一个成员,不能假设它知道所有前置条件。
@@ -59,7 +59,7 @@
59
59
 
60
60
  ## 实现注释
61
61
 
62
- `.cpp` 里的注释只写「为什么」和「这里有什么坑」,不复述语句。
62
+ `.cc` 里的注释只写「为什么」和「这里有什么坑」,不复述语句。
63
63
 
64
64
  ```cpp
65
65
  // 先判空再取首元素:空订单列表是合法状态,不能假设至少有一单。
@@ -103,8 +103,10 @@ if (orders_.empty()) {
103
103
  `.clang-format`、`.clang-tidy`、`.clangd`、CMake 的配置项旁边写「为什么这样设」,不写「这一项是什么」。
104
104
 
105
105
  ```yaml
106
- # 团队统一 100 列;80 列在模板和格式化串里换行过碎。
107
- ColumnLimit: 100
106
+ # 列宽、缩进、包含顺序都取 Google 预设,不逐项覆盖。
107
+ Language: Cpp
108
+ BasedOnStyle: Google
109
+ Standard: Latest
108
110
  ```
109
111
 
110
112
  - 配置注释贴在被解释的键上方,不写大段策略头。
@@ -115,6 +117,24 @@ ColumnLimit: 100
115
117
  - 同一个理由只写一次,不逐项复制。
116
118
  - 源码注释不写团队政策或流程;那些属于文档,不属于被阅读的代码。
117
119
 
120
+ ## 润色次序
121
+
122
+ 只改注释时,仍走第 3 步和第 4 步,然后按这个次序回读:
123
+
124
+ 1. 文件头:这个文件负责什么、不负责什么,6 行以内。
125
+ 2. 章节横幅:文件超过约 200 行才分组。
126
+ 3. 声明契约:`///` 写参数、返回值、失败方式;与 `references/exceptions.md` 一致。
127
+ 4. 成员行尾:先写保存的对象,再写约束。
128
+ 5. 实现注释:只写为什么和边界,不翻译下一行。
129
+ 6. 配置注释:按 `building-cpp` 的定工具写;只写为什么这样设。
130
+ 7. 回读:同一概念一种译法;不把读者章节的句子抄进注释。
131
+
132
+ ## 与读者正文的边界
133
+
134
+ - 中文、术语稳定、不复述下一行;两边各自写自己的文件。
135
+ - 句长、段落、标点、书根 README 不在本插件。
136
+ - `///`、横幅、`@param` 不进入 qmd、YAML、CSS。
137
+
118
138
  ## 禁止
119
139
 
120
140
  - 注释里写外部资料路径、项目专属类名或跨仓库引用。
@@ -44,7 +44,8 @@ class TaskTable {
44
44
  ## 命名空间作用域的头文件
45
45
 
46
46
  ```cpp
47
- #pragma once
47
+ #ifndef TASKFLOW_INCLUDE_TASK_TABLE_H_
48
+ #define TASKFLOW_INCLUDE_TASK_TABLE_H_
48
49
 
49
50
  #include <array>
50
51
  #include <cstdint>
@@ -72,15 +73,17 @@ class TaskTable {
72
73
  };
73
74
 
74
75
  } // namespace taskflow
76
+
77
+ #endif // TASKFLOW_INCLUDE_TASK_TABLE_H_
75
78
  ```
76
79
 
77
80
  顺序:包含 → 命名空间 → `inline constexpr` 常量 → `enum class` → `struct`(被依赖的在前)→ 自由函数 → 类。类的公共接口类型放在类内最前。
78
81
 
79
- ## `.cpp` 的定义顺序
82
+ ## `.cc` 的定义顺序
80
83
 
81
84
  - 与头文件声明同序:先类型和常量,再自由函数,再成员函数;成员函数按类内声明顺序。
82
- - 文件头注释之后先放本文件的 `namespace taskflow {`,再放公共定义;不要在生产 `.cpp` 里开匿名命名空间,文件内辅助函数用 `static` 并放在项目命名空间内,测试文件例外。
83
- - 构造函数、析构函数、拷贝/移动定义放在成员函数组的最前;`= default` 的定义放 `.cpp` 时紧跟声明顺序。
85
+ - 文件头注释之后先放本文件的 `namespace taskflow {`,再放公共定义;不需要外部引用的实现放匿名命名空间,或声明 `static`;按是否需要内部链接选择,不要为了「整洁」强行加。
86
+ - 构造函数、析构函数、拷贝/移动定义放在成员函数组的最前;`= default` 的定义放 `.cc` 时紧跟声明顺序。
84
87
  - 不要为了「先编译」重排定义;C++ 不要求定义顺序,读者顺序优先。
85
88
 
86
89
  ## 空行