Formatting Conventions#

This document is strongly based on (verily, virtually identical to) the Google C++ Style Guide and the Allman style. We have taken those styles and changed it in places to match our needs.

Spaces#

Space are omitted after opening round, square, curly brace and before corresponding closing ones.

You may choose between =, (), and {}; the following are all correct:

int x = 3;
int x(3);
int x{3};
std::string name = "Some Name";
std::string name("Some Name");
std::string name{"Some Name"};

Be careful when using a braced initialization list {...} on a type with an std::initializer_list constructor. A nonempty braced-init-list prefers the std::initializer_list constructor whenever possible. Note that empty braces {} are special, and will call a default constructor if available. To force the non- std::initializer_list constructor, use parentheses instead of braces.

std::vector<int> v(100, 1);  // A vector containing 100 items: All 1s.
std::vector<int> v{100, 1};  // A vector containing 2 items: 100 and 1.

Also, the brace form prevents narrowing of integral types. This can prevent some types of programming errors.

int pi(3.14);  // OK -- pi == 3.
int pi{3.14};  // Compile error: narrowing conversion.

Space are omitted between function name and opening round brace.

// Use this
int func(...);
// Instead of this
int func ( condition );
int func (condition);

Space is written between keyword and corresponding opening brace.

// Use this
if (condition)

// Instead of this
if( condition )
if(condition)
// Use this
for (int i = 0; i < kSomeNumber; ++i)

// Instead of this
for( int i = 0; i < kSomeNumber; ++i )
for(int i = 0; i < kSomeNumber; ++i)
// Use this
switch (bar)

// Instead of this
switch( bar )
switch(bar)
// Use this
while (condition)

// Instead of this
while( condition )
while(condition)

Space is written after comma delimiter, but is not written before.

// Use this
func(1, 2, 3, 4);
// Instead of this
func(1 , 2,3 ,4);

Space is written after semicolon for any statement (except trailing one).

// Use this
default: break

enum class Enum: int

class BoxImpl: public Box
// Instead of this
default:break

enum class Enum:int

class BoxImpl:public Box

Space is written before and after any operators.

Space is written before and after any operators &&, ||, +, -, &, * and so on.

Indentation#

We use spaces for indentation instead of tabs. Comfortable tab width is 4. It is recommended to enable “Display whitespace characters” preference in your IDE preferences. Also it is strongly recommended to enable “Remove trailing whitespaces” preference. Be careful to use external editors to avoid accidental mixing of spaces and tabs.

Access specifiers SHOULD be indented to bounded class#

Any access specified should have zero indentation level (relative to class bounds).

// Use this
class Point
{
public:
};

// Instead of this
class Point
{
    public:
};

Statements inside function SHOULD have one more indentation level#

Only single indentation level is allowed.

// Use this
int distance()
{
    return 42;
}

// Instead of this
int distance()
{
return 42;
}

Statements inside switch-case SHOULD have one more indentation level#

All labels (case, default) should be indented by one level from switch keyword (as curly braces). Each case should be placed on separate line and that line should not contain anything else. Each group should end with break or return statement that is placed on separate line with one more indentation level relatively to case keyword. Anyway default statement is required to provide error handling.

// Use this
switch (bar)
{
    case 0:
        ++bar;
        break;

    case 1:
        --bar;
        break;

    default:
    {
        bar += bar;
        break;
    }
}
// Instead of this
switch (bar)
{
case 0:
   ++bar;
   break;

case 1:
   --bar;
   break;

default: break;
}

Empty lines#

Each logical block in source code should be separated from other by single empty line. Usually empty line is placed between methods definition, class access specifiers, function group declarations, enums and so on.

Classes appearance#

Class organization#

It is recommended to place class members in order shown below:

  • friends

  • public inner classes and typedef s

  • public enumerations and constants

  • constructors

  • destructor

  • public methods

  • protected inner classes and typedef s

  • protected enumerations and constants

  • protected methods

  • private inner classes and typedef s

  • private enumerations and constants

  • private methods

  • private data

Class method parameters should be formatted as function parameters.

Class method definitions should be formatted as function definition rules.

Inheritance#

Inheritance should be described on the next line of class name. It should start with single indentation followed by : symbol. One space is expected after : symbol and before base class name. In case of multiple base classes, this list should be formatted as function parameters.

// Use this
class Derived
    : public Base
{}

class Derived
    : public Base1
    , public Base2
    , public Base3
{}
// Instead of this
class Derived
 : public Base
{}

class Derived : public Base {}

class Derived
    :   public Base1
    ,   public Base2
    ,   public Base3
{}

