-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
CPP Coding Style
Table of contents:
- Tools
- Naming Conventions
- Specific Naming Conventions
- Files
- Source Files
- Include Files and Include Statements
- Statements
- Types
- Inheritance
- Variables
- Loops
- Conditionals
- Functions
- Enums
- Miscellaneous
- White Space
- Comments
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
Names representing types must be in PascalCase
notation: mixed case starting with upper case.
Line, SavingsAccount
C++ Variable names must be in lowerCamelCase
: camel case starting with lower case.
int line;
SavingsAccount savingsAccount;
private:
int mLineSize;
SavingsAccount mSavingsAccount;
private:
static int cLineSize;
static SavingsAccount cSavingsAccount;
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 };
QString name();
double computeTotalWidth();
template<class T> ...
template<class C, class D> ...
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.
line.length(); // NOT: line.lineLength();
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);
valueSet->computeAverage();
matrix->computeInverse();
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();
mainWindow, propertiesDialog, widthScale, loginText, leftScrollbar, mainForm, fileMenu, minLabel, exitButton, yesToggle etc.
vector<Point> points;
int values[];
nPoints, nLines
for (int i = 0; i < nTables); ++i) {
:
}
vector<MyClass>::iterator i;
for (i = list.begin(); i != list.end(); ++i) {
Element element = *i;
:
}
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();
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.
bool hasErrors; // NOT: hasNoError
bool isFound; // NOT: isNotFound
bool isRunning() // NOT: isNotRunning()
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 {
:
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; }
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.
#ifndef CLASS_NAME_HPP
#define CLASS_NAME_HPP
:
#endif
#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>
#include "MyOtherClass.hpp" // NO
class MyOtherClass; // YES
#CS335 The parts of a class must be sorted public, protected and private. All sections must be identified explicitly.
class MyClass {
public:
...
protected:
...
private:
...
}
#CS338 Use virtual in front of a derived function declaration if the base class function was declared virtual.
class DerivedClass : public BaseClass {
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.
WbVector3 u(0.0, 1.0, 0.0);
WbVector3 v(u); // YES
WbVector3 w = u; // the very same thing as above, but NO
// 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;
if (isFinished()) {
:
}
// YES
if (isFinished()) ...
if (!isOpen()) ...
// NO
if (isFinished() == true) ...
if (isOpen() == false) ...
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.
while (true) {
:
}
if (condition) {
statements;
}
if (condition) {
statements;
} else {
statements;
}
if (condition) {
statements;
} else if (condition) {
statements;
} else {
statements;
}
for (initialization; condition; update) {
statements;
}
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;
// GOOD
File *fileHandle = open(fileName, "w");
if (!fileHandle) {
:
}
// BAD
if (!(fileHandle = open(fileName, "w"))) {
:
}
#CS363 Write small and focused functions: the body of a function should not exceed one page (no scrolling).
#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);
vector<MyClass>::iterator i;
for (i = list.begin(); i != list.end(); ++i) { % NOT i++
Element element = *i;
:
}
enum {
A, // implicitly 0
B, // implicitly 1
C = 12,
D // implicitly 12
}
double total = 0.0; // NOT: double total = 0;
double speed = 3.0e8; // NOT: double speed = 3e8;
double sum = (a + b) * 10.0;
if (p == NULL && fabs(a) == 0.0 && size == 0)
char c = '\0';
A *a = (A*)b; // NO
A *a = static_cast<A*>(b); // YES
A *a = dynamic_cast<A*>(b);
if (a)
doSomethingWithA(a);
- To define mathematical classes, and only if the operators supports a known math syntax
- To inter-operate with streams
- 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;
#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++){ ...
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);
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);
}
class MyClass: public BaseClass {
public:
MyClass();
virtual ~MyClass();
void setData(Data *data);
private:
Data *mData;
}
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();
}