Technology

ferdiOS C++ Coding Standard

raw

A public service announcement


Foreword

C++ is the worst programming language of all time (proof), and that is a good thing™, however please follow this document to keep things manageable.

In general, ferdiOS prefers simple, readable code over complex but faster code. Typically, you should not worry about if something is heap, or stack allocated, move semantics. We are software developers, not computer scientists.

An idiot admires complexity, a genius admires simplicity.

  • Terry A. Davis, Game Developer

Terminology

  • MUST - hard requirement
  • SHOULD - recommendation

Coding style

The software MUST have the following .clang-format file in the root of the project.

The software MUST use space indentation, either 2 or 4 spaces.

Kindly refrain from whatever this is:

// wrong
int some_test = 5;
bool foo      = true;
std::string   = "bar";

// good
int some_test = 5;
bool foo = true;
std::string = "bar";

The software MUST use snake case.

// wrong
void doSomething()
{

// good
void do_something() {

The software MUST use an opening curly brace on the same line as the function statement.

// wrong
void test()
{

// good
void test() {

As mentioned, refer to .clang-format.

Project layout

The software MUST place source/header files under src/, except main.cpp which SHOULD be in either the root directory OR in e.g. src/. If present, directory thirdparty SHOULD be placed in the root directory.

The software MUST NOT place header files in a separate directory, e.g. include/. Such a public interface shall be properly defined in CMake, thus CMake will know how to install the headers correctly.

CMake

The software MUST use CMake.

Rational: While we also like Meson, or Scons, it is best if the ferdiOS ecosystem only uses one build system across the board. CMake is an industry standard.

The software MUST place CMakeLists.txt in the root directory.

The software MUST enforce a minimum version in CMakeLists.txt via cmake_minimum_required(VERSION 3.18).

The software SHOULD use target_include_directories() over include_directories. In other words, the software SHOULD explicitly use target_* - also referred as ‘modern CMake’.

The software MUST use ccache to speed up (re)compilation.

find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
    set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
    set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()

The software SHOULD consider NOT creating multiple CMakeLists.txt, instead have one large CMakeLists.txt that covers the whole project.

The software MUST place CMake related files under cmake/, activated by list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake") set at the top of the CMakeLists.txt.

The software MUST define a project with a version

project(EXAMPLE VERSION 0.9.0 LANGUAGES CXX)

Templates

The software SHOULD NOT define templates, unless absolutely necessary.

The sofware SHOULD NOT define aliases for language keywords.

Rational: readability disaster, compilation time disaster, complexity disaster.

Macros

The software SHOULD NOT define macros, unless absolutely necessary.

Auto

The software MUST use auto wherever possible.

Rational: An IDE typically provides additional information about the underlying type.

Const

The software MUST use const wherever possible.

Filenames

The software MUST use C++ header files that are suffixed with .h. Not .hh, or .hpp.

The software MUST use C++ source files that are suffixed with .cpp. not .cxx or .cc, or .c++.

Classes

The software SHOULD mark base classes as final.

The software MUST prefix private member variables with m_ OR suffix them with _.

Namespaces

The software MUST use at least one namespace.

The software MUST NOT define nested namespaces.

Rational: symbol lookup + scopes = possible headaches.

The software MUST use the full namespace identifiers for external namespaces that are not under our control.

// bad
using namespace std;
string foo = "bar";

// good
std::string foo = "bar";

Pragma

The software MUST use #pragma once instead of include guards in header files.

Rational: it is all too easy to copy and paste a header file to another header file, modify it to suit ones needs, and forget to change the name of the include guard. Once both are included, it takes you a while to track down the error, as the error messages aren’t necessarily clear.

Pointers

The software SHOULD NOT use new, instead std::make_shared & friends.

The software SHOULD pick the right smart pointer that is appropriate for the situation in question, however this is not enforced. Using smart pointers in general is already an improvement over raw pointers.

Paths

The software MUST NOT use std::string to represent a path, prefer std::filesystem::path, or file descriptor handles.

Argparse

The software MUST use CLI11 for argument parsing, except for Qt apps where argument parsing is built-in.

Linking and Vendoring

The software SHOULD make an effort to vendor dependencies where possible.

The software SHOULD place such vendored dependencies in a folder called thirdparty, or external.

Rational: vendoring dependencies makes software easier to compile, as well as produce binaries that do not depend on a certain system state

The software MUST NOT link against, or vendor a library if it creates a license conflict.

The software SHOULD link against static libraries where possible.

The software SHOULD NOT statically link against the following ‘foundational’ libraries: Qt, openssl, libsodium, (and more).

The software MUST NOT link against the following libraries:

  • Boost
  • GTK

Dbus

The software SHOULD not use DBus, unless absolute necessary. The software SHOULD use fbus instead.

Git

The software MUST use semantic versioning.

Example: 0.0.1

The repository MUST use git tags that follow this same semantic versioning for releases.

Example: 0.0.1, not v0.0.1

The repository MUST NOT use git submodules.

Rational: Git submodules suffer from poor UX, and quirks.

The repository MUST use the master branch as primary.

Example: not main

The commit message MUST NOT contain emojis.

Rational: We are not the Javascript community

The repository README.md SHOULD NOT contain emojis.

Rational: We are not the PHP community

Precompiled headers

The software SHOULD use precompiled headers when compilation times are get out of hand.

The software MUST not include headers whose symbols are not used in subsequent code (unused headers).

Code editor

We suggest CLion as an IDE for ferdiOS C++ programming. It integrates nicely with CMake, supports remote debugging, and has various other useful features. CLion is free under non-commercial use.

More from Viktor “Vik” Bugov