CPP Style Guide and Recommendations - epfl-lasa/wiki GitHub Wiki

LASA C++ Style Guide and Recommendations

Foreword

C++ is a beast of a language - it is traditional, and it is modern. It is so close, and yet so far, from base C. It lets you get incredibly close to the hardware and manage every bit of memory yourself, and at the same time it provides incredible abstraction patterns.

Within LASA, where people's experience and ambitions with coding projects are diverse, the aim of this page is not to militantly enforce modern, gold-standard programming. Rather, we just want code to be consistent and readable so we can focus on actual research problems.

The Google C++ Style Guide has been widely adopted as good and safe practice. It is not without its flaws, and can be quite restrictive particularly towards modern use of the language. Still, its rules are clear and provide a solid foundation for shared code projects.

The ANYbotics ANYmal Research C++ Style Guide makes a few minor adaptations to the base Google style in the context of robotics research, and is the recommended reference for LASA.

To further promote readability and usability within LASA, some additional minor changes have been suggested below. Read and adopt the summary below first, then use the ANYbotics guide for everything else.

Use this guide in conjunction with the Git best practices and pull request guidelines and aim for incremental improvement to your code quality and usability with each new commit.

All the greatest code is written one line at a time.

LASA Style Summary

General

Write code in clear, readable, well-scoped blocks; prefer using multiple specific sub-functions in place of one big function. Document your code with a README and inline comments wherever the usage or behaviour is not painfully obvious.

Keep your code portable - don't hardcode file paths, environment variables or OS-specific behaviour if it can be avoided. If your code requires external dependencies to run, make sure you also provide installation and build instructions (CMakeLists, Dockerfile, README, etc).

File name and structure

Source files are *.cpp, header files are *.h. Filenames should be snake_case unless it is declaring / implementing a class, in which case it should match the class name.

An example file structure is shown below. The first include of a source file should be its relevant header file. Following that are built-in standard libraries, then external libraries, then LASA libraries from other modules / packages, and finally other headers from the local package.

For header files, define only the includes necessary for the declarations in that file. Put any further implementation dependencies in the related source file.

All code should be wrapped in a namespace scope where the name in lowercase is easily relatable to your package.

#pragma once

#include "my_package/my_header_file.h"

#include <unistd.h>
#include <vector>

#include <ros/ros.h>
#include <Eigen/Core>

#include <other_lasa_package/Controller.h>

#include "my_package/control_helpers.h"
#include "my_package/typedefs.h"

namespace mypackage {

}

General naming

  • Type names (including enums, structs, classes, typedefs, etc) should be in UpperCamelCase. Virtual classes should use the interface prefix I.
  • All functions and variables, whether static or local, should be in lowerCamelCase, with the following exceptions; class private and protected data members should have an underscore suffix: lowerCamelCase_.
  • When using enums, prefer UPPER_SNAKE_CASE for the members.
enum MyEnum {
  DEFAULT = 0,
  SECOND_CASE,
  OTHER
};

struct MyStruct {
  int publicDataMember;
};

class IMyVirtualClass {
public:
  virtual void myVirtualFunction() = 0;
};

class MyClass : public IMyVirtualClass {
public:
  void myVirtualFunction() override;
  float getPrivateData() const;
  float publicDataMember;
private:
  float privateDataMember_;
};

void myFunction(const MyClass& readonlyReference, MyClass& mutableReference) {
  int myLocalVariable;
}

Format and whitespace

  • Indents are 2 spaces.
  • Conditionals and loops always have brackets, with else if / else statements following the closing bracket on the same line. No spaces inside parentheses.
  • In a for loop, place a space after ;, and on both sides of :.
  • switch statements should define a default case. Only use additional scoping brackets within a case when necessary (e.g. declaring a local variable).
if (condition) {
  foo();
} else if {
  bar();
} else {
  ...
}

while (condition) {
  foo();
}

for (int i = 0; i < 10; ++i) {
  foo();
}

std::vector<SomeType> range(...);
for (auto iter : range) {
  bar();
}

switch (value) {
  case 0:
    foo();
    break;
  case 1: {
    float localVar;
    bar();
    break;
  }
  default:
  case 2:
    foo();
    bar();
    break;
}

