C++ coding¶
Source and files¶
- Rule 4 : Files tree
- Source files must be placed in a folder
src/
. Public header files must be placed in a folderinclude/
. Private headers may be placed in a different location.
- Rule 5 : Files hierarchy
- The file hierarchy should follow the namespace hierarchy. For instance, the implementation of a class
::ns1::ns2::SService
should be put insrc/ns1/ns2/SService.cpp
.
- Rule 6 : Files extensions
Header files use the extension
.hpp
.Implementation files use the extension
.cpp
.Files containing implementation of “template” classes use the extension
.hxx
.
- Recommendation 2 : Only one class per file
- It is recommended to declare (or to implement) only one class per file. However tiny classes may be declared inside the same file.
- Rule 7 : Includes
- Use the right include directive depending on the context.
#include "..."
must be used to import headers from the same module, whereas#include <...>
must be used to import headers from other modules.
- Rule 8 : Include path
- The include path is not an absolute path depending on a local file system. A correct include path does respect the letter case of the filenames and folders (since some platforms require it) and uses the character ‘/’ as a separator.
- Rule 9 : Protection against multiple inclusions
You must protect your files against multiple inclusions. To this end, use
#pragma once
.#pragma once
- Recommendation 3 : Independent headers
A header should compile alone. All necessary includes should be contained inside the header itself. In the following sample :
// Header.hpp class Foo { public: std::string m_string; }
you will be forced to include the file in this way to get a successful build :
// Source.hpp #include <string> #include "Header.hpp"
This is a bad practice, the header should rather be written :
// Header.hpp #include <string> // Header.hpp class Foo { public: std::string m_string; }
So that people can simply include the header :
// Source.hpp #include "Header.hpp"
- Recommendation 4 : Minimize inclusions
- Try to minimize as much as possible inclusions inside a header file. Include only what you use. Use forward declarations when you can (i.e. a type or class structure is not referenced inside the header). This will limit dependency between files and reduce compile time. Hiding the implementation can also help to minimize inclusions (see Hide implementation)
- Rule 10 : Sort headers inclusions
You must sort headers in the following order : same module, framework libraries, bundles, external libraries, standard library. This way, this helps to make each header independent. The rule can be broken if a different include order is necessary to get a successful build.
#include "currentModule.hpp" #include <libSampleB/second.hpp> #include <libSampleA/first.hpp> #include <libSampleB/subModule/first.hpp> #include <Qt/QtGui> #include <vector> #include <map>
- Recommendation 5 : Sort inclusions alphanumerically
In addition to the previous sort, you may sort includes in alphanumerical order, according to the whole path. Thus they will be grouped by module. For a better readability, an empty line can be added between each module.
#include "currentModule.hpp" #include <libSampleA/first.hpp> #include <libSampleB/second.hpp> #include <libSampleB/subModule/first.hpp> #include <libSampleB/subModule/second.hpp> #include <Qt/QtGui> #include <map> #include <vector>
Naming conventions¶
- Rule 11 : Class
- Class names must be written in upper camel case. It should not repeat a namespace name. For instance
::editor::SCustomEditor
should be rather called::editor::SCustom
.
- Rule 12 : File
- The name of the file should be based on the class name defined in it. It must follow the same letter case.
- Rule 13 : Namespace
Namespaces must be written in camel case. A comment quoting the namespace must be placed next to the ending ‘}’.
namespace namespaceA { namespace namespaceB { class Sample { ... }; } // namespace namespaceB } // namespace namespaceA
When referring a namespace, you must put
::
if this is a root namespace, with an exception forstd
namespace. Ex:::boost::filesystem
.
- Rule 14 : Function and method names
- Functions and methods names must be written in camel case.
- Recommendation 6 : Correct naming of functions
- Try as much as possible to help the users of your code by using comprehensive names. You may for instance help them to indicate the cost of a function. A function that executes a search to retrieve an object must not be called like a getter. In this case, it is better to call it
findObjet()
instead ofgetObject()
.
- Rule 15 : Variable
Variable names must be written in camel case. Members of a class are prefixed with a
m_
.class SampleClass { private: int m_identifier; float m_value; };
Static variables are prefixed with a
s_
.static int s_staticVar;
- Rule 16 : Constant
Static constant variables must be written in snake_case but in capitals, and follow the previous rule.
class SampleClass { static const int s_AAA_BBB_CCC_VALUE = 1; };
- Rule 17 : Type
Type names, like classes, must be written in upper camel case.
typedef int CustomType; typedef vector<int> CustomContainer;
- Rule 18 : Template parameter
Template parameters must be written in capitals. In addition, they must be short and explicit.
template< class KEY, class VALUE > class SampleClass { ... };
- Rule 19 : Macro
Macros without parameters must be written in capitals. On the contrary, there is no specific rule on macros with parameters.
#define CUSTOM_FLAG_A 1 #define CUSTOM_FLAG_B 1 #define CUSTOM_MACRO_A( x ) x #define Custom_Macro_B( x ) x #define custom_Macro_C( x ) x #define custom_macro_d( x ) x
- Rule 20 : Enumerated type
An enumerated type name must be written in upper camel case. Labels must be written in capitals. If a
typedef
is defined, it follows the upper camel case standard.typedef enum SampleEnum { LABEL_1, LABEL_2 ... } SampleEnumType;
- Rule 21 : Service
- A service implementation is identified by a
S
at the beginning of the class name. Example :SCustomEditor
. A service interface is identified by aI
at the beginning of the class name. Example :IEditor
.
- Rule 22 : Signal
A signal name must be prefixed with
sig
. It should be suffixed by a past action (ex: Updated, Triggered, Cancelled, CakeCookedAndBaked). It follows other common variable naming rules (member of a class, etc…).class Sample { SigType::sptr m_sigImageDisplayed; };
- Rule 23 : Slot
A slot name must be prefixed with
slot
. It should be suffixed by an imperative order (Ex: Update, Run, Detach, Deliver, OpenWebBrowser, GoToFail). It follows other common variable naming rules (member of a class, etc…).class Sample { SlotType::sptr m_slotDisplayImage; }
Coding rules¶
Blocks¶
- Rule 24 : Indentation
Code block indentation and bracket positioning follow the Allman style.
void function(void) { if(x == y) { something1(); something2(); } else { somethingElse1(); somethingElse2(); } finalThing(); }
- Rule 25 : Indentation of namespaces
Namespaces are an exception of the previous rule. They should not be indented.
namespace namespaceA { namespace namespaceB { ... } // namespace namespaceB } // namespace namespaceA
- Rule 26 : Blocks are mandatory
- After a control statement (if, else, for, while/do…while, try/catch, switch, foreach, etc…), it is mandatory to open a block, whatever is the number of instructions inside the block.
- Rule 27 : Scope
The keywords
public
,protected
andprivate
are not indented, they should be aligned with the keywordclass
.class Sample { public: ... private: ... };
Class declaration¶
- Recommendation 7 : Only three scope sections
- When possible, use only one section of each scope type
public
,protected
andprivate
. They must be declared in this order.
- Recommendation 8 : Group class members by type
- You may group class members in each scope according to their type: type definitions, constructors, destructor, operators, variables, functions.
- Rule 28 : Hide implementation
- Avoid non-const public member variables except in very small classes (i.e. a 3D point). The Pimpl idiom may also be helpful to separate the implementation from the declaration.
- Recommendation 9 : Hide implementation
- Try to put variables as much as possible in the
private
section.
- Rule 29 : Accessors
- Since you protect your member variables from the outside, you will have to write accessors, named
getXXX()
andsetXXX()
. Getters are alwaysconst
.
- Rule 30 : Template class function definition
The function definition of a template class must be defined after the declaration of the class.
template < typename TYPE > class Sample { public: void function(int i); }; template < typename TYPE > inline Sample<TYPE>::function(int i) { ... }
- Recommendation 10 : Separate template class function definition
In addition of the previous rule, you may put the definition of the function in a
.hxx
file. This file will be included in the implementation file right after the header file (the compile time will be reduced comparing with an inclusion of the.hxx
in the header file itself).#include <namespaceA/file.hpp> #include <namespaceA/file.hxx>
Initializer list¶
- Rule 31 : One initializer per line
In a class constructor, use the initialization list as much as possible. Place one initializer per line. Constructors of base classes should be placed first, followed by member variables. Do not specify an initializer if it is the default one (empty std::string for instance).
SampleClass::SampleClass( const std::string& name, const int value ) : BaseClassOne( name ), BaseClassTwo( name ), m_value( value ), m_misc( 10 ) {}
- Recommendation 11 : Align everything that improves readability
To improve readability, you may align members on one hand and argument lists on the other hand.
SampleClass::SampleClass( const std::string& name, const int value ) : BaseClassOne ( name ), BaseClassTwo ( name ), m_value ( value ), m_misc ( 10 ) {}
Functions¶
- Rule 32 : Constant reference
Whenever possible, use constant references to pass arguments of non-primitive types. This avoids useless and expensive copies.
void badFunction( std::vector<int> array ) { ... } void goodFunction( const std::vector<int>& array ) { ... }
- Recommendation 12 : Constant reference for shared pointers
- For performance sake, it is preferable to use
const&
to pass arguments of type::boost::shared_ptr
. It is only useful to pass the pointer by copy if the pointer can be invalidated by an another thread during the function call. If you have any doubt, it is safer to pass the argument by copy.
- Rule 33 : Constant functions
Whenever a member function should not modify an attribute of a class, it must be declared as
const
.void readOnlyFunction( const std::vector<int>& array ) const { ... }
- Recommendation 13 : Limit use of expression in arguments
When passing arguments, try to limit the use of expressions to the minimum.
// This is bad function( fn1(val1 + val2 / 4 ), fn2( fn3( val3 ), val4) ); // This is better const float res0 = val1 + val2 / 4; const float res1 = fn1(res0); const float res3 = fn3(val3); const float res2 = fn2(res3, val4); function( res1 , res2 );
Miscellaneous¶
- Rule 34 : Enumerator labels
Each label must be placed on a single line, followed by a comma. If you assign values to labels, align values on the same column.
enum OpenFlag { OPEN_SHARE_READ = 1, OPEN_SHARE_WRITE = 2, OPEN_EXISTING = 4, };
- Rule 35 : Use of namespaces
You have to organize your code inside namespaces. By default, you will have at least one namespace for your module (application or bundle). Inside this namespace, it is recommended to split your code into sub-namespaces. This helps notably to prevent naming conflicts.
It is forbidden to use the expression``using namespace`` in header files but it is allowed in implementation files. It is however recommended to use aliases in this latter case.
namespace bf = ::boost::filesystem;
- Rule 36 : Keyword const
Use this keyword as much as possible for variables, parameters and functions. When used for a variable or a parameter, it should be placed on the left of the qualified id, e.g. :
const double factor = 1.0; const auto* pFactor = &factor; std::vector< const Object* > objectsList; void func(const Object& param);
- Recommendation 14 : Keyword auto
- Use this keyword as much as possible to improve maintainability and robustness of the code.
- Rule 37 : Prefer constants instead of #define
- Use a static constant object or an enumeration instead of a
#define
. This will help the compiler to make type checking. You will also be able to check the content of the constants while debugging. You can also define a scope for them, inside the namespace, inside a class, private to a class, etc…
- Rule 38 : Prefer references over pointers
- When possible, use references instead of pointers, especially for function parameters. Pointer as parameter should only be used if it is considered to have a NULL pointer or when passing a C-like array. If you use a pointer, always check it if is null in the current scope before dereferencing it.
- Rule 39 : Type conversion
- For type conversion, use the C++ operators which are
static_cast
,dynamic_cast
,const_cast
andreinterpret_cast
. Use them wisely in the appropriate case. You may read this documentation.
- Recommendation 15 : Strings to numbers/numbers to string conversion
- When converting strings to numbers or numbers to string, prefer the use of boost::lexical_cast.
- Recommendation 16 : Exceptions
- Exceptions are the preferred mechanism to handle error notifications.
- Rule 40 : Explicit integer types
When you do need a specific integer size, use type definitions declared in <cstdint>, for example :
Bits Signed Unsigned 8 int8_t uint8_t 16 int16_t uint16_t 32 int32_t uint32_t 64 int64_t uint64_t