一、动机
代码一致才能可读、联调、高效率、高复用、可移植性。
二、命名方式
CamelCased
camelCased
under_scored
ALL_CAPITALS
2.1 Package命名方式:under_scored
2.2 Topics / Services命名方式:under_scored
2.3 Files命名方式:under_scored
比如:hokuyo_topurg_laser.cpp、hokuyo_topurg_laser.hpp
如果是类文件,那么命名方式迎合类名,比如:ActionServer类,那么action_server.h
2.3.1 lib命名方式:under_scored
不要在lib后面插入下划线,比如:
lib_my_great_thing ## Bad
libmy_great_thing ## Good
2.4 Classes / Types命名方式:CamelCased
比如:class ExampleClass
如果名字中有缩写,那么全部大写:class HokuyoURGLaser
2.5 Function / Methods
函数名称和类的方法:camelCased,参数采用:under_scored
2.6 Variables
一般采用under_scored。合理描述变量,不要表达不清。
迭代变量,比如:for(int i = 0; …),可以用短变量i, j, k;应用顺序保证为i,j,k。
STL 迭代变量,必须标明STL变量的类型如:
std::list<int> pid_list; std::list<int>::iterator pid_it;
STL迭代器的可以用自身类型表示
std::list<int> pid_list; std::list<int>::iterator int_it;
2.6.1 Constant命名方式:ALL_CAPITALS
2.6.2 Member variables命名方式:under_scored_
2.6.3 Global variables命名方式:g_under_scored
2.7 Namespaces命名方式:under_scored
三、License statements
在头文件与源文件的开头都要包含Copyright,比如:
Copyright (c) 2019 Fujian(Quanzhou)-HIT Research Institute of Engineering and Technology All rights reserved.
解释一下,©是版权的意思,也可以写成(c);2019表示代码完成时间;Fujian(Quanzhou)-HIT Research Institute of Engineering and Technology 泉州工研院;All rights reserved表示保留所有权利。
也可以写成:
Copyright (c) 2019-2020 Fujian(Quanzhou)-HIT Research Institute of Engineering and Technology All rights reserved.
2019-2020表示意思完成时间是2008年,最近一次修订在2011年。
四、Formatting
IDE应该可以处理大量的格式化事情。建议采用VSCode。
缩进为2个空格,不用tab键。Namespaces不缩进
比如:
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
4.1 每行字符数:120个字符以内
4.2 头文件格式
防止重复调用,采用以下格式:
#ifndef PACKAGE_PATH_FILE_H #define PACKAGE_PATH_FILE_H ... #endif
五、文档
所有程序必须有文档,而且采用doxygen建立文档。
六、控制台输出
尽量不采用printf与cout,采用 rosconsole作为输出。
七、宏
避免使用宏定义,与inline与const不同,宏既不是类型,也不是作用域。
八、预处理指令(#if vs. #ifdef)
对于条件编译,我们采用#if,不采用#ifdef。
比如:
#ifdef DEBUG temporary_debugger_break(); #endif
如果我们想关闭DEBUG,那么会输入:
cc -c lurker.cpp -DDEBUG=0
如果采用:
#if DEBUG temporary_debugger_break(); #endif
即使DEBUG没有定义,也可以用。
九、输出参数
用于输出的参数采用指针,而不是引用。比如:
int exampleMethod(FooThing input, BarThing* output);
十、Namespaces
非常建议大家使用命名空间来指定代码的作用域,根据package的名字来命名namespace
不采用using-directive,比如:
using namespace std; // Bad, because it imports all names from std::
采用using-declarations,比如:
using std::list; // I want to refer to std::list as list using std::vector; // I want to refer to std::vector as vector
十一、继承
采用virtual表示多态,不采用多重继承
十二、异常处理
异常处理采用error-report机制,而不采用返回整形值。比如:
在document记录在packages中function/method产生的全部异常。
不采用在destructors中调用throw。
不采用在callback方法中调用throw。
如果采用在package中采用错误代码,而非异常处理,请保持代码始终如一。
12.1 异常处理注意事项:
当您的代码可以被异常中断时,您必须确保在堆栈变量超出范围时将释放您保留的资源。 特别是,必须释放互斥锁,并且必须释放堆分配的内存。
十三、Enumerations(枚举类型)
用namespace控制枚举类型:
namespace Choices { enum Choice { Choice1, Choice2, Choice3 }; } typedef Choices::Choice Choice;
十四、Globals(全局类型)
不采用全局变量以及全局函数。
十五、Static class variables(静态成员变量)
不采用静态成员变量。
十六、调用exit()函数
仅在程序出口调用exit()函数。
在lib中不采用exit()函数。
十七、Assertions()
采用assert检查前提条件,数据结构完整性以及内存分配器的返回值。 assert比编写条件语句更好,条件语句很少(如果有的话)被执行。
不采用直接调用assert(),而采用ros/assert.h
/** 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();
不要在assert中做工作; 只检查逻辑表达式。 根据编译设置,可能无法执行断言。
十八、Testing
见gtest.
十九、Portability(可以移植性)
目前ros支持linux与XOS之间的移植,需要注意:
(1)不采用uint,采用unsigned int
(2)调用isnan()使用std::isnan()
二十、Deprecation(移除不用的代码)
如果移除包中的头文件,应在头文件加入警告:
#warning mypkg/my_header.h has been deprecated
如果移除函数,添加不推荐使用属性:
ROS_DEPRECATED int myFunc();
如果移除一个类,请移除其构造函数和任何静态函数:
class MyClass { public: ROS_DEPRECATED MyClass(); ROS_DEPRECATED static int myStaticFunc(); };