-
Notifications
You must be signed in to change notification settings - Fork 162
Coding Style
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)
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)
{
}
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);
}
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
.