Skip to content

Code Style

Clang-format

Format all C++ code with clang-format. The config file is in the repository root. If your editor supports format-on-save, enable it. Otherwise format before you commit.

Dependencies are excluded from automatic formatting. They rarely match Voxelius style.

Notable settings:

Settings Value
Column limit 140
Indentation 4 spaces
Line endings CRLF
Braces Custom, see below
Pointer alignment Left
Reference alignment Left

NOTE: If formatting looks wrong, do not edit .clang-format first. Split the code into smaller chunks so clang-format can handle it better.

Naming

Kind Style Example
Files snake_case shared/block_storage.cc
Namespaces snake_case namespace block_storage
Classes and structures PascalCase class ReadBuffer
Functions and methods snake_case block_registry::name_of(...)
Public fields and members snake_case BlockDefinition::model_offset
Private fields and members m_snake_case Chunk::m_blocks
Constants UPPER_SNAKE_CASE BASE_WIDTH
Type aliases Inherited ...

Brace newlines

  • Functions, methods, and namespaces put the opening brace on a new line.
  • Classes, structs, enums, unions, control statements, and lambdas keep the brace on the same line.
  • Put else and catch on a new line after the closing brace. Two braces and a keyword on one line are hard to read.

Example:

namespace vx
{
void setup(void);
void teardown(void);
} // namespace vx

void vx::setup(void)
{
    // empty
}

void vx::teardown(void)
{
    Type name {};
    name.do_something();

    if(name.valid()) {
        // ...
    }
    else {
        // ...
    }
}

Styling considerations

  • Mark classes and structures final unless they are meant as a base.
  • Functions and methods with zero arguments use C-style void inside the parentheses.
  • Place constexpr function bodies in the header, just after the declarations.
  • C++ headers use .hh. C++ sources use .cc.
  • Profanity in source code is allowed. I own the code and can comment as I choose.
  • Use standard assert for debug-time checks. Use vx::throw_if_xxxx when the game should crash on failure.

Include guards

  • Every header uses an ifndef block with a random GUID-style token. Dashes become underscores.
  • Do not use #pragma once.
  • End the ifndef block with a block comment that repeats the same GUID.

Example:

#ifndef B843EAA9_60C3_4C2B_8036_DF1026035AA8
#define B843EAA9_60C3_4C2B_8036_DF1026035AA8

// ...

#endif /* B843EAA9_60C3_4C2B_8036_DF1026035AA8 */

NOTE: This matches the default behaviour of the C/C++ Include Guard VSCode extension.

Namespaces

  • Namespaces follow modules. The same header often reopens a namespace per logical group. Put internal or private types in a detail sub-namespace.
  • Every closing brace gets a // namespace name comment. Clang-format enforces this.

Example:

namespace vx::detail
{
class Exception {
  // ...
};
} // namespace vx::detail

namespace vx
{
using runtime_error = detail::TaggedException<struct runtime_error_tag>;
} // namespace vx

Include grouping

  • Each engine module has its own precompiled header. Include it first in every compiled source.
  • Sort includes by dependency order (for example core, then shared, then client).
  • Within a group, clang-format sorts includes alphabetically.

Example:

#include "client/pch.hh"

#include "client/game.hh"

#include "core/buffer.hh"
#include "core/exception.hh"
#include "core/identifier.hh"
#include "core/res/resource.hh"

#include "shared/block_registry.hh"
#include "shared/block_storage.hh"
#include "shared/coord.hh"
#include "shared/mod_loader.hh"
#include "shared/utils/coord.hh"
#include "shared/utils/lua.hh"
#include "shared/world.hh"

#include "client/globals.hh"
#include "client/res/texture2D.hh"

Comments

  • Use plain C++ comments (except for include guards).
  • Comment sparingly. Explain non-obvious logic, or vent. Read existing comments for the tone.
  • Mark empty function or method bodies with // empty.

Example 1:

explicit Exception(std::string_view what, std::source_location location = std::source_location::current())
{
  // empty
}

Example 2:

// NOTE: SDL seems to defer handle release until the GPU
// is truly done with the object, so it's probably safe
// to even release the old handle mid-frame
reset();

See also