---
name: cpp-algo-style
description: MaaEnd cpp-algo C++ 编码风格与工程规范指南。参考 MaaFramework 的优秀实践，规范命名、头文件、公共工具、错误处理、日志、CMake 等方面。在编写、修改或审查 agent/cpp-algo/ 下的 C++ 代码时使用。
---

# cpp-algo C++ 编码风格指南

本指南以 MaaFramework 源码为标杆，针对 cpp-algo 中已知的工程缺陷制定规范。

## 1. 命名规范

cpp-algo 当前最严重的问题是 **命名风格不统一**，同一个项目中混用了多种约定。

### 必须遵循的规则

| 元素 | 风格 | 示例 |
|------|------|------|
| 类 / 结构体 / 枚举 | PascalCase | `MapLocator`, `MatchFeature` |
| 成员函数 | camelCase | `initialize()`, `resetTrackingState()` |
| 自由函数 | PascalCase（对外）/ snake_case（内部工具） | `CreateInputBackend()` |
| 成员变量 | snake_case + 尾下划线 `_` | `locator_`, `current_zone_id_` |
| 局部变量 | snake_case | `search_rect`, `fine_result` |
| 常量（constexpr） | `k` 前缀 + PascalCase | `kDefaultMinimapRoi`, `kMaxLostFrames` |
| 宏 | ALL_CAPS | `MAA_TRUE`, `LOG_ARGS` |
| 命名空间 | lowercase | `maplocator`, `mapnavigator` |
| 模板参数 | 后缀 `_t` 或 PascalCase | `Item_t`, `OutT` |
| 枚举值 | PascalCase | `TrackingLost`, `ScreenBlocked` |

### 已知违规（修改时顺手修复）

- `MapPosition` 中 `zoneId`、`sliceIndex` 等用了 camelCase 成员，应改为 `zone_id_`、`slice_index_`
- `TrackingConfig` 中 `maxNormalSpeed`、`screenBlockedThreshold` 同理
- `MatchStrategy` 中 `_isBase`（前导下划线）应改为 `is_base_`
- 常量 `MinimapROIOriginX`、`MaxLostTrackingCount`、`MinMatchScore` 缺少 `k` 前缀

## 2. 头文件规范

### Include Guard

统一使用 `#pragma once`（已做到）。

### Include 顺序

按以下分组排列，组间空行分隔：

1. 本 `.cpp` 对应的 `.h`
2. C++ 标准库 `<algorithm>`, `<string>` ...
3. 第三方库 `<opencv2/...>`, `<meojson/json.hpp>`, `<onnxruntime/...>`
4. MaaFramework `<MaaFramework/...>`, `<MaaUtils/...>`
5. 本项目头文件 `"MapTypes.h"`, `"../utils.h"`

### OpenCV 引入

**必须** 通过 `<MaaUtils/NoWarningCV.hpp>` 引入 OpenCV，禁止直接 `<opencv2/opencv.hpp>`。这是 MaaFramework 的统一做法，用于抑制编译器警告。

当前违规文件：`MatchStrategy.h`、`MotionTracker.h`、`YoloPredictor.h` 直接引入了 `<opencv2/opencv.hpp>`。

### MaaFramework 头文件引号

对外部依赖（MaaFramework、第三方库）统一使用尖括号 `<>`，对本项目内部头文件使用双引号 `""`。

## 3. 公共工具复用（消除重复代码）

cpp-algo 中存在多处 **重复实现**，必须提取到公共头文件。

### 必须提取的工具

**`ScopedImageBuffer`** — 当前在 `MapLocateAction.cpp`、`position_provider.cpp`、`adb_input_backend.cpp` 三处重复定义，应提取到公共头文件（如 `source/common/scoped_buffer.h`）：

```cpp
class ScopedImageBuffer
{
public:
    ScopedImageBuffer() : buffer_(MaaImageBufferCreate()) {}
    ~ScopedImageBuffer() { MaaImageBufferDestroy(buffer_); }

    ScopedImageBuffer(const ScopedImageBuffer&) = delete;
    ScopedImageBuffer& operator=(const ScopedImageBuffer&) = delete;

    MaaImageBuffer* Get() const { return buffer_; }

private:
    MaaImageBuffer* buffer_;
};
```

**`DetectControllerType`** — 当前在 `MapLocateAction.cpp`、`position_provider.cpp`、`backend.cpp` 三处重复实现，应提取到 `controller_type_utils.h`。

**`MAA_TRUE` / `MAA_FALSE` 宏** — 在多个 `.cpp` 中条件定义。应在一个公共头文件中统一处理，或直接使用 `MaaBool` 的 `1` / `0`。

### 新增公共工具的原则

- 在 `source/common/` 下建立公共头文件
- 跨模块（MapLocator / MapNavigator）共用的工具放这里
- 模块内部工具放在模块自己的匿名命名空间或 `detail` 命名空间中

## 4. 命名空间

### 正确做法

- 顶层按模块分：`maplocator`、`mapnavigator`
- 后端按层级嵌套：`mapnavigator::backend::adb`
- 实现细节用匿名命名空间（`namespace { }` 在 `.cpp` 中）

### 避免的问题

`utils::SleepFor` 嵌套在 `mapnavigator` 命名空间中（`navi_math.h`），而 `source/utils.h` 是另一个全局工具头。这造成了命名空间语义冲突。应将通用工具统一放入 `source/common/`。

## 5. 类设计

### 推荐模式（参考 MaaFramework）