class Derived : public Base1
    ,   public Base2
    ,   public Base3
{}

class Derived : public Base1, public Base2, public Base3 {}

Function appearance#

Function parameters#

Functions with many parameters may produce long lines. Parameters can be split by line to satisfy requirements for long lines. Function can be written in a row in case of several parameters with short names. If you have to move argument to next line - put every argument in separated line. Put comma at the line ending, after argument.

// Use this
int test();

int test(int _arg1);

int test(int _veryLongNameVeryLongName);

int test(int _arg1, int _arg2);

int test(
    int _arg1,
    int _arg2,
    int _arg3,
    int _arg4
);
// Instead of this
int test( int _veryLongNameVeryLongName1, int _veryLongNameVeryLongName2 );

int test( int _arg1, int _arg2,
        int _arg3, int _arg4 );

int test(
        int _arg1
    ,   int _arg2
);

int test(
        int _veryLongNameVeryLongName1
    ,   int _veryLongNameVeryLongName2
);

Same rules are applied to function call. Short names in a row, long names should be split by lines.

Unused function/method parameters should be named but name should be commented.

// Use this
int test(int /*_arg1*/);

// Instead of this
int test(int);

Function definition rules#

Template header should be formatted as for declaration.

It is recommended to place return type on separate line just before function name.

Additional attributes should be located at a separate before return type.

Additional keywords should be located at the same line as return type.

// Use this
int
test(int _arg1);

// Instead of this
int test(int _arg1);
// Use this
int
Object::test(int _arg1);

// Instead of this
int Object::test(int _arg1);
// Use this
template<typename _Iterator>
inline typename _Iterator::Value
test(
    _Iterator _first,
    _Iterator _last
);

// Instead of this
template<typename _Iterator>
inline typename _Iterator::Value test(
    _Iterator _first,
    _Iterator _last
);

Template parameters#

Template parameters have same rules as for function parameters. Keyword typename should be used to type parameter. Template header for class or function or variable should be placed on separate line just above name.

Warning

No class keyword is available for type specifier.

// Use this
template<typename _Type>
class Test;

template<
    typename _Type1,
    typename _Type2,
    typename _Type3
>
class Test;
// Instead of this
template<class _Type>
class Test;

template<
    typename _Type
>
class Test;

template< typename _Type1, typename _Type2, typename _Type3 >
class Test;

template<
        typename _Type1
    ,   typename _Type2
    ,   typename _Type3
>
class Test;

Statements#

Conditional#

Conditional statements with a lot of items should be split by several lines as well as functions. Single condition or several small conditions can be located at the same line as if keyword.

// Use this
if (cond)
{
}

if (isLast && canSwitch)
{
}

if (    getLanguage() == Language::Cpp
    &&  isArray()
    &&  hasNext()
)
{
}
// Instead of this
if (getLanguage() == Language::Cpp && isArray() && hasNext())
{
}

Shortest branch’s body must be placed first. In case of complex condition curly braces can be added even if branch’s body contains only single line.

// Use this
if ( !cond )
{
    // statement
}
else
{
    // statement 1
    ...
    // statement N
}
// Instead of this
if (cond)
{
    // statement 1
    ...
    // statement N
}
else
{
    // statement
}

A statement chain if-else-if is not recommended to use. Prefer switch-case instead of. When if-else-if is used as surrogate for switch-case with non-enumerable type, it should be formatted without indentation, placing sequence else-if condition on one line.

// Use this
if (name == "Door")
{
    return std::make_unique<Door>();
}
else if (name == "Wall")
{
    return std::make_unique<Wall>();
}
else
{
    assert(!"Unknown maze object");
}
// Instead of this
if (name == "Door")
    return std::make_unique<Door>();
else
    if (name == "Wall")
        return std::make_unique<Wall>();
    else
        assert(!"Unknown maze object");

Operators#

Operators usually should be located on the same line as operands.

int result{objectsCount + variablesCount - hello};

In case of large operands or a long chain of arithmetic or logical operators, expression should be split by operators. Each operator should be placed at start of new line.

int result{
        veryLargeObjectsCount
    +   veryLargeVariablesCount
    -   veryLargeHello
};

Assignment operator should be located on the same line as left-side expression.

int result =
        veryLargeObjectsCount
    +   veryLargeVariablesCount
    -   veryLargeHello
;

Lambda expressions#

Treat lambda as inline function. Capture list and parameters should be formatted as functions. Lambda body should be formatted as function body with respect to all coding style rules. In case of many capture list items you probably want to replace them to single universal capture form.

