Skip to content

CPP Coding Style

CoolSpy3 edited this page Jul 19, 2024 · 23 revisions

Table of contents:


Tools

#CS300 Cyberbotics clang-format style compliance

In order to facilitate the respect of our coding style, developer should respect the style defined in our clang format configuration file which gets copied by the Makefile at the root level as $WEBOTS_HOME/.clang-format. clang-format may be used with the Atom text editor by installing the atom-beautify package (apm install atom-beautify) and setting clang-format as the default Beautifier for C and C++ languages. Note that no warnings are printed and the beautify has to be executed manually or you should enable the apply on save options.

On Ubuntu 20.04, clang-format 14 can be installed from APT:

sudo apt remove clang-format clang-format-10 clang-format-11 ... # if needed
sudo apt install clang-format-14
sudo ln -s /usr/bin/clang-format-14 /usr/bin/clang-format

It may be needed to patch atom beautify with this: https://github.com/Glavin001/atom-beautify/issues/2290

On macOS, simply type:

brew install clang-format
apm install clang-format

... and to set clang-format into the Atom / Preferences / Packages / Clang Format / Settings / Executable.

cppcheck is also very useful to check for both the style and possible programming errors. The latest version is available from http://cppcheck.sourceforge.net.

On Ubuntu you have to install cppcheck from sources to get the latest version. Extract the package and type:

make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes
sudo make install FILESDIR=/usr/share/cppcheck/

On Windows, the latest versions of cppcheck and clang-format are available in MSYS64 packages and are installed by the MSYS64 installer script: msys64_installer.sh --dev

On macOS, simply type:

brew install cppcheck
apm install linter-cppcheck

Unfortunately, the linter-cppcheck atom package seems to be currently broken and unsupported... cppcheck is used in the CI sources tests with the following options:

  • Enable Information
  • Enable Missing Include
  • Enable Performance
  • Enable Portability
  • Enable Style
  • Enable Warning
  • Inline Suppressions

Naming Conventions

#CS301 Pascal case for type names

Names representing types must be in PascalCase notation: mixed case starting with upper case.

Line, SavingsAccount

#CS302 Lower camel case for variable names

C++ Variable names must be in lowerCamelCase: camel case starting with lower case.

int line;
SavingsAccount savingsAccount;

#CS303 Non-static private class variables should have the "m" prefix.

private:
  int mLineSize;
  SavingsAccount mSavingsAccount;

#CS304: Static private class variables should have the "c" prefix.

private:
  static int cLineSize;
  static SavingsAccount cSavingsAccount;

#CS305: Static variables (non-class members) should have the "g" prefix.

static int gLine;

#CS305 Global constant names (including enum values) must be all uppercase using underscore to separate words.

static const int MAX_ITERATIONS;
enum { RED, GREEN, BLUE };

#CS306 Names representing methods or functions must be written in lower camel case.

QString name();
double computeTotalWidth();

#CS307 Names representing template types should be a single uppercase letter.

template<class T> ...
template<class C, class D> ...

#CS308 Generic variables should have the same name as their type.

void setTopic(Topic *topic) 
// NOT: void setTopic(Topic *value) 
// NOT: void setTopic(Topic *aTopic) 
// NOT: void setTopic(Topic *t)

void connect(Database *database)
// NOT: void connect(Database *db)
// NOT: void connect (Database *oracleDB)

#CS308 Variables with a large scope should have long names, variables with a small scope can have shorter names.

#CS309 The name of the object is implicit, and should be avoided in a method name.

line.length(); // NOT: line.lineLength();

Specific Naming Conventions

#CS311 The term set must be used where an attribute is changed directly.

employee.setName(name);
matrix.setElement(2, 4, value);

#CS312 Methods that return a value should be named after the value they return (without get prefix).

employee.name();  // NOT employee.getName();
matrix.element(2, 4);  // NOT matrix.getElement(2, 4);

#CS313 The compute prefix should be used in methods where something (complex) is computed.

