Skip to content

C++#

This page describes the rules for C++ code in Autoware. Tools check many of the rules. The tools section lists them, and each rule says if a tool checks it.

References#

Follow the guidelines below if a rule is not defined on this page.

  1. https://docs.ros.org/en/humble/The-ROS2-Project/Contributing/Code-Style-Language-Versions.html
  2. https://www.autosar.org/fileadmin/standards/R22-11/AP/AUTOSAR_RS_CPP14Guidelines.pdf
  3. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines

Also, it is encouraged to apply Clang-Tidy to each file. For the usage, see Applying Clang-Tidy to ROS packages.

Note that not all rules are covered by Clang-Tidy.

Tools#

Language standard and compiler warnings#

Autoware code uses C++17. The autoware_package() macro sets CMAKE_CXX_STANDARD to 17 when the package does not set it. The macro also turns on -Wall -Wextra -Wpedantic and treats compiler warnings as errors. Fix each warning in your code. Do not turn the warnings off for the whole package.

ClangFormat#

pre-commit formats all C, C++, and CUDA files with ClangFormat. The configuration is the .clang-format file at the root of each repository. It is a modified copy of the ROS 2 configuration in ament_clang_format, which is based on the Google style. The main settings are:

  • The column limit is 100 characters.
  • The opening brace of a class, function, namespace, and struct goes on its own line.
  • Access modifiers (public:, private:) have an indent of -2.
  • Pointers and references are aligned in the middle: int * ptr and const Foo & foo.
  • Include directives are sorted in the order that this page defines.

The ClangFormat version is pinned in .pre-commit-config.yaml. To use the same version in your editor, see Matching your local clang-format to pre-commit.

cpplint#

pre-commit runs cpplint with the CPPLINT.cfg file at the root of each repository. The line length is 100. The configuration turns off the checks that conflict with ClangFormat and with the rules on this page, for example whitespace/braces, build/include_order, and runtime/references.

If cpplint reports a false positive, suppress it on that line with a // NOLINT comment that names the category and the reason. For example, status.hpp of autoware_component_interface_utils has:

void copy(const T1 & src, T2 & dst)  // NOLINT(build/include_what_you_use): cpplint false positive

Include guards#

Use include guards in header files. The ros-include-guard hook of tier4/pre-commit-hooks-ros makes the macro name from the path of the file after the include, src, or test directory. It joins the path parts with __, replaces . with _, changes all letters to uppercase, and adds _ at the end. The hook fixes a wrong name automatically. It accepts a header that uses #pragma once instead of an include guard. For example, the guard of include/autoware/interpolation/linear_interpolation.hpp in autoware_interpolation is:

#ifndef AUTOWARE__INTERPOLATION__LINEAR_INTERPOLATION_HPP_
#define AUTOWARE__INTERPOLATION__LINEAR_INTERPOLATION_HPP_

// ...

#endif  // AUTOWARE__INTERPOLATION__LINEAR_INTERPOLATION_HPP_

Static analysis#

These tools run in the CI of autoware_core and autoware_universe, not in pre-commit:

  • Clang-Tidy checks the changed files of a pull request with the .clang-tidy-ci file of the repository. The configuration treats all enabled warnings as errors.
  • Cppcheck checks all C++ files of the packages that the pull request changes, with the cppcheck-differential workflow. It can report problems in files that the pull request does not change.
  • ament_cmake_cppcheck runs during colcon test for each package that depends on autoware_lint_common.

Style rules#

Include header files in the defined order (required, partially automated)#

Rationale#

  • Due to indirect dependencies, the include system of C++ makes different behaviors if the header order is different.
  • To reduce unintended bugs, local header files should come first.

Reference#

Example#

Include the headers in the following order:

  • Main module header
  • Local package headers
  • Other package headers
  • Message headers
  • Boost headers
  • C system headers
  • C++ system headers
// Compliant
#include "my_header.hpp"

#include "my_package/foo.hpp"

#include <package1/foo.hpp>
#include <package2/bar.hpp>

#include <std_msgs/msg/header.hpp>

#include <iostream>
#include <vector>

If you use "" and <> properly, ClangFormat in pre-commit sorts headers automatically.

Do not define macros between #include lines because it prevents automatic sorting.

// Non-compliant
#include <package1/foo.hpp>
#include <package2/bar.hpp>

#define EIGEN_MPL2_ONLY
#include "my_header.hpp"
#include "my_package/foo.hpp"

#include <Eigen/Core>

#include <std_msgs/msg/header.hpp>

#include <iostream>
#include <vector>

Instead, define macros before #include lines.

// Compliant
#define EIGEN_MPL2_ONLY

#include "my_header.hpp"

#include "my_package/foo.hpp"

#include <Eigen/Core>
#include <package1/foo.hpp>
#include <package2/bar.hpp>

#include <std_msgs/msg/header.hpp>

#include <iostream>
#include <vector>

If there are any reasons for defining macros at a specific position, write a comment before the macro.

// Compliant
#include "my_header.hpp"

#include "my_package/foo.hpp"

#include <package1/foo.hpp>
#include <package2/bar.hpp>

#include <std_msgs/msg/header.hpp>

#include <iostream>
#include <vector>

// For the foo bar reason, the FOO_MACRO must be defined here.
#define FOO_MACRO
#include <foo/bar.hpp>

Use lower snake case for function names (required, partially automated)#

Rationale#

  • It is consistent with the C++ standard library.
  • It is consistent with other programming languages such as Python and Rust.

Exception#

  • For member functions of classes inherited from external project classes such as Qt, follow that naming convention.

Reference#

Example#

void function_name()
{
}

Use upper camel case for enum names (required, partially automated)#

Rationale#

  • It is consistent with ROS 2 core packages.

Exception#

  • Enums defined in the rosidl file can use other naming conventions.

Reference#

Example#

enum class Color
{
  Red, Green, Blue
};

Use lower snake case for constant names (required, partially automated)#

Rationale#

  • It is consistent with ROS 2 core packages.
  • It is consistent with std::numbers.

Exception#

  • Constants defined in the rosidl file can use other naming conventions.

Reference#

Example#

constexpr double gravity = 9.80665;

Count acronyms and contractions of compound words as one word (required, partially automated)#

Rationale#

  • To clarify the boundaries of words when acronyms are consecutive.

Reference#

Example#

class RosApi;
RosApi ros_api;

Do not use the auto keywords with Eigen's expressions (required)#

Rationale#

  • Avoid using auto with Eigen::Matrix or Eigen::Vector variables, as it can lead to bugs.

Reference#

Use RCLCPP_* (e.g. RCLCPP_INFO) macros instead of printf or std::cout for logging (required)#

Rationale#

  • Reasons include the following:
    • It allows for consistent log level management. For instance, with RCLCPP_* macros, you can simply set --ros-args --log-level to adjust the log level uniformly across the application.
    • You can standardize the format using RCUTILS_CONSOLE_OUTPUT_FORMAT.
    • With RCLCPP_* macros, logs are automatically recorded to /rosout. These logs can be saved to a rosbag, which can then be replayed to review the log data.

Reference#