CANN hixl 仓库 C++ 代码风格规范实战指南:命名、格式与注释规范全解析
2026/9/18 22:00:11 网站建设 项目流程

CANN hixl 仓库 C++ 代码风格规范实战指南:命名、格式与注释规范全解析

【免费下载链接】hixlHIXL(Huawei Xfer Library)是一个灵活、高效的昇腾单边通信库,面向集群场景提供简单、可靠、高效的点对点数据传输能力。项目地址: https://gitcode.com/cann/hixl

本文以 docs/zh/contributions/coding_standards/cpp-style.md 为骨架,系统讲解 CANN 开源社区(含 hixl 仓库)的 C++ 代码风格规范。全文覆盖命名、格式、注释三大维度的 20 余条规则与建议,并结合仓库根目录 .clang-format 配置与 src/hixl 目录下的真实源码进行印证。读者完成本文后可对照规范自查代码、理解 clang-format 与风格规则的对应关系,并能顺利通过 docs/zh/contributions/precommit_guide.md 描述的 pre-commit 代码格式化与合规检查。

规范背景与适用范围

CANN 的 C++ 代码风格规范以 Google C++ Style Guide 为基础,参考 MindSpore 社区与华为通用编码规范,并结合业界共识整理而成。参与 CANN 开源社区项目(包括本 hixl 仓库)的开发者,首先需要遵循本规范内容,其余场景遵循 Google C++ Style Guide。若对规则有异议,可提交 issue 说明理由,经 CANN 运营团队评审通过后可接纳并修改生效。

规范的适用对象是CANN 相关开源仓的代码风格检视,即所有新提交的 C++ 代码都应满足下表所列的规则。规范按「规则(Rule)」与「建议(Suggestion)」区分强制力:规则类条目(如 1.1、2.2、2.4)属于必须遵守的硬性要求;建议类条目(如 2.1、3.4、3.5)属于推荐实践,优先级略低但同样应当遵循。

规范总览

规范编号规范名称类别严重级别
1.1C++文件使用小写+下划线命名命名
1.2函数命名使用大驼峰风格命名
1.3类型命名采用大驼峰风格命名
1.4变量命名采用小写下划线(snake_case)风格命名
1.5宏、枚举值采用全大写下划线连接命名
1.6编译期常量采用 k 前缀大驼峰命名
2.1行宽不超过 120 个字符格式
2.2使用空格缩进,每次 2 个空格格式
2.3&*跟随变量名格式
2.4if 语句必须使用大括号格式
2.5for/while 循环必须使用大括号格式
2.6表达式换行运算符放行末格式
2.7使用附着式(Attach)大括号风格格式
2.8多个变量定义不允许写在一行格式
2.9合理安排空行,保持代码紧凑格式
3.1文件头注释包含版权声明注释
3.2右置注释使用//,注释与代码间有空格注释
3.3禁止使用 TODO/TBD/FIXME 注释注释
3.4不要写空有格式的函数头注释注释
3.5不用的代码直接删除,不要注释掉注释

1. 命名规范

命名风格基础

规范中涉及两种基本命名风格:

  • 驼峰风格(CamelCase):大小写字母混用,单词连在一起,不同单词间通过单词首字母大写来分开。按连接后首字母是否大写,又分为大驼峰(UpperCamelCase)和小驼峰(lowerCamelCase)。
  • 小写下划线风格(snake_case):单词全部小写,单词间用下划线连接,如table_name

各类符号与命名风格的对应关系如下表:

类型命名风格
类类型,结构体类型,枚举类型,联合体类型等类型定义,作用域名称大驼峰
函数(包括全局函数,作用域函数,成员函数)大驼峰
编译期常量(constexpr,或以字面量/编译期常量表达式初始化的 const,含各作用域)k 前缀大驼峰
全局变量(包括全局和命名空间域下的变量,类静态变量),局部变量,函数参数,类、结构体和联合体中的成员变量小写下划线(snake_case)
宏,枚举值,goto 标签全大写,下划线分割