valueSet->computeAverage();
matrix->computeInverse();

#CS314 The find prefix should be used in methods where something is looked up.

vertex.findNearestVertex();
matrix.findMinElement();

#CS315 The init prefix should be used where an object or a concept is established and match with a corresponding cleanup prefix.

printer.initFontSet();
:
printer.cleanupFontSet();

#CS316 Variables representing GUI components should be suffixed by the component type name.

mainWindow, propertiesDialog, widthScale, loginText, leftScrollbar, mainForm, fileMenu, minLabel, exitButton, yesToggle etc.

#CS317 Plural form should be used on names representing a collection of objects.

vector<Point> points;
int values[];

#CS318 The n prefix should be used for variables representing a number of objects.

nPoints, nLines

#CS319 Iterator variables should be called i, j, k etc.

for (int i = 0; i < nTables); ++i) {
  :
}

vector<MyClass>::iterator i;
for (i = list.begin(); i != list.end(); ++i) {
  Element element = *i;
  :
}

#CS320 The is prefix should be used for boolean variables and methods.

bool isSet() const { return mIsSet; }
bool isVisible() const;
bool isFinished() const;

private:
bool mIsFound;
bool mIsOpen;

// There are a few alternatives to the `is` prefix that fit better in some situations. These are the `has`, `can` and `should` prefixes: 
bool hasLicense();
bool canEvaluate();
bool shouldSort();

#CS321 Complement names must be used for complement operations.

init/cleanup, add/remove, create/destroy, start/stop, insert/delete, increment/decrement, old/new, begin/end, first/last, up/down, min/max, next/previous, old/new, open/close, show/hide, suspend/resume, etc.

#CS322 Negated boolean names must be avoided.

bool hasErrors;   // NOT: hasNoError
bool isFound;     // NOT: isNotFound
bool isRunning()  // NOT: isNotRunning()

Files

#CS324 Use pascal case for source files

C++ header files should have the .hpp extension. C++ source files should have the .cpp extension.

MyClass.cpp, MyClass.hpp

#CS325 A class should be declared in a header file and defined in a source file where the name of the files match the name of the class.

MyClass.hpp:

class MyClass {
 :

#CS326 Inline simple methods

Inlining should be used for methods that can be written in one line or so (typically getters and setters), if no additional include is required.

class MyClass {
  void setValue(Value value)  { mValue = value; }
  Value value() const         { return mValue; }

#CS327 Inline performance critical methods

For optimization reasons, longer methods can also be inlined, in this case the body of the method must be placed in the header file, just below the class declaration. Remember to avoid early optimization.

Include Files and Include Statements

#CS329 Header files must contain an include guard

#ifndef CLASS_NAME_HPP
#define CLASS_NAME_HPP
 :
#endif

#CS330 Sort #include statements in header blocks

#include statements in a header or a source file should respect the following order, seperated by an empty line:

