8.9 KiB
C++ Code Conventions
Rev. 1, 12-09-2025, Wessel T contact@wessel.gg
Proposals can be made in the form of a merge request to the
mainbranch. Don't forget to update the Changelog when doing so. These proposals need to be reviewed and accepted by everyone.
A big number of rules can be enforced by using the .editorconfig. It is
recommended to install the editorconfig plugin into your desired IDE to automate correction of
these formatting mistakes.
C++ Core Guidelines
The following rules have been adopted from the C++ Core Guidelines Examples are given for every rule. Read them if you do not understand what a rule means.
- C: Classes and class hierarchies
- C.ctor: Constructors
- C.45: Don’t define a default constructor that only initializes data members; use in-class member initializers instead.
- C.ctor: Constructors
- ES: Expressions and statements
- ES.dcl: Declarations
- ES.11: use auto to avoid redundant repetition of type names.
- ES.23: Prefer the {}-initializer syntax.
- ES.expr: Expressions
- ES.47: Use nullptr rather than 0 or NULL
- ES.dcl: Declarations
- Enum: Enumerations
- Enum.3: Prefer class enums over “plain” enums.
- T: Templates and generic programming
- Template interfaces
- T.43: Prefer using over typedef for defining aliases
- Template interfaces
- R: Resource management
- R.smart: Smart pointers
- R.20: Use unique_ptr or shared_ptr to represent ownership.
- R.21: Prefer unique_ptr over shared_ptr unless you need to share ownership.
- R.smart: Smart pointers
Naming
- FileNames are written in PascalCase.
- namespace are one word lower case.
- Class/Struct names are PascalCase.
- Interfaces are named as
IClass. - Design patterns are written fully, e.g.
AbstractBehaviorFactory. - Function names are snake_case.
- Function arguments are snake_case.
- Variable names are snake_case.
- Constexpr variables are named snake_case.
- Macro's are named SCREAMING_SNAKE_CASE.
- Give a variable the shortest descriptive name, so do not shorten the name if it makes the name ambiguous.
Indentation and tabs
- Indentation is done with spaces, tabs are to be converted to 4 spaces (most IDEs have a setting for this). So each level of indentation is 4 spaces.
- Ensure that commits do not add trailing whitespace (most IDEs have a setting for trimming trailing whitespace).
- The maximum line length is 100 characters, but exceptions are allowed where longer lines are more practical.
Style choices
- Curly brackets start on the same line (
if (true) {). - Curly brackets may start on a new line if the statement is multiple lines long. For example after a constructor initializer list.
- Lambda functions should have no spaces inbetween (
[](){}).
Files
- Put one class/struct in a file and name the file after that class/struct.
- Member structs may be in the same file as a class to avoid scattering of information over many files.
- When the file contains something else choose a name that accurately represents the contents.
- Use full names, so no abbreviations (e.g.
RouteFileGeneratorinstead ofRouteFileGenor waypoint instead of wp) - Avoid acronyms. Exceptions are made for company jargon, like
IMUforInertialMeasurementUnit. Do document the full name at the top of the file when using acronyms. - Friend classes are not allowed.
- File include paths may be global
include/ros/File.hppor relativeFile.hpp. Relative include paths must be the same directory or a child, so there can not be any../in include paths.
For C++ file types we use the following conventions:
- C++ header (*.hpp)
- C++ source (*.cpp)
Using C libraries is allowed, but any includes must be in an extern c block.
Comments
- Comments are lines starting with
//. - Header files have a short comment at the top that explains the purpose of the file's contents.
- Functions can get a comment for extra explanation if necessary.
- Avoid adding comments for variables or function parameters that do not add any value. (e.g. @ returns bool value <// returns value)
- If code is commented out, remove it.
Namespaces
- Code in a namespace is not indented.
- Avoid the
using namespaceinstead write out the name of what you need. (e.g. writestd::cout<<, instead ofusing std; .... cout<<) using namespaceis only allowed in small scope (short function, loop). Never expose them in the header file.using namespaceis also allowed in test files as they are not included anywhere else.
Classes
Access modifiers (public/private/protected)
- Access-modifiers have no indentation (level 0), but everything inside them has level 1 indentation. So member functions/variables have level 1 indentation.
- All members in a class have to be ordered by their access-modifier, so in the
following order:
- public
- protected
- private
- In a struct generally everything is public, so no access modifiers are needed.
- Avoid using public member variables in classes, as it breaks encapsulation.
Functions
- Getters and setters do not get the
get_ / set_prefix. - FUnctions that invoke asynchronous operation without blocking must get the
_asyncsuffix. - Declare functions as
constwhere possible. - Declare functions with
overridewhen overriding a base class function. - Functions declared as pure virtual must not have an implementation.
- Pass in function arguments as const ref to avoid copying or changing variables when not necessary. Simple types, such as int, float etc.. can be passed in regularly as they are easily copied.
- Avoid side effects in a function (i.e. a function does something extra that is not clear from the name), like a getter also incrementing a counter. Separate this logic out into a different function.
- The body of an if-statement must be surrounded by braces
{}. - Functions private to a .cpp file must be marked
static.
Function complexity
Limit the complexity of the functions you write by cutting them up into multiple functions or creating classes to handle some of the work. In general: a function should do only one thing.
- Command/query separation: a function should be either be a command (i.e. a function that does something like add_numbers, save_file) or a query (i.e. a function that retrieves information, like get_state).
- Maximum function length is 60 lines.
- The maximum number of function arguments is 4.
- The maximum cyclomatic complexity of a function is 20. (i.e. the amount of paths your function can take. So one if statement results in 2 paths, and 2 nested if statements are 4)
- Avoid passing in boolean arguments as flag arguments (i.e. the boolean determines which path the function takes). If possible split it up into two functions instead.
- Limit the stacking of conditionals (e.g.
if (is_charging && (!is_free || moving))). Instead wrap parts in a variable or simplify the if statement by removing the second part and wrapping that and the underlying code in a function.
Variables
- private and protected member variables in a class have a suffix
_(e.g. bool turned_on_) - public member variables are named without suffix.
- Initialize member variables in: (sorted by preference)
- in-class initializer
- constructor initializer list
- constructor body
- Wrap the following in a variable to increase clarity:
- magic numbers
- magic strings
- ternary statements (e.g. int module = is_connected ? 5 : 4)
- boundary condition in loops (e.g. uint loop_end = vector.size() - 1)
- Prefer constexpr variables over macros.
Example file
The code below serves as a visual example of the style guidelines, e.g. where do the brackets go, when is an indentation used, where do we add an whitespace. This example is included, because rules about code style are often easier to see than to read.
// Header file commentary
#pragma once
namespace foo::bar {
// When forward declaring a class you can choose to use indenting.
namespace robot {
class SimpleIdea;
}
// Use a comment only if it adds extra information
class ComplexIdea {
public:
ComplexIdea();
void some_function(int lower_limit);
private:
int the_answer_;
};
} // namespace foo::bar
#endif
namespace foo {
namespace bar {
extern "C" {
#include <c-library.h>
}
#include <cpp-library>
#include "custom-class.hpp"
ComplexIdea::ComplexIdea() : the_answer_(42) {
} // Headings should be surrounded by blank lines
void ComplexIdea::some_function(int lower_limit) {
int minimum_value = 1337;
if (lower_limit < minimum_value) {
return;
}
int lower_limit_squared = lower_limit * lower_limit;
// Note the indenting, the location of the { and the space before the {.
if (the_answer_ > lower_limit) {
the_answer_ = lower_limit;
} else if (the_answer_ < squared) {
the_answer_ = lower_limit * 0.5;
}
}
} // namespace bar
} // namespace foo
Changelog
[12-09-2025] Wessel T: Create initial document