代码风格和语言版本
为了实现外观一致的产品,我们将遵循外部(如果可能)为每种语言定义的样式指南。 对于其他事项,例如包布局或文档布局,我们需要根据当前使用的流行样式制定自己的指南。
此外,只要有可能,开发人员就应使用集成工具来检查他们的编辑器是否遵循了这些指南。 例如,每个人都应该在编辑器中内置一个 PEP8 检查器,以减少与样式相关的审核迭代。
此外,如果可能,包应该将样式检查作为其单元测试的一部分,以帮助自动检测样式问题(请参阅 ament_lint_auto)。
C
标准
我们将以 C99 为目标。
风格
我们将使用 Python 的 PEP7 作为我们的 C 风格指南,并进行了一些修改和添加:
我们将以 C99 为目标,因为我们不需要支持 C89(正如 PEP7 所建议的那样)
理由:除其他事项外,它允许我们使用
//
和/* */
样式的注释理由:C99 现在几乎无处不在
允许使用 C++ 样式的
//
注释
*(可选)始终将文字放在比较运算符的左侧,例如``0 == ret`` 而不是 ret == 0
理由:
ret == 0
很容易意外变成ret = 0
可选,因为当使用 ``-Wall``(或等效项)时,现代编译器会在发生这种情况时发出警告
以下所有修改仅适用于我们不编写 Python 模块的情况:
不要将
Py_
用作所有内容的前缀而是使用 CamelCase 版本的包名称或其他适当的前缀
有关文档字符串的内容不适用
我们可以使用 pep7 python 模块进行样式检查。编辑器集成似乎很少,我们可能需要更详细地研究 C 的自动检查。
C++
标准
Rolling 针对 C++17。
样式
我们将使用 Google C++ 风格指南,并进行一些修改:
行长
我们的最大行长为 100 个字符。
文件扩展名
头文件应使用 .hpp 扩展名。
理由:允许工具确定文件的内容,C++ 或 C。
实现文件应使用 .cpp 扩展名。
理由:允许工具确定文件内容,C++ 或 C。
变量命名
对于全局变量,请使用小写字母,下划线前缀为
g_
理由:在整个项目中保持变量命名大小写一致
理由:一目了然地轻松判断变量的范围
跨语言的一致性
函数和方法命名
Google 风格指南说
CamelCase
,但 C++ std 库的snake_case
风格也是允许的理由:ROS 2 核心包目前使用
snake_case
原因:可能是历史疏忽,也可能是个人偏好,没有经过 linter 检查
不更改的原因:追溯更改会造成太大的破坏
其他考虑因素:
cpplint.py
不检查这种情况(除了使用其他方法很难执行)审查)
*snake_case
可使跨语言的一致性更高
* 具体指导:
对于现有项目,优先使用现有样式
对于新项目,两者都可以接受,但建议优先匹配相关的现有项目
最终决定始终由开发人员自行决定
函数指针、可调用类型等特殊情况可能需要改变规则
请注意,类仍应默认使用``CamelCase``
访问控制
放弃所有类成员必须是私有的,因此需要访问器
理由:这对用户 API 设计来说过于严格
我们应该优先使用私有成员,只在需要时才将其公开
在选择允许直接成员访问之前,我们应该考虑使用访问器
除了方便我们之外,我们应该有允许直接成员访问的充分理由
异常
允许异常
理由:这是一个新的代码库,因此遗留论点不适用于我们
理由:对于面向用户的 API,使用异常更符合 C++ 的习惯
应明确避免析构函数中的异常
如果我们打算用 C 包装生成的 API,我们应该考虑避免异常
理由:这将使用 C 包装更容易
理由:我们打算用 C 包装的代码中的大多数依赖项无论如何都不会使用异常
类似函数的对象
对Lambda 或
std::function
或std::bind
Boost
除非绝对需要,否则应避免使用 Boost。
注释和文档注释
使用
///
和/** */
注释用于 文档 目的,使用//
样式注释用于注释和一般注释类和函数注释应使用
///
和/** */
样式注释理由:建议在 C/C++ 中的 Doxygen 和 Sphinx 中使用它们
理由:混合使用
/* */
和//
方便注释包含注释的代码块类和函数内的代码工作原理描述或注释应使用
//
样式注释
指针语法对齐
使用
char * c;
而不是char* c;
或char *c;
,因为这种情况char* c, *d, *e;
类隐私关键字
嵌套模板
切勿在嵌套模板中添加空格
首选
set<list<string>>``(C++11 功能)而不是 ``set<list<string> >
或set< list<string> >
始终使用括号
始终在
if
、else
、do
、while
和for
后使用括号,即使正文只有一行。理由:由于在正文中使用宏,导致视觉歧义和复杂化的机会更少
开放括号与拥抱括号
对“function”、“class”、“enum”和“struct”定义使用左括号,但对“if”、“else”、“while”、“for”等使用紧挨括号…
例外:当“if”(或“while”等)条件足够长以致需要换行时,请使用左括号(即不要紧挨括号)。
当函数调用无法放在一行中时,请在左括号处换行(不在参数之间),并在下一行以 2 个空格的缩进开始。对于更多参数,请在后续行继续使用 2 个空格的缩进。 (请注意,Google 风格指南 在这一点上存在内部矛盾。)
同样适用于``if``(和``while`` 等)条件太长而无法在一行中容纳。
示例
这没问题:
int main(int argc, char **argv)
{
if (condition) {
return 0;
} else {
return 1;
}
}
if (this && that || both) {
...
}
// Long condition; open brace
if (
this && that || both && this && that || both && this && that || both && this && that)
{
...
}
// Short function call
call_func(foo, bar);
// Long function call; wrap at the open parenthesis
call_func(
foo, bar, foo, bar, foo, bar, foo, bar, foo, bar, foo, bar, foo, bar, foo, bar, foo, bar,
foo, bar, foo, bar, foo, bar, foo, bar, foo, bar, foo, bar, foo, bar, foo, bar, foo, bar);
// Very long function argument; separate it for readability
call_func(
bang,
fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo,
bar, bat);
这**不好**:
int main(int argc, char **argv) {
return 0;
}
if (this &&
that ||
both) {
...
}
使用开括号而不是过多的缩进,例如,为了区分构造函数代码和构造函数初始化列表
这是可以的:
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
Type par_name1, // 2 space indent
Type par_name2,
Type par_name3)
{
DoSomething(); // 2 space indent
...
}
MyClass::MyClass(int var)
: some_var_(var),
some_other_var_(var + 1)
{
...
DoSomething();
...
}
这**不**好,甚至很奇怪(谷歌的方式?):
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
Type par_name1, // 4 space indent
Type par_name2,
Type par_name3) {
DoSomething(); // 2 space indent
...
}
MyClass::MyClass(int var)
: some_var_(var), // 4 space indent
some_other_var_(var + 1) { // lined up
...
DoSomething();
...
}
Linters
我们结合使用 Google 的 cpplint.py 和 uncrustify 检查这些样式。
我们提供具有自定义配置的命令行工具:
一些格式化程序(例如 ament_uncrustify 和 ament_clang_format)支持 --reformat
选项以就地应用更改。
我们还运行其他工具来检测和消除尽可能多的警告。
以下是我们尝试在所有软件包上执行的其他操作的非详尽列表:
使用编译器标志,例如
-Wall -Wextra -Wpedantic
运行静态代码分析,例如
cppcheck
,我们已将其集成到 ament_cppcheck 中。
Python
版本
我们将以 Python 3 为开发目标。
风格
我们将使用 PEP8 指南 作为代码格式。
我们选择了以下更精确的规则,而 PEP 8 则留下了一些自由:
`我们允许每行最多 100 个字符(第五段)<https://www.python.org/dev/peps/pep-0008/#maximum-line-length>`_。
`只要不需要转义,我们选择单引号而不是双引号<https://www.python.org/dev/peps/pep-0008/#string-quotes>`_。
`我们更喜欢为连续行设置悬挂缩进<https://www.python.org/dev/peps/pep-0008/#indentation>`_。
应在单元测试和/或编辑器集成中使用诸如 (ament_)pycodestyle
Python 包之类的工具来检查 Python 代码样式。
linter 中使用的 pycodestyle 配置位于`此处<https://github.com/ament/ament_lint/blob/rolling/ament_pycodestyle/ament_pycodestyle/configuration/ament_pycodestyle.ini>`__。
与编辑器集成:
CMake
版本
我们将以 CMake 3.8 为目标。
风格
由于没有现有的 CMake 风格指南,我们将定义自己的风格指南:
使用小写命令名称(“find_package”,而不是“FIND_PACKAGE”)。
使用“snake_case”标识符(变量、函数、宏)。
使用空的“else()”和“end…()”命令。
(
‘s 前无空格。使用两个空格缩进,不要使用制表符。
多行宏调用的参数不要使用对齐缩进。仅使用两个空格。
优先使用带有
set(PARENT_SCOPE)
的函数而不是宏。使用宏时,在局部变量前加上
_
或合理的前缀。
Markdown / reStructured Text / docblocks
样式
以下格式化文本的规则旨在提高可读性和版本控制。
[.md,仅限 .rst] 每个部分标题前面应有一个空行,后面应有一个空行。
理由:在筛选文档时,它可以加快对结构的概述。
[仅限 .rst] 在 reStructured Text 中,标题应遵循 `Sphinx 样式指南中描述的层次结构<https://documentation-style-guide-sphinx.readthedocs.io/en/latest/style-guide.html#headings>`__:
带上划线的 ``#``(仅一次,用于文档标题)
带上划线的
*
=
-
^
"
理由:一致的层次结构有助于在筛选文档时快速了解嵌套级别。
[仅限 .md] 在 Markdown 中,标题应遵循 Markdown 语法文档 中描述的 ATX 样式
ATX 样式标题在行首使用 1-6 个井号 (
#
) 来表示标题级别 1-6。应在井号和标题之间使用空格(例如“# Heading 1”),以便更容易在视觉上将它们区分开。
ATX 样式偏好的理由来自 Google Markdown 样式指南
理由:ATX 样式标题更易于搜索和维护,并使前两个标题级别与其他级别保持一致。
[任意] 每个句子都必须以新行开始。
理由:对于较长的段落,开头的单个更改会使差异变得不可读,因为它会延续到整个段落。
[任意] 每个句子都可以选择换行以保持每行简短。
[任意] 行尾不应有任何空格。
[.md,仅限 .rst] 代码块必须以空行开头和结尾。
理由:空格仅在隔离代码块之前和之后才有意义。
遵循这些说明将确保突出显示正确且一致地工作。
[仅限 .md、.rst] 代码块应指定语法(例如“bash”)。