- **PIMPL**：对外暴露的复杂类使用 PIMPL 隐藏实现（`MapLocator` 已正确使用）
- **NonCopyable**：需要禁止拷贝的类应明确 `= delete` 拷贝构造和赋值（`ScopedImageBuffer` 已做到，但建议提取基类或用宏）
- **Strategy 模式**：`IMatchStrategy` + Factory 的设计是好的，保持
- **RAII**：资源获取即初始化，析构时释放（`ScopedImageBuffer` 是好例子）

### 需要改进的点

- `NavigationStateMachine` 持有多个裸指针（`ActionWrapper*` 等），生命周期依赖调用者保证——应添加注释说明所有权语义，或使用 `std::shared_ptr` / `std::weak_ptr`
- 全局单例 `getOrInitLocator()` 使用 `static std::shared_ptr`——可接受但应注意线程安全和测试性

## 6. 日志规范

遵循 [maa-logging skill](../maa-logging/SKILL.md) 的完整指南。此处强调 cpp-algo 特有的问题：

### 禁止高频大量日志

YOLO 推理中每帧输出完整 softmax 向量是 **严重性能问题**：

```cpp
// 错误 — 每帧打印完整分类向量
LogInfo << "YOLO Raw All:" << yoloClassNames << std::vector<float>(...);

// 正确 — 仅输出关键结果，详细信息用 LogTrace
LogDebug << "YOLO:" << VAR(predicted_name) << VAR(max_conf);
LogTrace << "YOLO all scores:" << scores;
```

### 日志级别选择

| 场景 | 级别 |
|------|------|
| 初始化成功/失败、关键状态变更 | `LogInfo` |
| 定位结果、导航阶段切换 | `LogInfo` |
| 匹配分数、中间计算 | `LogDebug` |
| 完整矩阵/向量数据 | `LogTrace` |
| 可恢复异常（追踪丢失） | `LogWarn` |
| 不可恢复错误 | `LogError` |

## 7. 错误处理

### 模式

- 返回 `bool` / `std::optional` 表示成功/失败
- 失败路径 `LogError` + 早期 `return`
- OpenCV 操作用 `try/catch` 保护（`CoreMatch` 中已有，应推广到其他 cv 调用密集处）
- MaaFramework C API 返回值必须检查

### 禁止

- 静默忽略错误
- 假设指针非空而不检查

## 8. 现代 C++ 用法

项目目标 **C++20**，应积极使用现代特性：

| 推荐 | 示例 |
|------|------|
| `std::optional` | 返回可能失败的结果 |
| 指定初始化器 | `LocateResult { .status = ..., .position = ... }` |
| `std::filesystem` | 路径操作 |
| `std::format` | 字符串格式化（替代 `std::stringstream`） |
| `std::ranges` | 容器算法链（`controller_type_utils.h` 中已用） |
| `constexpr` | 编译期常量 |
| 结构化绑定 | `auto [x, y] = getPosition();` |
| smart pointers | `std::unique_ptr` / `std::shared_ptr` 管理资源 |

### X-Macro 的使用

`NAVI_ACTION_TYPES(X)` 宏用于生成枚举和字符串映射。这种模式可以接受，但应：
- 在宏定义处添加注释解释用途
- 确保使用 `#undef` 清理临时宏

## 9. 魔法数字

cpp-algo 中散布大量硬编码阈值（`0.43`、`4.5`、`0.85`、`0.55` 等）。

### 规则

- **所有阈值** 必须定义为 `constexpr` 命名常量，带 `k` 前缀
- 常量定义集中放在对应模块的 config 结构体或头文件顶部
- 必须附带注释说明物理含义和调优依据

```cpp
// 错误
if (score < 0.55) { return false; }

// 正确
constexpr double kGlobalSearchPassThreshold = 0.55;  // 全局搜索及格线，容忍 UI 遮挡 + 光影
if (score < kGlobalSearchPassThreshold) { return false; }
```

## 10. CMake 规范

### 禁止 `file(GLOB_RECURSE)`

当前 `source/CMakeLists.txt` 使用 `file(GLOB_RECURSE)` 自动收集源文件。CMake 官方文档明确不推荐此做法（新增/删除文件不会触发重新配置）。

应改为显式列出源文件：

```cmake
target_sources(cpp-algo PRIVATE
    main.cpp
    MapLocator/MapLocator.cpp
    MapLocator/MapLocateAction.cpp
    # ...
)
```

### 清理未使用变量

`${cpp_algo_header}` 从未定义却被引用，应清除。

## 11. 文件命名

| 元素 | 风格 | 示例 |
|------|------|------|
| 类对应的文件 | PascalCase | `MapLocator.h`, `MapLocator.cpp` |
| 工具/非类文件 | snake_case | `controller_type_utils.h`, `navi_math.h` |
| 目录 | PascalCase（模块）/ snake_case（工具） | `MapLocator/`, `Backend/Adb/` |

当前 `my_reco_1/` 是示例模板目录，如果保留应重命名为有意义的名称。

## 12. 注释语言

- 代码注释使用 **中文** 或 **英文** 均可，但单个文件内保持一致
- 对外接口（`.h` 中的 public 方法）建议英文注释
- 算法实现细节（`.cpp` 中）用中文注释解释"为什么"是可以的（当前做得好的部分）

## 审查清单

修改 cpp-algo 代码时，对照检查：

- [ ] 命名风格是否符合上表
- [ ] 是否引入了重复代码（检查是否已有公共工具）
- [ ] OpenCV 是否通过 `NoWarningCV.hpp` 引入
- [ ] 日志级别是否合理，是否避免了高频大量输出
- [ ] 新常量是否有 `k` 前缀和注释
- [ ] 错误路径是否有日志和合理返回值
- [ ] 新文件是否加入了 CMakeLists.txt 的显式列表（如已迁移）

详细的命名对照和重构示例见 [reference.md](reference.md)。