// Use this
[
    variable1,
    variable2,
    variable3,
    variable4
]
(std::string const & _value)
{
    return _value > "0";
}

[ = ](std::string const & _value)
{
    return _value > "0";
}
// Instead of this
[variable1, variable2, variable3, variable4](std::string const & _value)
{
    return _value > "0";
}

Always prefer explicit variables in capture list instead of universal capture form. Avoid capture & as much as possible. Exception is case when closure has very small lifetime.

Long lines#

Long line - line with width more than 100 symbols. Such lines should be avoided. Coding style rules prevent long lines by using special format to split long lines. Long names or long method call chain can violate this rule. Such code should be manually split by several lines.

// Use this
saveExecutionResult(
    getCallerWrapper(),
    getObjectParameter(),
    TypeCode::Clock{}
);

// Instead of this
saveExecutionResult(getCallerWrapper(), getObjectParameter(), TypeCode::Clock{});

Sometimes you want to call a lot of nested methods (which is actually violate Law of Demeter principle). Anyway if you really need to do this, pay attention to long lines and try to split them.

// Use this
getObject()
    .getWrapperController()
    .getActionExecutor()
    .forObject()
    .execute();


// Instead of this
getObject().getWrapperController().getActionExecutor().forObject().execute();

Long function calls can be formatted according to function parameters rules. Those rules can be applied recursively if function call is still long.

result = veryLongFunctionCall(
    firstParam,
    saveExecutionResult(
        getCallerWrapper(),
        getObjectParameter(),
        TypeCode::Clock{}
    ),
    shortFunctionCall(arg1)
);

Variable initialization can be long even if only single operand at right side. This expression can be split by several line around assignment operator. Move initializer to the next line. In case of universal initialization style, you may want to wrap initializer by {} symbols. This style makes initialization looks like function call with single argument.

std::unique_ptr< Writable::VariableValues > variableValues{
    std::make_unique< Impl::VariableValuesImpl >()
};

std::unique_ptr< Writable::VariableValues > variableValues =
    std::make_unique< Impl::VariableValuesImpl >();

Include guards#

To avoid recurrent header #ifndef clause, we accepted following naming rule: name should be built with such elements:

Curly braces#

Curly braces should be placed on separate line. That line should not contain anything else. Opening curly brace should be placed on the same indentation level as closing. Indentation level is defined by indentation level of above construct.

// Use this

void
hello()
{
    std::cout << "Hello!";
}

// Instead of this

void
hello() {
    std::cout << "Hello!";
}

void
hello() { std::cout << "Hello!"; }

void
hello()
    {
        std::cout << "Hello!";
    }
// Use this

do
{
    std::cout << "Hello!";
}
while (a < 10);

// Instead of this

do
{
    std::cout << "Hello!";
} while (a < 10);

do {
    std::cout << "Hello!";
}
while (a < 10);
// Use this

if (a < 10)
{
}

// Instead of this

if (a < 10) {
}

Curly braces MUST be added even for single statement in branch’s body for better visual separation and to avoid a big set of bugs.

// Use this

if (a < 10)
{
    doAction();
}

// Instead of this

if (a < 10)
    doAction();

Curly braces should be located at the same line only in case of namespace declaration, array initializer list or universal assignment style.

int digits[] = { 1, 2, 3 };
int value{ getResult() };
namespace MyNamespace {

int getValue();

}

Preprocessor#

Preprocessor directives should be formatted with indentation and spaces as described for plain code. Sharp is written together with preprocessor keyword.

// Use this

#if defined( WIN32 )
    #if defined( VA_EXPORTS )
        #define VA_EXPORT dllexport
    #else
        #define VA_EXPORT dllimport
    #endif
#else
    #define VA_EXPORT
#endif
// Instead of this

#if defined( WIN32 )
#if defined( VA_EXPORTS )
#define VA_EXPORT dllexport
#else
#define VA_EXPORT dllimport
#endif
#else
#define VA_EXPORT
#endif
// Instead of this

#if defined( WIN32 )
#   if defined( VA_EXPORTS )
#       define VA_EXPORT dllexport
#   else
#       define VA_EXPORT dllimport
#   endif
#else
#   define VA_EXPORT
#endif

It is recommended to align backslash (line continuation sign) in long macro definitions. Such code is slightly more readable.

// Use this

#define BTL_EXPORT_CLASS_FACTORY()  \
extern "C" void *                   \
getClassFactory()                   \
{                                   \
    ...                             \
// Instead of this

#define BTL_EXPORT_CLASS_FACTORY() \
extern "C" void * \
getClassFactory() \
{ \
    ... \