Pointers and references in C++ are a great tool, but unfortunately also easy to get lost in. The ownership of the underlying data can be fuzzy and the correct cleaning up. For this reason, in modern C++ the smart pointers have been introduced. They bring much more clarity to your code and come with only a little overhead, for any larger project I advise using those. Find more information on: https://docs.microsoft.com/en-us/cpp/cpp/smart-pointers-modern-cpp?view=msvc-170

Automatic formatting

While it is useful to internalize the rules of this style guide while writing your code, it would be cumbersome and unrealistic to check every line against every rule manually. Fortunately, the majority of the formatting rules can be applied to your code automatically. By configuring your development environment to automatically format your code (for example, format on save), you can ensure that each new addition you make will conform to the lab standard without any effort on your part.

Some files have been provided to help automatically reformat your code according to the LASA style. Use the LASA style directly within your code editor by importing the IntelliJ IDEA code style XML. Alternatively, post-format your code using your editor or the clang-format command-line tool with the clang-format-lasa file.

Follow the setup instructions below and within less than 5 minutes, your favourite code editor will automatically format your code in the future.

(Instructions written for Ubuntu 18.04)

clang-format installation and usage

clang-format is a tool to automatically format C/C++/Objective-C code. Install it on your computer with

sudo apt-get update
sudo apt install clang-format-10

Then, copy or symlink this file into the src folder of your catkin workspace. For example, place it on your computer here:

~/catkin_ws/src/.clang-format

ATTENTION: Do not rename this file! Otherwise it will not be found by clang-format. Now, any file inside your catkin workspace will be formatted according to the LASA C++ style guidelines described above.

You can run clang-format from the command line to format a single file as:

cd ~/catkin_ws/src/
clang-format-10 -i -style=file ./path/to/file.cpp

We do not advise you to use clang-format this way. Instead, find the instructions for your code editor below and let it take care of formatting your files.

CLion

Make the IntelliJ IDEA code style XML file available on your computer by either downloading it separately or cloning into this repository.

In CLion, go to FileSettingsEditorCode Style. Then, under Scheme, click on Show Scheme Actions and then Import Scheme.... Select the file you downloaded and confirm with Ok.

Additionally, go to FileSettingsPlugins and find the plugin called Save Actions in the marketplace. Install this plugin, then go to FileSettingsOther SettingsSave Actions. Check the box Activate save actions on save and then Reformat file. Now, each time you save your file, it will automatically be reformatted. Note that the Save Actions settings are project-specific, so you have to activate the save actions on save for every new project that you open with CLion.

Alternatively, you can check the box Reformat code under Before commit in the VCS commit window if you don't want to use Save Actions.

Visual Studio Code

First, install the C/C++ extension. Then, make sure that the .clang-format file is available in the root directory of your workspace, i.e. after follwoing the instructions above, open VS Code with

cd ~/catkin_ws/src/
code .

To configure the clang-format in VS Code, go to FilePreferencesSettings and start typing clang in the search bar. If you have the C/C++ extension installed and enabled, option called C_Cpp: Clang_format_style and C_Cpp: Formatting will appear. Set their value to file and clangFormat, respectively.

Additionally, still in the settings, type save in the search bar. Check the box Editor: Format On Save such that the files are automatically formatted on save. Change the option Editor: Format On Save Mode to file.

Finally, if you have used a different formatting tool before, go to any C++ file, right click in the editor, select Format Document With..., then select Configure Default Formatter and choose C/C++.

Sublime Text

If you haven't already done so, install the Package Manager the usual way. Then, open the Command Palette with Ctrl + Shift + P, choose Package Control: Install Package, and install the package named Clang Format.

Then, go to PreferencesPackage SettingsClang FormatCustom Style - User. Copy and paste the content of this file.

Finally, go to PreferencesPackage SettingsClang FormatSettings - User and make sure that the file looks like this:

{
    "binary": "clang-format-10",
    "style": "Custom",
    "format_on_save": true,
    "languages": ["C++", "C++11"]
}

Authors / Maintainers

⚠️ **GitHub.com Fallback** ⚠️