Skip to content
Duncan edited this page Jan 15, 2023 · 10 revisions

Note: All new C and C++ files and existing ones should adopt this style.

Coding style can be based on the following example source code. /// comments are just for the purpose of this coding style page.

  • Indent: spaces
  • Indent width: 4ch
  • Max line width: 128ch (not enforced)

Source files

Example.cpp:

/// All source files should be in TitleCase. All folders should match the namespace and be TitleCase
/// Start with a description of the contents of this source file.
/**
 * Functions related to tiles.
 */

/// Alphabetically ordered application headers (rely on clang-format ordering)
#include "Types.hpp"
#include "Example.h"
/// Alphabetically ordered standard headers (rely on clang-format ordering)
#include <cassert>
/// Alphabetically ordered library headers (rely on clang-format ordering)
#include <SDL/SDL.h>

/// Use namespaces for related functions
/// Namespaces are title case.
namespace OpenLoco::Map
{
    /// Global (extern) variables are prefixed with 'g', followed by title case.
    /// Where possible avoid using global variables and make accessor functions.
    /// All basic types are to be fixed width types. Prefer to use using definitions where appropriate.
    int32_t gNumTiles = 0;

    /// Local (static) variables are prefixed with '_', followed by lower camel case.
    static int32_t _nextFreeTile;

    /// Local function declarations
    static MapPos rotate2DCoordinate(const MapPos& pos, const uint8_t rotation);

    /// Only local functions need documentation (javadoc style)
    /// Global functions should have their documentation in the header file.
    static MapPos rotate2DCoordinate(const MapPos& pos, const uint8_t rotation)
    {
        /// Function variables are lower camel case
        MapPos coordinate2D;

        /// Do not place comments on the same line as code

        /// Use TODO: comments as reminders in the source code to address something in the future.
        /// All TODO comments should have a relevant issue in the issue tracker to help tracking.
        // TODO: Use orthogonal direction constants

        /// Block comments use // not /* */

        /// All braces are on a new line
        switch (rotation)
        {
            default:
            case 0:
                coordinate2D = pos;
                break;
            case 1:
                coordinate2D.x = pos.y;
                coordinate2D.y = -pos.x;
                break;
            case 2:
                coordinate2D.x = -pos.x;
                coordinate2D.y = -pos.y;
                break;
            case 3:
                coordinate2D.x = -pos.y;
                coordinate2D.y = pos.x;
                break;
        }

        return coordinate2D;
    }
}

/**
 * Find the SurfaceElement for a given tile
 * All tiles should have a surface element
 * @return A SurfaceElement or nullptr on failure.
 */
SurfaceElement* Tile::surface()
{
    /// Pointer asterisks are placed next to the variable name.
    SurfaceElement* result = nullptr;
    for (auto& tile : *this)
    {
        result = tile.asSurface();
        /// Always be explicit with nullptr checks
        if (result != nullptr)
        {
            break;
        }
    }
    return result;
}

/// Use regions to allow text editors to collapse large blocks of code
#pragma region Animation Tables

/// Types are Title Case and members are lower camel case.
/**
 * Represents a currently running animation.
 */
struct VehicleAnimation
{
    int32_t animationIndex;
    int32_t animationLength;
};

/// Local/Global constants should be prefixed with k using also lower camel case.
/// Constants should be also declared using constexpr when possible.
static constexpr int32_t kSmokeAnimation[] =
{
    0, 0, 0, 1, 1, 1, 2, 2, 3, 2, 3, 2, 1, 0,
    3, 0, 0, 1, 1, 1, 2, 2, 3, 2, 2, 2, 1, 0
};

#pragma endregion

/// If a function definition runs over the line length limit, the arguments
/// should be broken up as follows:
void showAnimation(int32_t columns,
                   int32_t rows,
                   int32_t isResizable)
{
    /// Function calls with many arguments can be broken up like the following.
    /// The closing parenthesis should appear at the end of the last argument.
    ui::createWindow(
        gDefaultWindowX,
        gDefaultWindowY,
        columns * 200,
        rows * 300,
        isResizable ? WF_RESIZABLE : 0);
}

/// Template parameters should be prefixed with `T`, e.g. `TFoo`
template<typename TGenerator, typename TSizeType = size_t>
void take(TGenerator g, TSizeType num)
{
}

Header files

Example.h:

/// Use the following header guard style to prevent multiple inclusions.
#pragma once

/// Keep includes to a minimum in header files. Usually just the header files necessary
/// for the rest of the header file.
#include "Types.h"

/// Global (extern) variables are prefixed with g and then title case.
extern int32_t gNumGuests;

namespace OpenLoco::Map
{
    /// Prefer scoped enums over basic enums
    enum class Type
    {
        surface = 0;
        industry = 3;
    }

    /// For flags use an enum class and the enum flags helper macro.
    enum class Flag : uint8_t
    {
        none,
        ghost = (1 << 0),
        lastTile = (1 << 7),
    }
    OPENLOCO_ENABLE_ENUM_OPERATORS(Flag);
}

clang-format and Git hooks

The .clang-format file in this repository reflects the code style described above. The program clang-format can be used to reformat code style 'issues'. This is also what the CI pipeline does when building PRs. The repository also features a Python script scripts/run-clang-format.py that wraps clang-format and provides a patch instead of modifying the files.

To make life a little easier, a Git hook can be installed. This hook will execute pre-commit and halt the commit when issues arise and supply a patch to fix the issues. Just use the following lines in the .git/hooks/pre-commit file.

#!/bin/bash
src=`git diff-index --cached --name-only HEAD -- 'src/*.cpp' 'src/*.hpp' 'src/*.h'`
if [ ! -z "$src" ]; then
    ./scripts/run-clang-format.py $src
fi

Another way to format your code in-place is to run git clang-format.

Clone this wiki locally