watonomous.github.io

  1. Software Division
  2. Software Division Home
  3. Tutorials

[ Software Division : WATO ROS C++ Code Style ]

Created by [ Henry Wang], last modified on Feb 04, 2020

This style guide is partly based on the ROS C++ Style Guide and Google’s C++ Style Guide. It is required that all members adhere to this guide when developing C++ code, and that all leads and reviewers compare code against these guidelines when performing code reviews. If an item is not specified in this document, please consult the ROS and Google style guides linked above (ROS first, Google second). 

Section 1: File System Components

1.0 Directory Structure

Your git repositories should be cloned into the src folder of your catkin workspace. Any ROS packages that you build should be created in your sub-team’s folder. For example:

catkin_workspace/
    build/
    devel/
    src/
        team_folder/
            sub_team_folder/
                package_1/
                package_2/

Inside each package folder you should have:

The include/package_name directory should contain .h files and the src directory should contain .cpp files. Any .cpp file that implements a class should have a corresponding .h file. See another section for how to write your .h and .cpp files.

1.1 File Naming

Section 2: Code Formatting

2.0 Naming Conventions

ROS topic names, service names, node names, and frame names: like_this

Libraries (such as in your CMakeLists.txt): like_this

C++ classes and structs 

Variables 

Functions

Constants and Enumerators

Namespaces

Macros

2.1 Styling

Braces must go on the next line, and have a line to themselves

Keep the length of each line to be less than 100 characters

All code must be indented 2 additional spaces to illustrate scope

Add spaces between arithmetic and logical operators

Add spaces after decision-making keywords
while (true)  // Good
while(true)   // Bad

One-line decisions may omit braces if it is still readable

CORRECT:

for (int i = 0; i < NUM_PEOPLE_TO_FEED; ++i)  // Note ++i or i++ is fine
{
  if (tim_hortons->closed() || tim_hortons->line_length >= 20)
  {
    food = campus_pizza->getFood();
  }
  else
  {
    food = tim_hortons->getFood();
  }
  person[i].eat(food);
}

\

INCORRECT:

for(int i=0 ; i<NUM_PEOPLE_TO_FEED ; ++i) {  // Poor spacing and braces
  if (tim_hortons->closed() || tim_hortons->line_length>=20)  // Spacing
  {
          food=campus_pizza->getFood();  // Bad indentation
}  // Misaligned braces
  else   // Why would you do this
  person[i].eat(food);
}

\

ACCEPTABLE:

for (int i = 0; i < NUM_PEOPLE_TO_FEED; i++)  // Note ++i or i++ is fine
{
  if (tim_hortons->closed() || tim_hortons->line_length >= 20)
    food = campus_pizza->getFood();
  else
    food = tim_hortons->getFood();
  person[i].eat(food);
}

2.2 Commenting

Using // or /* */ are both fine, just be consistent. Comment thoroughly so that anyone can understand your work.

All files should have a comment at the top with the file name, author’s name and a brief description of the file:

/**
 * Name    colonize_mars.h
 * Author  Elon Musk (emusk@uwaterloo.ca)
 * Brief   Creates an instance of the SpaceXRocket class and launches it
 *         to Mars
 */

This type of comment should be included in all C++ files (.h, .cpp) and ROS files (.msg, .launch, .srv, ...)I

[If you are editing a file that has already been created, add your name and email underneath the original author’s, like so:]

 * Author       Original Author (oauthor@uwaterloo.ca)
 * Modified by  Second Author (sauthor@uwaterloo.ca)
 *              Third Author (tauthor@edu.uwaterloo.ca)

In your .cpp file, every member function should have a function header with the following properties:

\

/**
 * Name    getEncoderVal
 * Brief   Accesses the encoder value for a specified wheel
 * Param   wheel_num: The index of the requested wheel
 * Return  double: The encoder value in RPM
 *                 Positive for CCW, negative for CW
 *                 Return DBL_MAX if invalid
 */
double getEncoderVal(unsigned int wheel_num)
{
  if (wheel_num > 3)
    return DBL_MAX;
  return encoder_vals_[wheel_num];
}

2.3 C++ Header Files

Header files should be contained within an appropriate header guard:

#ifndef PACKAGE_PATH_NAME_FILE_NAME_H
#define PACKAGE_PATH_NAME_FILE_NAME_H
...
#endif  // PACKAGE_PATH_NAME_FILE_NAME_H

Within these guards you should define your class, which should have the same name as your file name. For example, a class called MyClass should be defined in a file called my_class.h. If you are using a header file to define utility functions only (e.g., no class implementation) then you should contain your functions/variables in a namespace. Declare all your #includes at the top of your header file, underneath the header guard. The general convention is to contain libraries in <> and header files in “”.

#include <string>
#include “my_class.h”
...

2.4 C++ Implementation Files

C++ implementation files (.cpp) should #include all appropriate headers and libraries, even if it was already included in the corresponding .h file. This improves readability and allows developers to see the dependencies of the file with a glimpse.

2.5 ROS Files

2.5.0 Topics, Messages, Services, Actions

All variables defined in ROS files (e.g. .msg) should have an accompanying comment

If you find you have several variables contained within one message file, consider breaking it into different message types that can be included in the main message type

Tips for when to use topics, services and actions:

2.5.1 CMakeLists.txt, package.xml

Delete all unnecessary comments that are generated by the default CMakeLists.txt and package.xml

In package.xml:

Section 3: Coding Practices

3.0 Classes

3.1 Inheritance

3.2 Friend Classes

3.3 Namespaces

3.4 Exceptions

3.5 Print Statements

3.6 Macros

3.7 Global and Static Variables

3.8 Constant Variables

3.9 Assertions

3.10 Passing/Returning by Reference/Value

Pass simple types by value, not reference - use const when values are immutable

Returning by & or const can have significant performance savings when the normal use of the returned value is for observation

Returning by value is better for thread safety and if the normal use of the returned value is to make a copy anyhow, there's no performance lost

If your API uses covariant return types, you must return by & or *

Temporary and local values are always returned by value

More details: https://github.com/lefticus/cppbestpractices/issues/21 

3.11 Memory Leaks and Smart Pointers

Good Example:

auto myobj = std::unique_ptr<MyClass>(new MyClass(constructor_param1, 

constructor_param2));
auto mybuffer = std::unique_ptr<char[]>(new char[length]);

// or for reference counted objects
auto myobj = std::make_shared<MyClass>(); 
// ...
// myobj is automatically freed for you whenever it is no longer used.

Difficult to avoid memory errors when:

MyClass *myobj = new MyClass;
// ...
delete myobj;

3.12 Forward Declaration

Using include:

//file C.h 
#include "A.h" 
#include "B.h" 
class C;


Using forward declarations:

//file C.h 
#include "B.h" 
class A; 
class C; 
//file C.cpp 
#include "C.h" 
#include "A.h" 

Document generated by Confluence on Dec 10, 2021 04:02

Atlassian