C++ Coding Style

I often find that partway through a project I get the urge to rethink certain aspects of the style I'm using. I think that's partly because I've never really sat down and decided on a style; usually I just get started with whatever style I most recently matched at work. My intention here is to have a solid coding style doc to reference for any personal projects. It may be a living document that changes as I encounter new arguments for and against different styles but it should always reflect my current stance.

Rules

General rules
  • All lines should be less than 120 characters long.
  • No function should be greater than 100 lines.
  • Prefer descriptive variable names over single character ones. Abbreviations are acceptable.
  • Avoid nesting more than 3 levels deep of loops or ifs
  • Use tabs for indentation to block level and spaces for alignment.
Avoid Raw Loops

This rule was taken from: https://channel9.msdn.com/Events/GoingNative/2013/Cpp-Seasoning
Raw loops reduce readability of code and should be pulled out of larger functions.

The code:

// Do stuff above
u32 max = 0;
for ( int idx = 0; idx < collectionSize; ++idx )
{
    if ( collection[idx] > max )
        max = collection[idx];
}
// Do more stuff below

Should instead be written as

// Do stuff above
u32 max = MaxOf ( collection, collectionSize );
// Do more stuff below

This is a contrived example and also illustrates a case where using standard algorithms would be a better choice.

Naming Convention

Classes will begin with a capital letter and every new work will have it's first letter capitalized

class CamelCase;

Functions will follow the same naming convention of classes.

Plain Old Data (POD) types will be marked as structs and will be lower case with words separated by underscores

struct plain_old_data;

Variables will follow the naming convention of s_dName where s is the scope and d is the data type which will only be present in the case of primitive types: n for signed integer, u for unsigned integer, f for float, p for pointer, s for string (std or c-style) and b for bool. Scope may be omitted for local variables ( in which case the underscore is also omitted ); in other cases it will be m for member and g for global or static at the class level.

Const

The first const may go before or after the type. While before is considered more conventional, having the const after the type makes it more consistent with the other instances of const ( which also affect the previous declaration ).

Const functions should be preferred to non const functions. Pure functions should, in particular, be preferred as it increases code readability and reduces likelihood of unwanted state mutation.

If a reference is passed into a function it must be const. If a function is meant to mutate the state of a passed in variable that variable must be passed in as a pointer. This is to ensure all state mutation is explicit on the side of the function caller.

Whitespace

Parenthesis spacing

There should be whitespace after every opening parenthesis and before every closing parenthesis. Assignment and math operators should be surrounded by whitespace. Commas should have whitespace after them.

T variable = Function( arg0, arg1 );
T variable2 = variable + variable;

There should also be a whitespace after an if and before the parenthesis. There can optionally be one after a functions name and before the parenthesis.

if ( condition )
T0 Function ( T1 arg )
Logical spacing

Whitespace can be used to break up long functions into logical pieces. The better solution in many cases where this applies is to pull the functionality into it's own function. In the cases where doing so is not better, be sure to add a comment before the block describing the functionality.

There should also be spacing before after every Access Modifier in a class definition. The only exception to this is if a class definition starts with an access Modifier in which case it doesn't need the space before. This also applies to struct definitions.

class Class
{
public:

    T0 Function ();

private:

    u32 m_uVar;
}
Nested block spacing

There will be a linebreak before and after every opening brace and closing one. The only exception to this is when both opening and closing brace can fit on the same line. This exception does not apply if there are multiple related blocks ( such as an if followed by one or more else ifs and/or an else.

// Acceptable
T0 Function( T1 arg )
{
    // code
}

// Unacceptable
T0 Function( T1 arg ) {
    // code
}

// Also acceptable
if ( condition ) { /* code */ }

// Unacceptable
if ( condition ) { /* code */ }
else { /* code */ }

// Acceptable
if ( condition ) 
{
    // code
}
else
{
    // code
}

This applies also to else and else if meaning they must both be on their own line.