  • the most meaningful header comes first, followed by an empty line; usually we include "MyClass.hpp" at the top of MyClass.cpp text
  • Webots headers come second in alphabetical order
  • Qt headers in alphabetical order
  • Ogre headers in alphabetical order
  • ODE headers in alphabetical order
  • standard headers come last in alphabetical order
%From  WbTriangleMesh.cpp

#include "WbTriangleMesh.hpp"

#include "WbBox.hpp"
#include "WbMFInt.hpp"
#include "WbMFVector2.hpp"
#include "WbMFVector3.hpp"
#include "WbRay.hpp"
#include "WbTesselator.hpp"

#include <limits>
#include <cassert>

#CS331 Don't use an #include when a forward declaration would suffice

#include "MyOtherClass.hpp"  // NO

class MyOtherClass;  // YES

Statements

#CS333 Types that are local to one file only can be declared inside that file.

#CS334 Use a struct only for passive objects that carry data; everything else is a class.

#CS335 The parts of a class must be sorted public, protected and private. All sections must be identified explicitly.

class MyClass {
public:
  ...
protected:
  ...
private:
  ...
}

Inheritance

#CS337 Do not change the public/protected/private status of a method in derived classes.

#CS338 Use virtual in front of a derived function declaration if the base class function was declared virtual.

#CS339 Always declare destructors as virtual except in classes that are not meant to be derived.

#CS340 Declare a function virtual only if it is going to be used polymorphically.

#CS341 Use public inheritance exclusively.

class DerivedClass : public BaseClass {

Variables

#CS343 Variables should be initialized where they are declared.

QString s = "abc"; 

int x, y, z;
computeCenter(&x, &y, &z);

if (x > z) {
  int w = x * z;
  :
}

#CS344 Member variables should be initialized in the constructor initialization list provided it doesn't yield code duplication in other constructors.

MyClass::MyClass() : mMySize(10), mMyObject1("obj1"), mMyObject2("obj2") {
  % non trivial initialization;
}

#CS345 Place a function's variables in the narrowest scope possible (block, function, class), and initialize variables in the declaration.

int i;
i = f();      // BAD - initialization separate from declaration.

int j = g();  // GOOD - declaration has initialization.

#CS346 Do not use global variables, use singletons or static methods instead.

#CS347 Use the copy constructor for an object definition

WbVector3 u(0.0, 1.0, 0.0);
WbVector3 v(u); // YES
WbVector3 w = u; // the very same thing as above, but NO 

#CS348 Class variables should never be declared public. Use getters and setters instead.

// BAD!
class MyClass {
public:
  int numEmployee;
}

// GOOD
class MyClass {
public:
  void setNumberOfEmployees();
  int numberOfEmployees() const;

private:
  int mNumberOfEmployee;
}

#CS349 C++ pointers and references should have their reference symbol next to the name rather than to the type.

float *x, *y, *z;  // NOT: float* x, y, z;
int &y;            // NOT: int& y;

#CS350 Implicit test for 0 should not be used other than for boolean and pointer variables.

if (isFinished()) {
  :
}

#CS351 Do not test boolean expressions against true or false

// YES
if (isFinished()) ...
if (!isOpen()) ...

// NO
if (isFinished() == true) ...
if (isOpen() == false) ...

Loops

#CS353 Loop variables should be initialized immediately before the loop.

bool isDone = false;
while (!isDone) {
  :
}

#CS354 The use of break and continue in loops should only be used if they give higher readability than their structured counterparts.

#CS355 The while (true) form should be used for infinite loops.

while (true) {
  :
} 

Conditionals

#CS357 The if-else class of statements should have the following form:

if (condition) {
  statements;
}

if (condition) {
  statements;
} else {
  statements;
}

if (condition) {
  statements;
} else if (condition) {
  statements;
} else {
  statements;
}

#CS358 A for statement should have the following form:

for (initialization; condition; update) {
  statements;
}

#CS359 A while statement should have the following form:

while (condition) {
  statements;
}

#CS360 Single if, while or for statement must be written without brackets. The statement part should be put on a separate line. In case of nested block keep brackets.

// BAD:
if (condition)
  for (initialization; condition; update)
    statement;

if (condition) statement; 

// GOOD:
if (condition) {
  for (initialization; condition; update)
    statement;
}

if (condition)
  statement;

while (condition)
  statement;

for (initialization; condition; update)
  statement;

#CS361 Assignment in conditionals must be avoided.

// GOOD
File *fileHandle = open(fileName, "w");
if (!fileHandle) {
  :
}

// BAD
if (!(fileHandle = open(fileName, "w"))) {
  :
} 

Functions

#CS363 Write small and focused functions: the body of a function should not exceed one page (no scrolling).

#CS364 High-level and low-level code should not be mixed in a same function.

#CS365 When defining a function, parameter order is: inputs, then outputs. If a function returns only one value use the return type.

void computePoints(const Line &line, Point &p1, Point &p2);
Point computePoint(const Line &line1, const Line &line2);

#CS366 Prefer pre-incrementation over post-incrementation whenever possible.

vector<MyClass>::iterator i;
for (i = list.begin(); i != list.end(); ++i) { % NOT i++
  Element element = *i;
  :
}

Enums

#CS367 Prefer implicit initialisation.

enum {
  A, // implicitly 0
  B, // implicitly 1
  C = 12,
  D // implicitly 12
}

Miscellaneous

#CS368 Prefer int data type for integer numbers, including unsigned integer numbers.

#CS369 Prefer double data type for floating point numbers.

#CS370 Floating point constants should be written with decimal point and at least one decimal.

double total = 0.0; // NOT: double total = 0;
double speed = 3.0e8; // NOT: double speed = 3e8;

double sum = (a + b) * 10.0;

#CS371 Use 0 for integers, 0.0 for reals, NULL for pointers, and '\0' for chars.

if (p == NULL && fabs(a) == 0.0 && size == 0)
  char c = '\0';

#CS372 Use C++ static_cast<>() instead of C style casts.

A *a = (A*)b;  // NO
A *a = static_cast<A*>(b);  // YES

#CS373 Use C++ dynamic_cast<>() to test the run-time type of an object.

A *a = dynamic_cast<A*>(b);
if (a)
  doSomethingWithA(a);

#CS374 Do never use the friend keyword (but for operator overloading).

#CS375 Avoid defining macros at all costs.

#CS376 Avoid operator overloading, except in these two cases:

