ROS C++编程规范

命名

命名方案:

  • 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);

函数和方法通常执行一个动作,因此它们的名称应该清楚它们的作用:c​​heckForErrors()而不是errorCheck(),dumpDataToFile()而不是dataFile()。课程通常是名词。通过制作函数名称动词并遵循其他命名约定,可以更自然地阅读程序。

 

变量

有合理的描述性,长的变量名不会在内存中占用更多空间。

积分迭代器变量可以非常短,例如ijk。在如何使用迭代器方面保持一致(例如,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-pkgwg-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.hrosconsole包的一部分)中声明:

/** 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​​​​​​​

猜你喜欢

转载自blog.csdn.net/Travis_X/article/details/87968746