注释风格
代码注释依据其面向的读者不同,可以分为接口注释与实现注释。
- 接口注释,重点在于描述业务功能、调用方式、注意事项等,为了使调用者无需了解实现细节的基础上依然能快速、正确的使用接口。接口注释
==
需求文档+接口文档。 - 实现注释,重点在于描述实现算法、设计结构、特殊处理等,为了使用模块维护者可以清晰的了解代码,支撑其需求开发以及问题修复。
通过不断的反问:这段代码是为了什么目的而写?代码的读者会是谁?来区分以上两者。
注:并非头文件中声明的内容都需要接口注释,得按实际用途划分。例如,对于一个代码量较大的Phase
,可能将其拆分为多个头文件和源文件,但这些头文件中的内容只服务于当前Pahse
,其他模块无需感知,则其应该使用实现注释。若该Phase
属于分析Phase
,其分析结果AnalysisResult
需要传递给其他优化,则其AnalysisResult
适用于接口注释。
此处,接口注释使用文档注释风格,实现注释使用代码注释风格。采用两种风格的优点是可以更加明确的区分对外接口和实现接口,并可以生成文档;缺点是新人在使用注释时,需要更多的考量。
文档注释风格
文档注释采用较为通用的doxygen
文档注释,注释统一采用C++
风格的///
注释方式。以下介绍的注释项为当前已经采纳的注释风格,doxygen
中有更多的文档注释能力,如group
等,暂时不接入,以保持当前代码的简洁。
文件注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| /*
* Copyright (c) [2019] Huawei Technologies Co.,Ltd.All rights reserved.
*
* OpenArkCompiler is licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
*
* http://license.coscl.org.cn/MulanPSL
*
* 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 v1 for more details.
*//**
* \file
* This is the description of the file.
*/
|
文件注释是所有文档注释中唯一使用JAVADOC
风格而非C++
风格的注释方式,主要为了和版权声明的头注释保持一致,如此经过一定考量后的注释方式。借鉴于doxygen
官方文档,将版权声明注释尾的*/
与文档注释/**
合并到一行,以保持文件头注释的美观性。
1
2
| * \file
* This is the description of the file.
|
文件注释以\file
作为关键字标记,文件注释新起一行。
文件注释并非用于描述具体某个类或接口的功能,而是从整体的设计层面,描述当前文件中类族或接口集合设计的目的和用途,表明其在整个模块中的关系,乃至提供解决问题的代码示例。在仅包含单个类的头文件中,文件注释内容与类注释往往很难界定,所以在无法明确区分时,此处武断的推荐将注释放在类上。
大多数情况下,文件注释均可以省略。
class
/struct
/enum
/union
/using
的类型注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| /// \brief
/// Brief of definition `Number`.
///
/// Detail of definiton `Number`, a blank line is needed upper.
enum Number {};
/// \brief
/// Brief of definition `String`
///
/// Detail of definition `String`
class String {
/// \brief
/// Brief of size_type.
///
/// Detail of size_type.
using size_type = size_t;
};
|
类型注释以\brief
作为概要关键字标记,注释内容另起一行。概要信息后空一行,接详情信息的注释。其中,概要信息将随着元素展示,而详情信息将在所有概要的注释后展开。
缺省\brief
关键字时,默认为详情信息的注释。
类型注释描述当前类的设计目的和用途,以及和其他类之间的关系,包括类的安全性、不应被使用的场景、风险等。
函数/成员函数/函数宏的函数注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| /// \brief
/// Brief of macro.
///
/// Detail of macro.
#define MACRO_LOG do {} while(0);
class String {
public:
/// \brief
/// Brief of explaination for implict construct.
///
/// \param str The life will be transfered.
String(char *str)
: str(str) {}
/// \brief
/// Brief of `Get`.
///
/// Detail of `Get`.
/// \return Never delete.
/// \attention Never delete.
char *Get() {
return str;
}
};
/// \brief
/// Brief of `Func`.
///
/// Detail of `Func`.
void Func() {
}
|
函数注释在描述概要信息和详情信息与类型注释相同,但其针对参数和返回值添加了几个新的关键字。
\param {name} {description}
阐明参数的用途。
更推荐由形参名/类型+函数名自注释,且若函数有多个参数时,只针对需要注释的参数进行注释,无需写全参数列表。
注:[in]|[out]|[in, out]
在C++
中必要性不高,结合规范优先传递引用而非指针,多数场景下,满足:[in]=传值/const
引用/底层const
指针;[out]/[in, out]=非const
引用/非底层const
指针。
\return {description}
阐明返回值的用途。
更推荐由返回值类型+函数名自注释。
\attention {description}
告知风险与注意事项,但亦要避免对语言或领域内的基础性或常识性概念的描述。
枚举成员/全局常量/全局变量的数据注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| enum Num {
kZero, ///< This is 0
kOne, ///< This is 1
kTwo, ///< This is 2
kOther ///< This is others
};
constexpr int32_t kConstValue0 = 0; ///< \brief This is const 0 brief, but is not suggested.
/// This is const 1 brief.
constexpr int32_t kConstValue1 = 1; ///< This is const 1 detail.
///< Next of the detail.
/// Brief for global, thread safety is a must.
int32_t globalValue = 0; ///< Deatil is a must, as golbal values need to be avoided.
|
可以看到///
注释均在内容的上方进行注释,本节新增一种注释格式///<
,用于对枚举成员/全局常量/全局变量的右侧(后方)进行注释。
数据注释在上置注释的为概要信息,在右置注释的为详情信息。至于为什么如此设计?个人理解为数据对象无逻辑,其概要信息完全可以且必须由其命名承载,若出现特殊信息,则其也并不是简单的概要可以描述的,当使用详情信息做单独介绍。即不推荐对数据使用上置注释,优选命名自注释,特殊场景使用右置注释进行详情补充。
全局变量并不推荐使用,尤其是在新增代码中当明确禁止,但存量代码中依然存在。
常量宏已被const
/consexpr
定义的常量所取代,所以此处不支持文档注释。注意:存在部分常量宏用于控制编译。
成员变量必须为私有,私有代码不应包含文档注释。
格式上,右置注释亦可以写多行,规定统一保持列对齐,如kConstValue1
。
样例注释
针对注释中提供代码片段用作样例,考虑到与文档的一致性,方便拷贝与同步,此处决定使用markdown
的代码格式(doxygen
支持markdown
语法)。
1
2
3
4
5
6
| * ```c++
* Num num = kZero;
* Num num1 = kZero;
* ```
/// For single `num`, `Num`, `kZero`.
|
完整样例
可用doxygen
生产查看效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
| /*
* Copyright (c) [2019] Huawei Technologies Co.,Ltd.All rights reserved.
*
* OpenArkCompiler is licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
*
* http://license.coscl.org.cn/MulanPSL
*
* 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 v1 for more details.
*//**
* \file
* This is the description of the file.
* ```c++
* Num num = kZero;
* Num num1 = kZero;
* ```
*/
#include <string>
namespace maple {
constexpr int32_t kConstValue0 = 0; ///< \brief This is const 0 brief.
/// This is const 1 brief.
constexpr int32_t kConstValue1 = 1; ///< This is const 1 detail.
///< Next of the detail.
/// \brief
/// Brief of macro.
///
/// Detail of macro.
#define MACRO_LOG \
do {} while(0);
/// \brief
/// Brief of definition `Num`.
///
/// Detail of definiton `Num`, a blank line is needed upper.
enum Num {
kZero, ///< This is 0
kOne, ///< This is 1
kTwo, ///< This is 2
kOther ///< This is others
};
/// \brief
/// Brief of definition `String`
///
/// Detail of definition `String`
class String {
public:
/// \brief
/// Brief of size_type.
///
/// Detail of size_type.
using size_type = size_t;
/// \brief
/// Brief of explaination for implict construct.
///
/// \param str The life will be transfered.
String(char *str)
: str(str) {}
/// \brief
/// Brief of `Get`.
///
/// Detail of `Get`.
/// \return Never delete.
/// \attention Never delete.
char *Get() {
return str;
}
protected:
/// \brief
/// Brief of `Size`.
///
/// Detail of `Size`.
size_t Size() const {
return len;
}
private:
void Reset(char *other) {
str = other;
}
private:
char *str;
size_t len;
};
/// Brief for global, thread safety is a must.
int32_t globalValue = 0; ///< Deatil is a must, as golbal values need to be avoided.
/// \brief
/// Brief of `Func`.
///
/// Detail of `Func`.
void Func() {
}
} // maple
|
代码注释风格
代码注释统一采用C++
风格的//
注释(文件头注释除外),除非其完全由C
实现并被C
程序调用。
文件注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| /*
* Copyright (c) [2019] Huawei Technologies Co.,Ltd.All rights reserved.
*
* OpenArkCompiler is licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
*
* http://license.coscl.org.cn/MulanPSL
*
* 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 v1 for more details.
*//*
* This is the description of the file.
*/
|
相较文档注释,有两点需要注意:
*//**
变更为*//*
关闭doxygen
注释,并移除\file
关键字。- 修改
Copyright
时间戳
1
| * Copyright (c) [2019] Huawei Technologies Co.,Ltd.All rights reserved.
|
其中[2019]
标记的为初始开源年份到最近一次修改的年份。即2019年开源后未修改,[2019]
;2019年开源,且2020年又进行了修改,[2019-2020]
。
###class
/struct
/enum
/union
/using
的类型注释
1
2
3
4
5
6
7
8
| // Document of `Number`
enum Number {};
// Document of `String`
class String {
// Document of size_type.
using size_type = size_t;
};
|
勿使用文档注释风格中的\brief
关键字。
函数/成员函数(包括private
)/函数宏的函数注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // Document of macro.
#define MACRO_LOG do {} while(0);
class String {
public:
// Document of construct
String(char *str)
: str(str) {}
// Document of `Get`
char *Get() {
return str;
}
};
// Document of `Func`
void Func() {
}
|
勿使用文档注释风格中的\param
、\return
、\attention
关键字。
枚举成员/成员变量/全局常量/全局变量的数据注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| enum Num {
kZero, // This is 0
kOne, // This is 1
kTwo, // This is 2
kOther // This is others
};
// Document of `kConstValue0`.
constexpr int32_t kConstValue0 = 0;
constexpr int32_t kConstValue1 = 1; // Document of `kConstValue0`.
// Document of `globalValue`.
int32_t globalValue = 0;
class String {
private:
// Document of `str`.
char *str;
size_t len; // Document of `len`.
};
|
此处相对文档注释亦有三点需要注意:
- 右置注释当简单精炼,不可出现换行,如果右置注释过长,应使用上置注释。
- 全局变量只支持上置注释,不支持右置注释。
- 枚举成员/成员变量/全局常量优选右置注释,当且仅当右置注释过长时才选用上置注释。
样例注释
同文档注释中的样例注释,采用doxygen
风格。
块内注释
此处的代码块指的是函数内部的代码实现,这类代码统一使用上置注释,除非上置注释会让代码可读性下降,且右置注释可以达到更好的效果,此时才使用右置注释。
注释原则
在编译系统中,注释并不会对最终二进制的生成产生任何影响,但程序员大部分时间都是在阅读与理解代码,注释的准确、美观都将会影响到程序的开发。所以此处要求写注释当同写代码一样重视,最期望的结果是代码+注释=美文,注释是对代码无法表达或难以表达的内容补充。
通用原则
- 无论是文档注释亦或是代码注释,必须添加注释这类指标性的注释添加方式是误导性的,注释的添加一定要明确阅读的角色是谁,注释的目的是什么,从而明确是否需要注释,以及注释的内容。重点强调代码自注释,但亦不可死鸭子嘴硬,未能自注释时故意不写注释。
- 注释应契合其领域,认为其有一定的
C++
知识储备、编译器知识储备,而非傻瓜式的教程。 - 不应重复函数、类等等的声明已经明确表达的内容。
- 相同的注释不应多处出现,如注释字面值常量,当提取全局常量;注释同系列的不同字面值常量,当统一提取全局常量;声明和实现处注释相类似的内容应当只保留一处。
- 不应注释实现部分已一目了然的内容。
摘自LLVM注释规范:
Aim to describe what the code is trying to do and why, not how it does it at a micro level.
【译】着重描述代码在做什么、为什么这么做,而不是微观层面的代码是怎么实现的。
注释应为符合英语语法的语句或短语。例如首字母大写,结束使用.
等。
注释符与注释内容间要有1空格;右置注释与前面代码要有1空格,或保持与上下文的右置注释统一列对齐(此处禁用了tab对齐,一来2格缩进与1个空格的长度不明显,未能有明晰的效果,而来重命名会导致tab对齐失效)。
禁止场景1
来自Google编码规范,函数内部对于字面值实参的补充注释禁用。
1
2
3
4
5
| ProductOptions options;
options.set_precision_decimals(7);
options.set_use_cache(ProductOptions::kDontUseCache);
const DecimalNumber product =
CalculateProduct(values, options, /*completion_callback=*/nullptr);
|
- IDE如Clion自动回补充形参名
- 通常会使用有意义的常量名、更有意义的函数名、或重构成员函数的设计等方式来让意图变得明晰。
- 这类注释往往是不得已而为之,大部分场景下,恰恰证明此函数设计的缺陷,当重构之(重载两个函数、修改业务逻辑等),而非注释之。
禁止场景2
禁止在代码的局部对整段代码进行注释。
1
2
3
| for (auto &element : data) { // Document for the loop.
}
|
此处对于for
的行为注释,应使用上置注释,但却在{}
内部注释循环行为,应禁止。