  • To define mathematical classes, and only if the operators supports a known math syntax
  • To inter-operate with streams

#CS377 Use const whenever it makes sense to do so, e.g.,:

  • Declare const any method that does not change a variable of its class.
  • Declare const any pointer or reference parameter that will not be altered by a function.
  • Declare const any variable which value will not change.
double computeTotal(const double v[], int n) const;
const double size = 4.5;

White Space

#CS379 Special characters like Tab and page break must be avoided.

#CS380 Basic indentation should be 2 spaces.

#CS381 Conventional operators should be surrounded by a space character. Commas and semi-colons (in for loops) should be followed by a white space.

a = (b + c) * d; // NOT: a=(b+c)*d
doSomething(a, b, c, d); // NOT: doSomething(a,b,c,d);
for (i = 0; i < 10; ++i) { // NOT: for(i=0;i<10;i++){ ... 

#CS382 Logical units within a block should be separated by one blank line.

Matrix4x4 matrix = new Matrix4x4();

double cosAngle = Math.cos(angle);
double sinAngle = Math.sin(angle);

matrix.setElement(1, 1, cosAngle);
matrix.setElement(1, 2, sinAngle);
matrix.setElement(2, 1, -sinAngle); 
matrix.setElement(2, 2, cosAngle);

multiply(matrix); 

#CS383 Use alignment wherever it enhances readability.

Comments

#CS385 Use // for all comments, including multi-line comments.

#CS386 Use /**/ only for temporary debugging purposes.

#CS387 Follow the standard indentation for a switch statement.

switch(choice) {
case 0: printf("Good choice!\n"); break;
case 1: printf("Not optimal, you can do better\n"); break;
case 2: printf("Please give it a second thought\n"); break;
default: assert(false);
}

#CS388 Follow the standard indentation for a class declaration

class MyClass: public BaseClass {

public:
  MyClass();
  virtual ~MyClass();
  void setData(Data *data);

private:
  Data *mData;
}

#CS389 In a class declaration, order privacy levels, signals and slots, the following way:

class MyClass: public QObject {
  Q_OBJECT
public:
  MyClass();
  virtual ~MyClass();
  void setData(Data *data);

signals:
  dataChanged();

protected:
  formatData();

protected slots:
  updateData();
  
private:
  checkDataValidity();
  Data *mData;
  Picture *mPicture;

private slots:
  showPicture();
}
Clone this wiki locally