C++ Coding Standards
Preface
Rule of Thumb
- Readability first (your code should be your documentation most of the time)
- Crash/Assert early. Don't wait until the worst case happens to make the crash condition.
- Follow IDE's auto formatted style unless you have really good reasons not to do so. (Ctrl + K + D in VC++)
- Learn from existing code
References
This coding standards is inspired by these coding standards
I. Main Coding Standards
-
Use Pascal casing for class and structs
class PlayerManager; struct AnimationInfo;
-
Use camel casing for local variable names and function parameters
void SomeMethod(const int someParameter); { int someNumber; int id; }
-
Use verb-object pairs for method names
a. Use pascal casing for public methods
public: void DoSomething();
b. Use camel casing for other methods
private: void doSomething();
-
Use ALL_CAPS_SEPARATED_BY_UNDERSCORE for constants and defines
constexpr int SOME_CONSTANT = 1;
-
Use all lowercase letters for namespaces
namespace abc{};
-
prefix boolean variables with
b
bool bFired; // for local and public member variable bool mbFired; // for private class member variable
-
prefix interfaces with
I
class ISomeInterface;
-
prefix enums with
e
enum class eDirection { North, South }
-
prefix class member variables with
m
.class Employee { protected: int mDepartmentID; private: int mAge; }
-
Use ALL_CAPS_SEPARATED_BY_UNDERSCORE for
goto
labels.goto MY_LABEL; // .... MY_LABEL: std::cout << "Magic!" << std::endl; return 0;
-
Methods with return values must have a name describing the value returned
uint32_t GetAge() const;
-
Use descriptive variable names. e.g
index
oremployee
instead ofi
ore
unless it is a trivial index variable used for loops. -
Capitalize every characters in acronyms only if there is no extra word after them.
int OrderID; int HttpCode;
-
Always use setter and getters for class member variables
Use:
class Employee { public: const string& GetName() const; void SetName(const string& name); private: string mName; }
Instead of:
class Employee { public: string Name; }
-
Use only public member variables for a struct. No functions are allowed. Use pascal casing for the members of a struct.
struct MeshData { int32_t VertexCount; }
-
Use
#include<>
for external header files. Use#include ""
for in-house header files -
Put external header files first, followed by in-house header files in alphabetic order if possible.
#include <vector> #include <unordered_map> #include "AnimationInfo.h"
-
Use
#pragma once
at the beginning of every header file -
Declare local variables as close as possible to the first line where it is being used.
-
Use precision specification for floating point values unless there is an explicit need for a
double
float f = 0.5f;
-
Always have a
default
case for aswitch
statement.switch (number) { case 0: ... break; default: break; }
-
Always add predefined FALLTHROUGH for switch case fall through unless there is no code in the
case
statement. This will be replaced by[[fallthrough]]
attribute coming in for C++17 laterswitch (number) { case 0: DoSomething(); FALLTHROUGH case 1: DoFallthrough(); break; case 2: case 3: DoNotFallthrough(); break; default: break; }
-
If default case must not happen in a
switch
case, always addAssert(false)
. In our assert implementation, this will add optimization hint for release build.switch (type) { case 1: ... break; default: Assert(false, "unknown type"); break; }
-
Use
const
s as much as possible even for local variable and function parameters. -
Any member functions that doesn't modify the object must be
const
int GetAge() const;
-
Do not return const value type.
Const
return is only for reference and pointers -
Names of recursive functions end with
Recursive
.void FibonacciRecursive();
-
Order of class variables and methods must be as follows:
a. list of friend classes b. public methods c. protected methods d. private methods e. protected variables f. private variables
-
Function overloading must be avoided in most cases
Use:
const Anim* GetAnimByIndex(const int index) const; const Anim* GetAnimByName(const char* name) const;
Instead of:
const Anim* GetAnim(const int index) const; const Anim* GetAnim(const char* name) const;
-
Overloading functions to add
const
accessible function is allowed.Anim* GetAnimByIndex(const int index); const Anim* GetAnimByIndex(const int index) const;
-
Avoid use of
const_cast
. Instead create a function that clearly returns an editable version of the object -
Each class must be in a separate source file unless it makes sense to group several smaller classes.
-
The filename must be the same as the name of the class including upper and lower cases.
class PlayerAnimation; PlayerAnimation.cpp PlayerAnimation.h
-
When a class spans across multiple files, these files have a name that starts with the name of the class, followed by an underscore and the subsection name.
class RenderWorld; RenderWorld_load.cpp RenderWorld_demo.cpp RenderWorld_portals.cpp
-
Platform specific class for reverse OOP pattern uses similar naming convention
class Renderer; Renderer.h // all renderer interfaces called by games Renderer.cpp // Renderer's Implementations which are // to all platforms Renderer_gl.h // RendererGL interfaces called by // Renderer Renderer_gl.cpp // RendererGL implementations
-
Use our own version of Assert instead of standard c assert
-
Use assert for any assertion you have. Assert is not recoverable. This can be replaced by compiler optimization hint keyword __assume for the release build.
-
Any memory allocation must be done through our own New and Delete keyword.
-
Memory operations such as memset, memcpy and memmove also must be done through our own MemSet, MemCpy and MemMove.
-
Generally prefer reference(
&
) over pointers unless you neednullptr
for any reason. (exceptions are mentioned right below) -
Use pointers for
out
parameters. Also prefix the function parameters with out.function:
void GetScreenDimension(uint32_t* const outWidth, uint32_t* const outHeight) { }
caller:
uint32_t width; uint32_t height; GetScreenDimension(&width, &height);
-
The above out parameters must not be null. (Use assert, not if statement)
void GetScreenDimension(uint32_t* const outWidth, uint32_t* const outHeight) { Assert(outWidth); Assert(outHeight); }
-
Use pointers if the parameter will be saved internally.
void AddMesh(Mesh* const mesh) { mMeshCollection.push_back(mesh); }
-
Use pointers if the parameter should be generic
void*
parametervoid Update(void* const something) { }
-
The name of a bitflag enum must be suffixed by
Flags
enum class eVisibilityFlags { }
-
Do not add size specifier for
enum
unless you need that specific size (e.g, for serialization of data members)enum class eDirection : uint8_t { North, South }
-
Prefer overloading over default parameters
-
When default parameters are used, restrict them to natural immutable constants such as
nullptr
,false
or0
. -
Prefer fixed-size containers whenever possible.
-
reserve()
dynamic containers whenever possible -
Always put parentheses for defined numbers
#define NUM_CLASSES (1)
-
Prefer constants over defines
-
Always use forward declaration if possible instead of using includes
-
All compiler warnings must be addressed.
-
Shadowed variables are not allowed.
class SomeClass { public: int32_t Count; public: void Func(const int32_t Count) { for (int32_t count = 0; count != 10; ++count) { // Use Count } } }
-
Declare only one variable per line
BAD:
int counter, index;
GOOD:
int counter; int index;
-
Take advantage of NRVO, when you are returning a local object. This means you need to have only one return statement inside your function. This applies only when you return an object by value.
-
Do not use
const
member variables in astruct
orclass
simply to prevent modification after initialization. Same rule is applied to reference(&
) member variables. -
When initializing member variables, use initializer list over assignments, by default.
-
«<__restrict keyword
II. Code Formatting
-
There must be a blank line between includes and body.
-
Use Visual Studio default for tabs. If you are not using Visual Studio, use real tabs that are equal to 4 spaces.
-
Always place an opening curly brace (
{
) in a new line -
Add curly braces even if there's only one line in the scope
if (bSomething) { return; }
-
Put pointer or reference sign right next to the type
int& number; int* number;
-
When initializing member variables via initializer list, initialize one member variable per line and follow below format.
BAD:
MyClass::MyClass(const int var1, const int var2) :mVar1(var1), mVar2(var2), mVar3(0) {
GOOD:
MyClass::MyClass(const int var1, const int var2) : mVar1(var1) , mVar2(var2) , mVar3(0) {
III. Modern Language Features
-
override
andfinal
keywords are mandatory -
Use
enum class
alwaysenum class eDirection { North, South }
-
Use
static_assert
overAssert
, if possible. -
Use
nullptr
overNULL
-
Use
unique_ptr
when a object lifetime is solely handled inside a class. (i.e. new in constructor delete in destructor) -
Range-based
for
are recommended where applicable -
Do not use
auto
unless it is for a iterator ornew
keyword is on the same line, showing which object is created clearly -
Do not manually perform return value optimization using
std::move
. It breaks automatic NRVO optimization. -
Move constructor and move assignment operator are allowed.
-
Use
constexpr
instead of const for simple constant variablesconstexpr int DEFAULT_BUFFER_SIZE = 65536
Instead of
const int DEFAULT_BUFFER_SIZE = 65536
-
«<TBD: constexpr
-
«<TBD: Lambda
-
«<TBD: do not use shared_ptr
IV. Project Settings and Project Structure
-
Visual C++: Always use property sheets to change project settings
-
Do not disable compile warnings in project settings. Use #pragma in code instead.