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-formatfirst. 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
elseandcatchon 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
finalunless they are meant as a base. - Functions and methods with zero arguments use C-style
voidinside the parentheses. - Place
constexprfunction 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
assertfor debug-time checks. Usevx::throw_if_xxxxwhen 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
detailsub-namespace. - Every closing brace gets a
// namespace namecomment. 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, thenshared, thenclient). - 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();