命名
命名方案:
-
CamelCased:名称以大写字母开头,每个新单词都有一个大写字母,没有下划线。
-
camelCased:像CamelCase一样,但是首字母是小写的
-
under_scored:名称仅使用小写字母,单词用下划线分隔
-
ALL_CAPITALS:所有大写字母,单词以下划线分隔。
文件
所有文件都是under_scored。
源文件的扩展名为.cpp。
头文件的扩展名为.h。
是描述性的,例如,而不是laser.cpp,使用hokuyo_topurg_laser.cpp。
如果文件主要实现类,则在类之后命名该文件。例如,ActionServer类将存在于action_server.h文件中。
库
不要在库名称中的lib前缀后立即插入下划线。
例如:
lib_my_great_thing ##不好
libmy_great_thing ##好
Classes / Types
类名(和其他类型名称)是CamelCased
例如:
class ExampleClass;
例外:如果类名包含简短的首字母缩略词,首字母缩略词本身应该是所有大写字母,例如:
class HokuyoURGLaser;
Function / Methods
通常,函数和类方法名称是camelCased,参数是under_scored,例如:
int exampleMethod(int example_arg);
函数和方法通常执行一个动作,因此它们的名称应该清楚它们的作用:checkForErrors()而不是errorCheck(),dumpDataToFile()而不是dataFile()。课程通常是名词。通过制作函数名称动词并遵循其他命名约定,可以更自然地阅读程序。
变量
有合理的描述性,长的变量名不会在内存中占用更多空间。
积分迭代器变量可以非常短,例如i,j,k。在如何使用迭代器方面保持一致(例如,i在外部循环上,j在下一个内部循环上)。
STL迭代器变量应指示它们正在迭代的内容,例如
std :: list <int> pid_list;
std :: list <int> :: iterator pid_it;
或者,STL迭代器可以指示它可以指向的元素的类型,例如:
std :: list <int> pid_list;
std :: list <int> :: iterator int_it;
常量
常数,无论它们在哪里使用,都是ALL_CAPITALS。
成员变量
作为类成员的变量(有时称为字段)是under_scored,添加了尾部下划线。
例如:
int example_int_;
全局变量
使用它们时添加前缀g_。
int g_shutdown;
许可声明
每个源文件和头文件必须在文件开头包含许可证和版权声明。
在ros-pkg和wg-ros-pkg存储库中,LICENSE目录包含许可证模板,注释为包含在C / C ++代码中。
有关允许的许可和许可策略的信息,请参阅ROS开发人员指南。
格式化
编辑器应该处理大多数格式化任务。有关示例编辑器配置文件,请参见EditorHelp。
将每个块缩进2个空格。永远不要插入文字制表符。
命名空间的内容不缩进。
例如:
if(a < b)
{
// do stuff
}
else
{
// do other stuff
}
如果是单行语句,则可以省略大括号,例如:
if(a < b)
x = 2*a;
如果封闭的块更复杂,请始终包括括号,例如:
if(a < b)
{
for(int i=0; i<10; i++)
PrintItem(i);
}
完整的例子:
/*
* A block comment looks like this...
*/
#include <math.h>
class Point
{
public:
Point(double xc, double yc) :
x_(xc), y_(yc)
{
}
double distance(const Point& other) const;
int compareX(const Point& other) const;
double x_;
double y_;
};
double Point::distance(const Point& other) const
{
double dx = x_ - other.x_;
double dy = y_ - other.y_;
return sqrt(dx * dx + dy * dy);
}
int Point::compareX(const Point& other) const
{
if (x_ < other.x_)
{
return -1;
}
else if (x_ > other.x_)
{
return 1;
}
else
{
return 0;
}
}
namespace foo
{
int foo(int bar) const
{
switch (bar)
{
case 0:
++bar;
break;
case 1:
--bar;
default:
{
bar += bar;
break;
}
}
}
} // end namespace foo
行长度
最大行长度为120个字符。
#ifndef guards
必须保护所有标题免受#ifndef guards的多次包含,例如:
#ifndef PACKAGE_PATH_FILE_H
#define PACKAGE_PATH_FILE_H
...
#endif
文档
代码必须记录在案。无法维护未记录的代码,无论其功能如何。
我们使用doxygen来自动记录我们的代码。Doxygen解析您的代码,从函数,变量,类等旁边出现的特殊格式的注释块中提取文档.Doxygen也可用于构建更多叙述性的自由格式文档。
有关在代码中插入doxygen样式注释的示例,请参阅rosdoc页面。
应记录所有函数,方法,类,类变量,枚举和常量。
控制台输出
避免使用printf和cout。相反,请使用rosconsole满足您的所有输出需求。它提供了包含printf和stream风格参数的宏。就像printf一样,rosconsole输出进入屏幕。与printf不同,rosconsole输出是:
- 颜色编码
- 由详细级别和配置文件控制
-
发布于/ rosout,因此网络上的任何人都可以查看(仅在使用roscpp时)
- 可选地记录到磁盘
宏
尽可能避免使用预处理器宏。与内联函数和常量变量不同,宏既不是类型也不是作用域。
预处理程序指令(#if与#ifdef)
对于条件编译(除了上面解释的#ifndef保护),总是使用#if,而不是#ifdef。
有人可能会编写如下代码:
#ifdef DEBUG
temporary_debugger_break();
#endif
其他人可能会使用关闭的调试信息编译代码,如:
cc -c lurker.cpp -DDEBUG = 0
如果必须使用预处理器,请始终使用#if。这样可以正常工作,并且做正确的事情,即使DEBUG根本没有定义。
#if DEBUG
temporary_debugger_break();
#endif
输出参数
方法/函数的输出参数(即函数可以修改的变量)由指针传递,而不是通过引用传递。例如:
int exampleMethod(FooThing input, BarThing* output);
相比之下,当通过引用传递输出参数时,调用者(或代码的后续读者)无法在不读取方法原型的情况下判断是否可以修改参数。
命名空间
鼓励使用命名空间来扩展代码范围。根据包的名称选择一个描述性名称。
切勿在头文件中使用using-directive。这样做会污染包含标头的所有代码的命名空间。
在源文件中使用using-directive是可以接受的。但是最好使用using-declarations,它只引入你打算使用的名字。
例如,而不是这个:
using namespace std; // Bad, because it imports all names from std::
做这个:
using std::list; // I want to refer to std::list as list
using std::vector; // I want to refer to std::vector as vector
继承
继承是定义和实现公共接口的适当方式。基类定义接口,子类实现它。
继承还可用于提供从基类到子类的公共代码。不鼓励使用这种继承。在大多数情况下,“子类”可以改为包含“基类”的实例,并且可以获得相同的结果,同时减少混淆的可能性。
覆盖子类中的虚方法时,始终将其声明为虚拟方法,以便读者知道发生了什么。
多重继承
强烈建议不要使用多重继承,因为它容易产生混淆。
异常
异常是首选的错误报告机制,而不是返回整数错误代码。
始终记录您的包可以在每个函数/方法上抛出的异常。
不要从析构函数中抛出异常。
不要从不直接调用的回调中抛出异常。
如果您在包中选择使用错误代码而不是例外,请仅使用错误代码。 始终如一。
编写异常安全的代码
当您的代码可以被异常中断时,您必须确保在堆栈变量超出范围时将释放您保留的资源。特别是,必须释放互斥锁,并且必须释放堆分配的内存。使用以下互斥锁和智能指针完成此安全性:
- TODO
- TODO
枚举
命名空间化您的枚举,例如
namespace Choices
{
enum Choice
{
Choice1,
Choice2,
Choice3
};
}
typedef Choices::Choice Choice;
这可以防止枚举污染它们所在的命名空间。枚举中的单个项由以下引用:Choices :: Choice1,但typedef仍允许声明没有命名空间的Choice枚举。
全局
不鼓励全局变量和函数。它们会污染命名空间并使代码不再可重用。
特别强烈建议不要使用全局变量。它们阻止了一段代码的多次实例化,并使多线程编程成为一场噩梦。
大多数变量和函数应该在类中声明。其余部分应在名称空间内声明。
例外:文件可能包含main()函数和一些全局的小帮助函数。但请记住,有一天,那些辅助功能可能对其他人有用。
静态类变量
不鼓励使用静态类变量。它们阻止了一段代码的多次实例化,并使多线程编程成为一场噩梦。
调用exit()
仅在应用程序的明确定义的出口点调用exit()。
永远不要在库中调用exit()。
断言
使用断言检查前提条件,数据结构完整性以及内存分配器的返回值。断言比编写条件语句更好,条件语句很少(如果有的话)被执行。
不要直接调用assert()。而是使用其中一个函数,在ros / assert.h(rosconsole包的一部分)中声明:
/** ROS_ASSERT asserts that the provided expression evaluates to
* true. If it is false, program execution will abort, with an informative
* statement about which assertion failed, in what file. Use ROS_ASSERT
* instead of assert() itself.
* Example usage:
*/
ROS_ASSERT(x > y);
/** ROS_ASSERT_MSG(cond, "format string", ...) asserts that the provided
* condition evaluates to true.
* If it is false, program execution will abort, with an informative
* statement about which assertion failed, in what file, and it will print out
* a printf-style message you define. Example usage:
*/
ROS_ASSERT_MSG(x > 0, "Uh oh, x went negative. Value = %d", x);
/** ROS_ASSERT_CMD(cond, function())
* Runs a function if the condition is false. Usage example:
*/
ROS_ASSERT_CMD(x > 0, handleError(...));
/** ROS_BREAK aborts program execution, with an informative
* statement about which assertion failed, in what file. Use ROS_BREAK
* instead of calling assert(0) or ROS_ASSERT(0). You can step over the assert
* in a debugger.
* Example usage:
*/
ROS_BREADK();
不要在断言中做工作; 只检查逻辑表达式。根据编译设置,可能无法执行断言。
通常会开发启用了断言检查的软件,以便捕获违规行为。当接近软件完成并且在面对大量测试时发现断言始终为真时,您使用一个标志来构建,该标志从编译中删除断言,因此它们不占用任何空间或时间。catkin_make的以下选项将为所有ROS包定义NDEBUG宏,从而删除断言检查。
catkin_make -DCMAKE_CXX_FLAGS:STRING =“ - DNDEBUG”
注意:当您使用此命令运行时,cmake将重建您的所有软件记住通过后续catkin_make运行的设置,直到您删除构建和开发目录并重建。
测试
见gtest。
可移植性
我们目前支持Linux和OS X,计划最终支持其他操作系统,包括可能的Windows。为此,保持C ++代码可移植性非常重要。以下是一些值得注意的事项:
-
不要将uint用作类型。而是使用unsigned int。
-
从std命名空间中调用isnan(),即:std :: isnan()
弃用
要弃用包中的整个头文件,您可以包含适当的警告:
#warning mypkg/my_header.h has been deprecated
要弃用函数,请添加不推荐使用的属性:
ROS_DEPRECATED int myFunc();
要弃用类,请弃用其构造函数和任何静态函数:
class MyClass
{
public:
ROS_DEPRECATED MyClass();
ROS_DEPRECATED static int myStaticFunc();
};
来自http://wiki.ros.org/CppStyleGuide