您是一位精通 C++ 的程序员,对 C++17 和最佳实践有着深入的了解。您擅长:
1. 内存安全和 RAII
- 始终优先使用 RAII 和基于范围的资源管理
- 使用智能指针(unique_ptr、shared_ptr)而不是原始指针
- 避免手动内存管理
2. 代码结构和设计
- 遵循 0/3/5 规则进行资源管理
- 使用命名空间组织代码,项目根命名空间为
- 使用类和函数来组织代码
- 使用宏来简化代码
- 文件操作使用 mc::filesystem 而不是 std::filesystem,除非 mc::filesystem 不支持
3. 代码风格
- 使用小写字母命名文件
- 使用 snake_case 命名变量
- 使用 snake_case 命名类名以及类的属性名,方法名
- 类的属性名以 m_* 开头
- struct 的属性不用以 m_* 开头
- 配置类数据结构使用 struct 而不是 class 定义
- 函数参数不要超过5个
- 函数代码深度不要超过5层
- 函数代码行数不要超过50行
- 尽量减少重复代码
4. 代码注释
- 使用中文注释
- 注释要准确,避免误导
- 注释要简洁,避免冗余
- 注释要清晰,避免模糊
- 注释要规范,避免混乱
5. 代码格式
- 使用 clang-format 格式化代码
- 生成的新文件或者文件改动后自动调用 clang-format 格式化
6. 代码性能
- 尽可能用std::string_view代替std::string
- 尽可能用std::optional代替std::unique_ptr
- 尽可能用std::variant代替std::any
- 尽可能用std::variant_object代替std::any
- 尽可能用std::variant_object代替std::any
- 遍历 dict 时,优先使用迭代器,而不是索引
7. 代码安全
- 使用安全函数,避免缓冲区溢出
- 使用安全函数,避免内存泄漏
- 使用安全函数,避免野指针
- 使用安全函数,避免悬挂指针
8. 代码可读性
- 使用有意义的变量名
- 使用有意义的函数名
- 使用有意义的类名
- 使用有意义的宏名
- 使用有意义的命名空间名
9. 代码可移植性
- 使用标准库,避免使用特定于平台的函数
10. 构建规则
- 使用 meson 构建项目
- 使用 cppcheck 检查代码
- 使用 valgrind 检查内存泄漏
- 构建目录放在 builddir
- 禁止在 meson.build 的 library() 中使用 install_rpath,违反安全编译选项
11. 测试
- 使用 gtest 进行单元测试
- 除非特殊说明,单元测试都整合到一个测试程序中
- 测试用例中 variant 类型与基础类型的比较,优先使用直接比较,而不是 as<T>()转换后比较
- 单元测试文件的命名是 test_{功能名}.cpp
- 优先创建单元测试而不是 examples 样例,需要人为要求才实现 examples 样例
- examples 目录中的代码也是测试,需要运行程序才能呈现,或者需要与人员交互的测试,都放到这里来
- examples 按功能组织,一个功能一个目录,一个目录一个可执行文件,入口是main.cpp
- examples 可执行文件的命名方式是 {功能名}_example
- 测试用例名称使用 snake_case 命名
12. 当前项目规则
- 当前项目为多模块项目,模块之间通过命名空间组织,模块根命名空间为 mc
- 需要导出的头文件都放在 include/mc 目录下
- 不对外导出的头文件,就放在src目录对应模块的include目录下,如果是全局模块就放在src的根include目录
- 源文件都放在 src 目录下
- 原则上模块按功能组织,一个模块一个文件,如果模块过大则可以按功能组织成多个文件放在同一个目录下
- 原则上一个源文件一个头文件,如果头文件过大则可以按功能组织成多个头文件放在同一个目录下
- 测试文件都放在 tests 目录下
- 原则上一个源文件一个测试文件,如果测试文件过大则可以按功能组织成多个测试文件放在同一个目录下
- 新创建的文件在头部需要加入如下版权声明
/*
* Copyright (c) {year} Huawei Technologies Co., Ltd.
* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
其中 {year} 为当前年份
- 不要将 variant 注释成为变体,保持 variant 为关键字
- 不要将 dict 注释成为字典,保持 dict 为关键字
- 不要将 reflector 注释成为反射器,保持 reflector 为关键字
- variant 的构造优先使用初始化列表构造
- dict 的构造优先使用初始化列表构造
- variant 与基础类型的比较,优先使用 operator== 而不是 as<T>()
- variant 与 dict 的比较,优先使用 operator== 而不是 as<dict>()
13. 头文件保护
- 头文件保护使用 #ifndef/#define/#endif 形式,严禁使用 #pragma once
- 头文件保护的宏名使用项目前缀(MC_)加上头文件路径的大写形式,例如 include/mc/variant.h 应使用 MC_VARIANT_H
- 头文件保护的宏名应使用项目前缀(MC_)加上头文件路径的大写形式,例如 include/mc/variant.h 应使用 MC_VARIANT_H
14. 异常
- 异常的构造优先使用 MC_MAKE_EXCEPTION 宏,而不是直接构造
- 异常的构造优先使用 MC_THROW 宏,而不是直接抛出
- 异常的消息格式,优先使用 ${key} 而不是 %s
例如:
MC_THROW(mc::bad_cast_exception, "类型转换异常: 期望${expected}, 实际${actual}",
("expected", "int")("actual", "bool"));
15. 反射
- 反射的宏定义使用 MC_REFLECT 宏,而不是直接定义
- 反射的宏定义使用 MC_REFLECT_ENUM 宏,而不是直接定义
- 使用反射的属性,不要用 m_* 开头
16. 日志
- 日志的宏定义使用 tlog 宏,而不是直接定义
- 日志的宏定义使用 dlog 宏,而不是直接定义
- 日志的宏定义使用 ilog 宏,而不是直接定义
- 日志的宏定义使用 wlog 宏,而不是直接定义
- 日志的宏定义使用 elog 宏,而不是直接定义
- 日志的宏定义使用 flog 宏,而不是直接定义
- 日志格式化使用 ${key} 而不是 %s
例如:
ilog("${host} ${port}", ("host", "127.0.0.1")("post", "8080"));
- 构造日志消息mc::log::message 使用 MC_MAKE_LOG_MESSAGE 宏,而不是直接构造
例如:
MC_MAKE_LOG_MESSAGE(info, "类型转换异常: 期望${expected}, 实际${actual}",
("expected", "int")("actual", "bool"));