Style Guide for Orxonox C++ Code
Table of Contents
'Coding style' refers to the way source code is formatted. For C++, this involves things like brace placement, indentation, and the way parentheses are used. The most important thing is for the code to be consistent within a program or library - code with sloppy formatting is not acceptable, since it is hard to read.
When writing a new program or library, please follow a consistent style of brace placement and indentation. We recommend the following code style.
Remember that any violation to the guide of course is allowed if it enhances readability.
General
this
Address all member variables and functions with the this→ operator:
this->processData(this->data_);
const
Use const when it's possible. This includes variables and function declaration. Specially for functions, const is very important. Use it always if a function doesn't change the object.
SomeClass.h: int getSomeNumber() const; SomeClass.cc: int SomeClass::getSomeNumber() const { return myValue_; }
Read this for more information.
Reference
Use pass-by-reference over pass-by-value when possible:
public: void setPosition(const Vector3& position); const Vector3& getPosition() const; private: Vector3 myPosition_;
Read this for more information.
Initialization
- All class variables should be initialized to a sane value in the constructor.
- Prefer initialization to assignment in constructors.
- Initialize pointers with 0 or 'new ClassName'.
Pointer
- When a member value pointer is deleted, it should be set to 0.
- Don't forget to delete objects in the destructor if your class is responsible for the object.
virtual
- If a base class has virtual functions, derived classes should have the "virtual" keyword repeated for those functions.
- Don't use virtual functions in the constructor of the same class
Files
General
Keep your classes/files short, don't exceed 2000 LOC (if it get's longer you may separate functions into different modules). Put every class in a separate file and name the file like the class name (use CamelCase names). Create a separate header (ends with .h) and source file (ends with .cc). Example for class MyExampleClass.
file names: MyExampleClass.cc MyExampleClass.h
Headers
Try to minimize header file dependencies by making use of forward declaration and careful dependency studies. Whenever a class member is just a pointer or reference, the forward declaration is sufficient. To make that easier there exists a prerequisites file for each library, containing all the necessary forward declarations. These have to be kept up to date of course. Example for a header file include sequence.
#include "MyLibraryPrereqs.h" #include <string> // std includes #include <OgrePrerequisites.h> // most external libraries have forward declaration files as well #include "OrxonoxClass.h" // only include this if you inherit from it or you don't use an object as pointer/reference
Note: Never ever (!!!) write using namespace foo; in header files! (think about what that would mean)
Source files
Again, keep the dependencies short. However it is not that severe in source files.
To ensure that every header file can be compiled without additional header dependencies, include the class header file first.
Include sequence is for MyClass:
#include "MyClass.h" #include <vector> // std headers #include <map> #include <OgreSceneManager.h> // external library headers #include "BaseObject.h" // our own header files
Namespaces
Use namespaces for separate libraries like network, 'orxonox' otherwise. Names representing namespaces should be all lowercase.
Don't write "using namespace ..;" in header files because otherwise if someone included that header file, the "using namespace .." would automatically be included as well. That may lead to unwanted errors.
Inlining and Templates
Inline functions and template structures should be declared in a .h header file as described above and implemented below the class declaration if the definition exceeds one line.
Using the 'inline' keyword within the class declaration doesn't give advantages (it's inline anyway), so let it be. It is necessary outside however.
An Example:
class FooBar { void funcA() { return this->myVar_; } void funcB(); }; inline void FooBar::funcB() { doSomething(); doOtherStuff(); }
Machine-Dependent Code
Place machine-dependent code in a special file so that it may be easily located when porting code from one machine to another.
Include Guard
Header files must contain an include guard.
#ifndef _ClassName_H__ #define _ClassName_H__ #endif /* _ClassName_H__ */
Comments
Comment your code and add Doxygen Comments to all files, classes, structs, functions and member variables.
Read this to learn more about Doxygen.
Naming Conventions
Classes and Types
Names representing types must be in mixed case (CamelCase) starting with upper case.
class SavingsAccount { }; struct SimpleStruct { };
Variables
Variable names must be in mixed case starting with lower case. Each variable declaration on a new line. Avoid abbreviations in names.
string testString; int numberOfTimes;
Memeber variables of classes all end with an underline:
class TestClass { private: int numberOfTimes_; };
Static variables of classes have to end with '_s':
class TestClass2 { protected: static float someList_s; };
Use useful names:
// wrong ProgressBar *prbar; string prtxt, errstr; // correct ProgressBar* downloadProgressBar; string progressText; string errorString;
Comment: Apart from its name and its type, the scope of a variable is its most important feature. Indicating class scope by using underscore makes it easy to distinguish class variables from local scratch variables. This is important because class variables are considered to have higher significance than method variables, and should be treated with special care by the programmer.
Constants
Named constants (including enumeration values) must be all uppercase using underscore to separate words. Always use constants instead of numbers and macros.
const int MAX_ITERATIONS = 5; for (int i = 0; i < MAX_ITERATIONS; i++) { } // Instead of (... i < 5; ...)
Functions
Names representing methods or functions must be verbs and written in mixed case starting with lower case. Avoid abbreviations in names.
void calculateTheLowerBoundary() {} int howManyItemsLeft() {}
For interface functions to local member variables use short functions (setter and getter functions):
class WorldEntity { public: std::string getName() { return name_; } void setName(const std::string& name) { name_ = name; } private: std::string name_; };
The name of the object is implicit, and should be avoided in a method name.
class Line { public: float getLength(); // Not getLineLength() since it's clear that it is a line. };
Namespaces
Names representing namespaces should be all lowercase and consist of one word.
namespace util { }
Enumerations
Enumerations should either be put in class scope or within a separate namespace beginning with a capital letter. Use 'Value' as the name of the enum in the latter case. The members of the enum should be written in UpperCamelCase.
class MyClass { public: enum Colour { Red, Green, Blue }; }; // Or better this way namespace Colour { enum Value { Red, Green, Blue }; }
Indentation
Spaces
Use 4 spaces for an indentation level. Never use tabs, as it is common in windows IDEs.
Split Lines
The incompleteness of split lines must be made obvious:
totalSum = a + b + c + d + e; function(param1, param2, param3); setText("Long line split" "into two parts."); for (int tableNo = 0; tableNo < nTables; tableNo += tableStep) { ... }
Whitespaces
Use one space after each keyword and enclose operators with 2 of them: if (a + b < c)
Include Statements
Include statements should be sorted and grouped. Sorted by their hierarchical position in the system with low level files included first. Leave an empty line between groups of include statements.
#include <fstream> #include <iomanip> #include <qt/qbutton.h> #include <qt/qtextfield.h> #include "com/company/ui/PropertiesDialog.h" #include "com/company/ui/MainWindow.h"
Constants
The use of magic numbers in the code should be avoided. Numbers other than 0 and 1 should be considered declared as named constants instead.
Statements
Access Modifiers for Classes
The parts of a class must be sorted public, protected and private. All sections must be identified explicitly. Not applicable sections should be left out.
Type Casts
Type conversions must always be done explicitly. Never rely on implicit type conversion.
floatValue = static_cast<float>(intValue); // NOT: floatValue = intValue; // or: floatValue = float(intValue);
By this, the programmer indicates that he is aware of the different types involved and that the mix is intentional.
Note: Never, ever use static_cast or C-Style cast when dealing with multiple or virtual inheritance!
Data Encapsulation
Class variables should never be declared public. The concept of C++ information hiding and encapsulation is violated by public variables. Use private variables and access functions instead. One exception to this rule is when the class is essentially a data structure, with no behavior (equivalent to a C struct). In this case it is appropriate to make the class' instance variables public.
Loops
Only loop control statements must be included in the for() construction.
Layout
Indentation
Basic indentation should be 4.
for (i = 0; i < numberOfElements; ++i) a[i] = 0;
Block Layout
Block layout should be as illustrated in this example:
while (!done) { doSomething(); done = moreToDo(); }
Class Declaration
The class declarations should have the following form:
class SomeClass : public BaseClass { public: void functionBla(); protected: ... private: ... };
Function Definitions
Method definitions should have the following form:
void functionBla() { ... }
If-Statement
The if-else class of statements should have the following form:
if (condition) { ... } else { ... }
Never use the token below as it does not, what you expect:
if (condition1) if (condition2) doSomething(); else // WRONG! This else corresponds to the second if! doSomethingElse();
Special Implementations
Use STL Libraries
Use STL (standard template library) implementations of common abstract data structures like lists, arrays, stacks, queues, never implement your own!
Compiling
- different build targets possible (for subprojects)
- different make options (make-dbg, make-opt)
- unit test supported (in debug mode)
- compiler mode: warnings being treated as errors
Code Reviews
Code review is a pretty simple process: at its heart, it is little more than reading some code written by someone else. Nevertheless, it can be useful to have a set of things on which to focus during a review:
- Does the branch merge or the diff apply cleanly?
- Are there unit tests for the code being changed or added?
- Do the unit tests pass for you?
- Do the unit tests pass for buildbot?
- Is there documentation for new code?
- Where appropriate, has existing documentation been updated (including ChangeLog?/NEWS files)?
- Does the code adhere to the coding standard?
There's the easy list. Most are mechanical checks. Don't feel bad about rejecting a branch if the answer to any of these questions is no: the problems may seem minor in isolation, but each contributes to overall poor code quality. Moreover, sometimes apparently minor problems can be hiding larger issues.