这里有两点需要特别注意:

  1. 常量的判定标准:上表中的「常量」指值在编译期即可确定的 const/constexpr 基本数据类型、枚举、字符串类型的变量(constexpr,或以字面量/编译期常量表达式初始化的 const;含各作用域),不包括数组和其他类型变量;以函数调用或运行期数据初始化的 const 变量不属于常量,按普通变量命名。
  2. 变量的判定标准:除常量定义以外的其他变量,均使用小写下划线(snake_case)风格。

规则 1.1 C++ 文件使用小写+下划线命名

C++ 源文件默认以.cpp结尾,头文件以.h结尾。业界还存在其他后缀表示方式:

  • 头文件:.hh.hpp.hxx
  • cpp 文件:.cc.cxx.c

如果当前项目组已经使用了某种特定后缀(例如部分历史工程使用.cc),可以继续使用,但必须保持风格统一。本规范默认使用.h.cpp作为后缀。

在 hixl 仓库中可以看到两种后缀并存但各自统一的情况:src目录下的核心实现以.cc/.h为主(如 src/hixl/cs/endpoint.cc、src/hixl/common/hixl_log.h),测试目录同样延续.cc(如 tests/cpp/hixl/common/thread_pool_ut.cc);而 include 目录对外发布的头文件统一使用.h。这正体现了「风格统一」的原则——同一仓库内不混用多种后缀。

规则 1.2 函数命名统一使用大驼峰风格

函数(全局函数、作用域函数、成员函数)统一使用大驼峰命名,一般采用动词或动宾结构

class List { public: void AddElement(const Element &element); Element GetElement(const unsigned int index) const; bool IsEmpty() const; }; namespace Utils { void DeleteUser(); }

一个重要的例外是:实现标准库、第三方库或序列化框架要求的接口契约函数(如lock/try_lock/unlockto_json/from_json等)可保留接口要求的原名,不受本规则约束。

在 hixl 源码中,函数命名严格遵守大驼峰:如 src/hixl/cs/endpoint.cc 中的InitQueueDepthBuildChannelNameInitChannelDescIsDefaultHostVaMappingEnabled等,均为「动词/动宾」或「Is 开头的布尔查询」结构。

规则 1.3 类型命名采用大驼峰命名风格

所有类型命名——类、结构体、联合体、类型别名(using/typedef)、枚举——使用相同的大驼峰约定:

// classes, structs and unions class UrlTable { ... struct UrlTableProperties { ... union Packet { ... // type aliases using PropertiesMap = std::map<std::string, UrlTableProperties *>; // enums enum UrlTableErrors { ...

命名空间也建议使用大驼峰:

// namespace namespace FileUtils {}

hixl 仓库中大量类型命名遵循此规则,例如 src/hixl/common/hixl_inner_types.h 中的EndpointDescChannelDescHcommChannelDesc,以及 include/hixl/hixl_types.h 中对外暴露的CommProtocolChannelType等枚举类型。

规则 1.4 通用变量命名采用小写下划线(snake_case)

全局变量、函数形参、局部变量、成员变量均采用 snake_case:

std::string table_name; // Good: 推荐此风格 std::string tableName; // Bad: 禁止小驼峰风格 std::string tablename; // Bad: 禁止无单词分隔的风格 std::string path; // Good: 只有一个单词时,snake_case 为全小写

全局变量应增加g_前缀,静态变量命名不需要加特殊前缀。加前缀的目的是在视觉上突出全局变量,促使开发人员对这些易出问题的变量使用更加谨慎:

  • 全局静态变量命名与全局变量相同;
  • 函数内的静态变量命名与普通局部变量相同;
  • 类的静态成员变量和普通成员变量相同。
int g_active_connect_count; void Func() { static int packet_count = 0; ... }

类的成员变量命名以 snake_case 加后下划线组成;struct 纯数据结构的公共成员可不加后下划线,同一结构内保持一致:

class Foo { private: std::string file_name_; // 类成员:添加 _ 后缀区分 }; struct Point { int x; // struct 纯数据成员:可不加后缀 int y; };

hixl 源码是这一规则的标准范例:如 src/hixl/cs/endpoint.cc 中的局部变量is_clientdata_depthchannel_nameclient_epserver_ep均采用 snake_case;成员变量加_后缀的模式在 src/hixl/cs/channel.h、src/hixl/fabric_mem/fabric_mem_slot_pool.h 等头文件中大量可见。

规则 1.5 宏、枚举值采用全大写,下划线连接

宏、枚举值、goto 标签使用全大写 + 下划线连接:

#define MAX(a, b) (((a) < (b)) ? (b) : (a)) // 仅对宏命名举例,并不推荐用宏实现此类功能 enum TintColor { // 注意,枚举类型名用大驼峰,其下面的取值是全大写,下划线相连 RED, DARK_RED, GREEN, LIGHT_GREEN };

注意示例中的细节:枚举类型名TintColor用大驼峰,而枚举值DARK_RED用全大写 + 下划线。hixl 中的日志宏即遵循此规范,如 src/hixl/common/hixl_log.h 中的HIXL_LOGEHIXL_LOGWHIXL_LOGIHIXL_LOGDHIXL_EVENTHIXL_RECORD,以及各类检查宏HIXL_CHK_STATUS_RETHIXL_CHK_ACL_RETHIXL_CHK_HCCL_RET等。

规则 1.6 编译期常量采用 k 前缀大驼峰命名

编译期常量(constexpr,或以字面量/编译期常量表达式初始化的 const)不论作用域(全局、命名空间、类静态成员、函数局部),统一使用k 前缀大驼峰命名:

int Func(...) { constexpr unsigned int kBufferSize = 100; // 编译期常量:k 前缀大驼峰 char *buffer = new char[kBufferSize]; const int saved_errno = errno; // 运行期初始化的 const:按普通变量 snake_case 命名 ... } namespace Utils { constexpr unsigned int kDefaultFileSizeKb = 200; // 全局编译期常量 }

判定要点

  • 函数调用或运行期数据初始化的 const 变量不是常量,按普通变量命名(规则 1.4);
  • 类的非静态const 成员变量按成员变量命名规则(snake_case 加后下划线)命名。

hixl 源码中 k 前缀常量随处可见,例如 src/hixl/cs/endpoint.cc 中的kRoceQueueNumkMinTransportQueueDepth,src/hixl/common/hixl_inner_types.h 中的kProtocolRocekProtocolUboekRdmaTrafficClasskRdmaServiceLevelkDefaultSplitBatchSizekMaxMultiWorkerNum等。可以看到常量覆盖了数值(uint32_tuint8_t)与字符串(const char *)两类,符合规范中「基本数据类型、枚举、字符串类型的变量」的界定。

2. 格式规范

建议 2.1 行宽不超过 120 个字符

建议每行字符数不超过120个。若超过 120 个字符,请选择合理的方式换行。允许的例外包括:

  • 一行注释包含超过 120 个字符的命令或 URL,可保持一行,方便复制、粘贴和通过 grep 查找;
  • 包含长路径的#include语句可以超出 120 个字符,但应尽量避免;
  • 编译预处理中的 error 信息可以超出一行(保持一行便于阅读和理解)。
#ifndef XXX_YYY_ZZZ #error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h, because xxxxxxxxxxxxxxxxxxxxxxxxxxxxx #endif

这一限制与仓库根目录 .clang-format 中的ColumnLimit: 120完全一致,clang-format 会按此配置自动折行。

规则 2.2 使用空格进行缩进,每次缩进 2 个空格

只允许使用空格(space)缩进,每次缩进为2 个空格,不允许使用 Tab 符。当前几乎所有 IDE 都支持将 Tab 自动扩展为 2 空格输入,建议配置 IDE 开启该选项。另外,命名空间内部不缩进,与 .clang-format 的NamespaceIndentation: None一致。

对应到 .clang-format:IndentWidth: 2TabWidth: 2UseTab: Never。观察 src/hixl/cs/endpoint.cc 的代码,namespace hixl {内部顶格书写,函数体内容缩进 2 空格,控制流嵌套逐级加 2 空格,正是该规则的落地效果。

规则 2.3&*跟随变量名

声明指针、引用变量或参数时,&*跟随变量名,另外一边留空格:

char *c; const std::string &str;

这与 .clang-format 的PointerAlignment: Right对应。规则 1.2 示例中的const Element &element即按此写法。

规则 2.4 if 语句必须使用大括号

即便只有一条语句,if 语句也必须使用大括号。理由如下:

  • 代码逻辑直观、易读;
  • 在已有条件语句代码上增加新代码时不容易出错;
  • 对于在 if 语句中使用函数式宏时,有大括号保护不易出错(如果宏定义时遗漏了大括号)。
// 即使if分支代码只有一行,也必须使用大括号 if (cond) { single line code; }

注意:clang-format 不会自动为单条语句补全大括号(.clang-format 中AllowShortIfStatementsOnASingleLine: true允许单行 if 形式),本规则依赖代码检视或 clang-tidy(readability-braces-around-statements)保证。

规则 2.5 for/while 等循环语句必须使用大括号

与条件表达式类似,for/while 循环语句必须加上大括号,即便循环体是空的,或循环语句只有一条

for (int i = 0; i < some_range; i++) { // Good: 使用了大括号 DoSomething(); }
while (condition) { // Good:循环体是空,也使用大括号 }

注意:同规则 2.4,clang-format 不会自动为循环语句补全大括号(.clang-format 中AllowShortLoopsOnASingleLine: true允许单行循环形式),需依靠代码检视保证。

规则 2.6 表达式换行要保持一致性,运算符放行末

较长的表达式不满足行宽要求时,需要在适当的地方换行。一般在较低优先级运算符或连接符后面截断,运算符或连接符放在行末,表示「未结束,后续还有」:

// 假设下面第一行已经不满足行宽要求 if ((current_value > threshold) && // Good:换行后,逻辑操作符放在行尾 some_condition) { DoSomething(); ... } int result = really_long_variable_name1 + // Good really_long_variable_name2;

表达式换行后,续行与首个操作数对齐(与 .clang-format 的AlignOperands: true一致):

int sum = long_variable_name1 + long_variable_name2 + long_variable_name3 + long_variable_name4 + long_variable_name5 + long_variable_name6; // Good: 续行与首个操作数对齐

规则 2.7 使用附着式(Attach)大括号风格

附着式(Attach)风格:所有左大括号(包括函数、类、结构体、控制语句等)跟随语句放行末,前置 1 空格。右大括号独占一行,除非后面跟着同一语句的剩余部分,如 do 语句中的 while,或者 if 语句的 else/else if,或者逗号、分号。

struct MyType { // 跟随语句放行末,前置1空格 ... }; int Foo(int a) { // 函数左大括号同样跟随语句放行末 if (...) { ... } else { ... } }

推荐这种风格的理由:

  • 代码更紧凑;
  • 相比另起一行,放行末使代码阅读节奏感上更连续;
  • 符合后来语言的习惯,符合业界主流习惯;
  • 与仓库 .clang-format 配置(BreakBeforeBraces: Attach)一致,提交前执行 clang-format 不会产生额外 diff;
  • 现代 IDE 都具有代码缩进对齐显示的辅助功能,大括号放在行尾并不会对缩进和范围产生理解上的影响。

对于空函数体,可以将大括号放在同一行:

class MyClass { public: MyClass() : value_(0) {} private: int value_; };

查看 src/hixl/cs/endpoint.cc 可以直观看到附着式风格的统一应用:bool IsUrmaProtocol(...)左大括号紧跟行末,if/elsefor等控制结构同样如此,} else if} else的右大括号与后续关键字同行。

规则 2.8 多个变量定义和赋值语句不允许写在一行

每行只写一个变量初始化语句,更容易阅读和理解。

规则 2.9 合理安排空行,保持代码紧凑

减少不必要的空行,可以显示更多代码,方便阅读。建议遵守以下规则:

  • 根据上下内容的相关程度,合理安排空行;
  • 函数内部、类型定义内部、宏内部、初始化表达式内部,不使用连续空行;
  • 不使用连续空行,最多保留 1 个(与 .clang-format 的MaxEmptyLinesToKeep: 1一致);
  • 大括号内的代码块行首之前和行尾之后不要加空行,但 namespace 的大括号内不作要求。
int Foo() { ... } int Bar() { // Bad:最多保留 1 个连续空行。 ... } if (...) { // Bad:大括号内的代码块行首不要加入空行 ... // Bad:大括号内的代码块行尾不要加入空行 } int Foo(...) { // Bad:函数体内行首不要加空行 ... }

3. 注释规范

一般的,尽量通过清晰的架构逻辑、好的符号命名来提高代码可读性;需要的时候,才辅以注释说明。注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,按需注释。注释内容要简洁、明了、无二义性,信息全面且不冗余。

在 C++ 代码中,使用/* *///都是可以的。按注释的目的和位置,注释可分为不同的类型,如文件头注释、函数头注释、代码注释等;同一类型的注释应该保持统一的风格

规则 3.1 文件头注释包含版权声明

每个源文件头部必须包含版权声明,示例如下:

/** * Copyright (c) 2026 Huawei Technologies Co., Ltd. * This program is free software, you can redistribute it and/or modify it under the terms and conditions of * CANN Open Software License Agreement Version 2.0 (the "License"). * Please refer to the License for details. You may not use this file except in compliance with the License. * 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 FITNESS FOR A PARTICULAR PURPOSE. * See LICENSE in the root of the software repository for the full text of the License. */

关于版权年份的写法:

  • 2026 年新建的文件,应为Copyright (c) 2026 Huawei Technologies Co., Ltd.
  • 2025 年新建、2026 年修改的文件,应为Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.

hixl 仓库的所有源文件均带此版权头,例如 src/hixl/common/hixl_log.h 与 scripts/check_log_spec.py 的前 9 行。值得注意的是,该文件头是合规检查(OAT 扫描)的硬性要求——docs/zh/contributions/precommit_guide.md 中明确,许可证头缺失或错误会阻止提交,必须修复后才能通过 pre-commit 的oat-check

规则 3.2 右置注释使用//,注释与代码间有空格

代码注释应置于对应代码的上方或右边;注释符与注释内容之间要有 1 个空格;右置注释与前面代码至少 1 个空格;右置注释使用//,而不是/**/;置于代码上方的注释使用///* */均可:

// this is multi- // line comment int foo; // this single-line comment

.clang-format 中的SpacesBeforeTrailingComments: 2保证右置注释前至少保留 2 个空格。源码中该规则的典型应用如 src/hixl/common/hixl_inner_types.h 中的constexpr uint8_t kRdmaTrafficClass = 132; // RDMA网卡的traffic class 默认值

规则 3.3 禁止使用 TODO/TBD/FIXME 注释

代码中禁止使用 TODO/TBD/FIXME 等注释,建议提 issue 跟踪待办事项。这类残留注释会在 pre-commit 流程中被扫描识别,也容易在代码评审中被拦截。

建议 3.4 不要写空有格式的函数头注释

并不是所有函数都需要函数头注释,函数尽量通过函数名自注释,按需写函数头注释;函数原型无法表达的、却又希望读者知道的信息,才需要加函数头注释辅助说明。函数头注释内容可选,但不限于:功能说明、返回值、性能约束、用法、内存约定、算法实现、可重入的要求等。

好的例子——说明了函数语义与调用者必须知道的内存约定:

/* * 返回实际写入的字节数,-1表示写入失败 * 注意,内存 buf 由调用者负责释放 */ int WriteString(const char *buf, int len);

坏的例子——空有格式没内容,函数名信息冗余,关键信息缺失:

/* * 函数名:WriteString * 功能:写入字符串 * 参数: * 返回值: */ int WriteString(const char *buf, int len);

上述坏例的问题在于:

  • 参数、返回值空有格式没内容;
  • 函数名信息冗余;
  • 关键的buf由谁释放没有说清楚。

建议 3.5 不用的代码段直接删除,不要注释掉

被注释掉的代码无法被正常维护;当企图恢复使用这段代码时,极有可能引入易被忽略的缺陷。正确的做法是:不需要的代码直接删除掉;若再需要时,考虑移植或重写这段代码。

工具链支撑:clang-format 与 pre-commit 如何保障风格落地

风格规范不只是纸面文档,仓库提供了配套工具保证落地:

.clang-format配置对照

仓库根目录 .clang-format 基于 Google 风格,将上述大部分格式规则固化为机器可执行的配置,关键项与规范的对应关系如下:

规范条目.clang-format配置项配置值
建议 2.1 行宽 120ColumnLimit120
规则 2.2 缩进 2 空格IndentWidth/TabWidth/UseTab2/2/Never
规则 2.2 命名空间不缩进NamespaceIndentationNone
规则 2.3 指针引用靠右PointerAlignmentRight
规则 2.6 续行对齐操作数AlignOperandstrue
规则 2.7 附着式大括号BreakBeforeBracesAttach
规则 2.9 最多保留 1 个空行MaxEmptyLinesToKeep1

同时,该配置也保留了与规范存在张力的选项:AllowShortIfStatementsOnASingleLine: trueAllowShortLoopsOnASingleLine: true允许单行 if/循环形式,因此规则 2.4、2.5 的「必须加花括号」无法由 clang-format 自动保证,需要依赖代码检视或 clang-tidy(readability-braces-around-statements)补充。

pre-commit 中的格式化与检查

docs/zh/contributions/precommit_guide.md 说明了 pre-commit 的完整用法:安装 pre-commit 后,每次git commit前会自动执行代码格式化(基于 clang-format 镜像)、拼写检查(codespell/typos)与 OAT 合规扫描。格式化失败时会提示修改,而真正的合规性问题(二进制文件、许可证头缺失/错误)会阻止提交

本地也可以手动运行检查:

# 安装 pre-commit 并安装 git hooks pip install pre-commit pre-commit install # 手动运行 OAT 合规检查 pre-commit run oat-check

实战自查清单

结合本文全部规则,提交 C++ 代码前可对照以下清单快速自查:

  1. 命名:文件名为小写+下划线(.h/.cpp统一后缀);函数大驼峰且动词/动宾;类型(类/结构体/联合体/别名/枚举)大驼峰;普通变量 snake_case,全局变量加g_,类成员加_后缀;宏与枚举值全大写下划线;编译期常量k前缀大驼峰,运行期 const 按普通变量。
  2. 格式:行宽 ≤ 120;2 空格缩进且不用 Tab;&/*跟随变量名;if/for/while 一律加大括号;表达式换行运算符在行末且续行对齐操作数;附着式大括号(空函数体可同行);一行一个变量定义;连续空行最多 1 个,代码块首尾不加空行。
  3. 注释:文件头带版权声明且年份正确;右置注释用//且与代码间有空格;禁止 TODO/TBD/FIXME;函数头注释按需、有实质内容(说明语义与内存约定等);不用的代码直接删除而非注释掉。
  4. 工具:提交前运行 clang-format 对齐 .clang-format;依赖 pre-commit 完成格式化、拼写与 OAT 合规检查,避免二进制文件与许可证头问题阻塞提交。

hixl 仓库中 src/hixl/cs/endpoint.cc、src/hixl/common/hixl_log.h、src/hixl/common/hixl_inner_types.h 等文件是上述规范的完整落地样例,可以作为新代码的风格参考范本。

【免费下载链接】hixlHIXL(Huawei Xfer Library)是一个灵活、高效的昇腾单边通信库,面向集群场景提供简单、可靠、高效的点对点数据传输能力。项目地址: https://gitcode.com/cann/hixl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询