C++ Developer Guide

Please note; these rules are not random; nor are they my personal preferences. They represent – for the most part – an emerging ‘de facto’ standard for how modern C++ code is developed.

Basics

C++14 “modern” style

For the most part follow the C++ Core Guidelines

Class Design

Try to avoid ‘-er’ type classes (Manager, Controller, Handler etc). It’s often hard to determine what their purpose is.

Sometimes you need a class whose only purpose is to handle a set of (logical) child classes. But they should normally be short and do only that. If actual business logic starts to sneak in to your manager class, it’s either time to rename it or (better) move that logic somewhere else.

Project layout

We try to follow the pitchfork proposal

It tries to unify de facto standards.

We use a single, top level CMakeLists.txt

We also have a boot strapping Makefile so you can just type make after cloning.

File structure

Put the interface in the header and the implementation in the cpp file. The cpp file should include the headers as its first line.

Use #pragma once instead of ifdef guards; it is supported by all compilers and is less error prone (and faster).

Sort your includes; The corresponding class h-file first. Then other local include files. Then 3rd party headers. Last std library includes.

Try to keep dependencies out of include files.

Source code style

namespace esdk {

class StateMachine
{
public:
    void doSomething(std::string const& name)
    {
        if (name.empty()) {
            return;
        }
    }
};

} // namespace esdk

Naming

Camel case, Upper case for classes and structs, lower case for methods and variables.

Namespaces should be lower case and kept short.

Use UPPERCASE only for macros, not for enums and constants, to avoid collisions with existing macros.

Priority; where to spend most time thinking about good names

ie Think really hard about what a class should be called so it conveys as clearly as possible what it does, and change the name if the responsibility of the class changes.

As a special case, function arguments should of course have good names, but you should not need to see them to understand the function.

How to use a function should preferably be clear by it’s name and the types of its arguments. This way mixing up arguments will result in a compilation error and not a runtime error.

Error Handling

Distinguish between errors and nonerrors. A failure is an error if and only if it violates a function’s ability to meet its callees’ preconditions, to establish its own postconditions, or to reestablish an invariant it shares responsibility for maintaining. Everything else is not an error.

Herb Sutter

Check pre conditions at compile time if possible (static_assert). For errors, we use exceptions. We can catch and rethrow in jni layer.

Tools

Testing

Aim for test driven design.

We use catch2 and put all our unit tests under tests/

Since you don’t have tests for your tests; Test code should be understandable and correct by inspection.

Also because test code is a good starting place to understand the rest of the code.

Avoid special test utility method, or test classes. Try to write top-down, understandable code.

Documentation

Try to document API level methods and classes with descriptive text, using //! comments like these

We can then use Doxygen to generate documentation.

Motivation / Contention

80 Columns

Boost uses 80 columns. The clang standard library uses 80 columns. Most lines are shorter, so a longer line width wastes more screen real estate.

With clang-format, you don’t need to do line breaking manually, it will format the code good enough to be readable.

It is universally understood that long lines are hard to read. This is why newspapers format things into columns. For code this can break up statements too much and at some point it will make it harder to read. As far as I know there has been no studies made to figure out where the “breaking point” lies, but I think the consensus in the community is that it is below 80 characters.

Don’t let the historical significance of the number fool you into thinking this is just a legacy rule. It’s just a nice, even number that seems to be a good trade off between too long lines and two much wrapping.

East const

I prefer fn(std::string const& arg) instead of fn(const std::string& arg). There has been a lot of debate about this, but the consensus is basically that the first (east const) is more logical, but the second is the standard.

Either one is OK.

Curly braces

Some C++ programmers (mainly those used to C#) prefer to put a new line before every curly brace.

Java code never puts new lines before braces.

The de facto C++ standard is a mix, and not really a better alternative than either Java or C#, but since there is no standard that is universal, we may as well go with the de facto C++ standard.

Naming

The C++ community is moving away from CamelCase towards lower snake_case for everything. This guide still recommends the older more universal standard that is similar to Java and C#. I think this still reflects what most developers are used to, but it would be more correct to switch to snake case. We can still start types with upper case.