Autonomous Racing
1
f1tenth Project Group of Technical University Dortmund, Germany
|
This document is loosely based on the ROS C++ Style Guide and defines a style guide to be followed in writing C++ code in the Autonomous-Racing-PG. This guide SHOULD be followed as close as possible but MUST NOT be seen as holy book that has to be followed without any question.
In case this document does not describe something, try to finde a reasonable solution. In case of discussion: Be excellent to each other.
This document uses the following shortcuts:
The words SHOULD, SHOULD NOT, MUST, MUST NOT and MAY are used with the semantic described by RFC 2119.
It is heavily recommended to use the clang-format
tool with the provided .clang-format
. See the wiki for more information.
Names SHOULD make it easy for readers to understand your code.
Readers SHOULD need to make as few assumptions and guesses about your code as possible.
Avoid abbreviations and acronyms. Acronyms MAY be used if they can be understood without domain knowledge. Abbreviations MAY be used if their scope is very small.
A ROS package name SHOULD be snaked_cased.
A ROS topic and service name SHOULD be snaked_cased.
All filenames SHOULD be snake_cased.
All C++ source files SHOULD have the extension .cpp
.
All C++ header files SHOULD have the extension .h
and SHOULD be placed in the include
directory of the ROS Packages src
directory.
If a file mainly contains definition or implementation of a class, the file SHOULD be named snake_cased after the class name e.g. class MyOwnClass
results to the filename my_own_class.h/.cpp
A filename SHOULD be descriptive.
You MAY split the implementation on a by-function basis e.g.
include/my/example.h
src/my/example_my_function.cpp
src/my/example_do_stuff.cpp
A class name SHOULD be CamelCased e.g. class MyVector
. Short acronyms MAY be in all capitals e.g. MyMPC
(see MPC) or MyROSNode
.
A class name SHOULD NOT be composed of more then three words.
A function name SHOULD be camelCased. Arguments SHOULD be snake_cased. E.g.
As functions usually do something, their name SHOULD describe clearly what they do.
A variable name SHOULD be snake_cased and SHOULD NOT be cryptic, but try not to make them unnecessary long.
Counter variables MAY only use a single character variable like i
, j
or k
. Iterator variable SHOULD have it
in its name. E.g.
All member variable names SHOULD be snake_cased with m_
as prefix. E.g.
The prefix MAY be omitted if a member variable is public
. E.g.
All Constants names SHOULD be SNAKE_CASED.
Try to use constexpr
, enum
and enum class
before resorting to #define
or const
. E.g.
See Using constexpr to Improve Security, Performance and Encapsulation in C++ for more information on why constexpr
is awesome.
If a ROS topics or services name is stored in a constant, the constant's name SHOULD beginn with TOPIC_.
If a ROS parameters name is stored in a constant, the constant's name SHOULD beginn with PARAMETER_.
Global variables SHOULD NOT be used. See this.
Global variables names SHOULD be snake_cased with g_
as prefix. E.g.
A Namespace MUST be snake_cased.
Try to avoid long names.
Each source and header file MAY contain a license and copyright statement.
Any C++
code SHOULD be formatted using the provided .clang-format
file.
In rare cases where formatting is not wished for, you SHOULD use // clang-format off
to disable clang-format. E.g.
See disabling-formatting-on-a-piece-of-code for more information.
A line of code SHOULD NOT have more then 120 characters.
You SHOULD NOT use #ifndef
based include guards, as they are cumbersome and prone for errors. E.g.
Instead you SHOULD use #pragma once
. E.g.
Documentation explains the high-level structure of the code. It also provides information on how to use the project to those who didn't read the code.
Code SHOULD be documented in a doxygen compatible fashion.
Function and class summaries SHOULD be omitted if they do not provide more information than the name:
Conversely, classes and functions SHOULD be named so that it is obvious what they do:
Comments SHOULD explain why something is done, not what is being done. They SHOULD explain counter-intuitive semantics, assumptions, invariants and workarounds. However, code that needs explanatory comments can often be avoided by good naming.
Comments SHOULD NOT be used to structure the code. Instead of separating a long function with comments, it SHOULD be split into multiple shorter functions.
You SHOULD only add a summary to the declaration of a function.
You SHOULD NOT use printf
or std::cout
, instead use rosconsole as it supports:
/rosout
You SHOULD NOT use macros. Use constexpr
or inline
functions if possible, as macros are neither typed nor scoped and prone for errors. E.g.
You SHOULD NOT use preprocessor directives, as they are prone for errors. Use template
and inline
function as subtitution if possible.
You SHOULD NOT use output arguments, as they obfuscate side effects.
Usage of namespaces is highly encouraged.
The source files MAY be organized by namespace e.g. the file my_class.cpp
with the implementation of my::own::MyClass
MAY be put in the directory src/my/own/
.
The usage of nested namespace (e.g. namespace A::B::C::D::E::F::G::H::J {}
) is encouraged.
You SHOULD NOT use a using-directive in a header file. E.g.
You MUST declare an overridden virtual function with the identifiers virtual
AND override
to clarify whether or not a given function is virtual (and overridden).
See override and virtual for more information.
If a class is used to define a common interface for several possible implementations, virtual member functions SHOULD be used, as type casting otherwise could lead to hard to debug errors.
It is encouraged to use pure virtual classes as a common interface.
It is encouraged to use virtual
only with moderation.
A virtual
class SHOULD have a virtual
destructor.
It is encouraged to use multiple inheritance only with moderation. Try to avoid it if possible.
Exceptions SHOULD be preferred over the usage of error codes. If you are using error codes, it is highly encouraged to use an enum
or enum class
as return type.
You SHOULD document what kind of exception a given function might throw. E.g.
You MUST NOT throw an exception from a destructor.
You MUST NOT throw an exception from a callback function.
If your code can be interrupted by an exception, you MUST make sure such an exception does not lead to an undefined or otherwise broken state e.g. forgetting to free a mutex
. This MAY be accomplished by things like a lock_guard.
To prevent conflicts between enums, they SHOULD be either namespaced, classed or declared as a enum class
. E.g.
You SHOULD NOT use global variables and functions, as they create a hidden (global) state and are prone for threading and link time errors. (some more reasons)
You SHOULD NOT use static
class member variables, as they create a hidden (global) state and are prone for threading errors.
You SHOULD NOT use magic numbers in the source code. E.g.
If a number has a special meaning, usage of an constexpr
, enum
/enum class
to address it is strongly recommended.
You SHOULD NOT replace a magic number with a magic constant. E.g.
The usage of assertions to check invariants and assumptions is highly encouraged.
You SHOULD use the ROS assertions (ROS_COMPILE_ASSERT(cond)
,ROS_STATIC_ASSERT(cond)
,ROS_ASSERT(cond)
,ROS_ASSERT_MSG(cond, "format string", ...)
,ROS_ASSERT_CMD(cond, function())
) provided in ros/assert.h
. E.g.
See http://docs.ros.org for more information.
You SHOULD NOT do work in an assertion. E.g.
You SHOULD use the deprecated attribute to declare a struct
, class
, enum
, function
or other elements as deprecated. E.g.
You SHOULD use #pragma GCC warning "MSG"
or #pragma GCC error "MSG"
do deprecate whole files. E.g.
or
The main
function of a program SHOULD be in a seperate .c
/.cpp
file.
Zero cost abstractions like std::array
or std::lock_guard
SHOULD always be prefered.