Topaz 2 Style Guide - harrand/Topaz-2 GitHub Wiki

Topaz Style Guide

This document describes and explains all coding style choices used in the Topaz engine. In summary, it can be described as a mixture between the Google C++ Style Guide and the Linux Kernel Coding Style.

File Extensions

C/C++

  • Every C++ header file should end with .hpp. If used, C headers should end with .h
  • Every C++ source file should end with .cpp. If used, C sources should end with .c
  • Both C++ template and inline implementation files should end with .inl

GLSL

  • Every Vertex Shader file should end with .vertex.glsl
  • Every Tessellation Control Shader file should end with .tessellation_control.glsl
  • Every Tessellation Evaluation Shader file should end with .tessellation_evaluation.glsl
  • Every Geometry Shader file should end with .geometry.glsl
  • Every Fragment Shader file should end with .fragment.glsl
  • Every Header Shader file should end with .header.glsl

The remainder of the style guide applies to C++ and GLSL unless specified otherwise

Header Guards

All headers should be guarded using #define. The use of #pragma once is discouraged due to it being non-standard albeit widely supported among compilers.

The definition name should be something along the lines of #define HEADERNAME_HPP. Engine code typically uses #define TOPAZ_HEADERNAME_HPP although there may be exceptions.

Forward Declarations

It is recommended to make ample use of forward declarations where necessary in header files. Source files should not use forward declarations for dependencies, but only to allow class definitions in other orders rather than chronologically.

The motivation behind this is compile-time. Use of forward declarations can improve compile-times as a whole aswell as prevent unnecessary recompilation due to unrelated changes.

An exception to this rule is made for anything within the standard library. This is because doing so yields undefined behaviour.

Inline Functions

Inline small functions that you believe would benefit. This is something that should absolutely not be overused -- Large binary sizes are all-too-common nowadays and overuse of functionality like this doesn't help the situation.

For this reason, assume that all inline functions are subject-to-change without warning. Functions can switch between inline and not inline without notice unless introduces breaking changes.

Include Order

Includes should be in the following order:

  1. Required game-side dependency
  2. Required engine-side dependency
  3. Miscellaneous project-local dependency (such as external library headers)
  4. C++ Standard Library Headers
  5. Non-Source File Includes

Note that non-source file includes are rare but not currently prohibited.

Include Syntax

C++ Standard Library headers should always be included using angle-brackets.

E.g: #include <vector>.

Project-Local Headers, such as game-side and engine-side headers should always be included using quotes.

E.g: #include "core/debug/assert.hpp"

C Standard Library headers should always be included via their C++ variant.

Bad: #include <stdio.h>

Good: #include <cstdio>

Namespaces

Engine types are always found within the tz namespace. Game types should be similar. All Game types should be located within either a game namespace, or a namespace named similarly to the game project. Free-functions in the global namespace are discouraged and types in the global namespace are prohibited.

Bad: tz::game::World

Very Bad: std::game::World

Good: game::World

Good: red_nightmare::World

Custom namespace names should always be in snake_case.

Anonymous namespaces are absolutely fine to use in source files. They should be avoided in header files, as it's unlikely to achieve the behaviour you want. In headers, consider using a game::detail namespace or something along those lines instead.

Functions & Methods

Do not use classes to group together static methods. Instead, prefer a namespace to group together free-functions. Static methods should be used only if it's very relevant to the class. This is going to be rare because it is unlikely for highly-relevant functionality to not require the class state.

Function Names

Function and method names should always be in snake_case. This is so that code doesn't look quite so alien when compared to the standard library. Topaz does not reinvent the wheel and makes extensive use of the standard library.

Bad: void DefeatEvilMonster();

Bad: void defeatEvilMonster();

Good: void defeat_evil_monster();

Methods should be const-correct. This means that if a method does not alter the state of the struct, it should be const. There are a few exceptions to this rule in engine-side code as much of it uses opaque handles. This should however be very rare in game-side code.

Methods need not be noexcept-correct. Only move constructors should be marked as noexcept if they don't throw. Currently, Topaz completely disables exceptions in Release builds. For functions that may fail (but not fatally), consider returning a std::optional or a bool indicating success. If a function returns a reference type and it may fail, it is recommended to instead return a pointer type and return null on failure.

If you instead make an executive decision to re-enable exceptions for game-side code, you are free to ignore this specific rule.

Preconditions to functions should be asserted using topaz_assert. This should suffice for most fatal issues. For fatal issues that cannot be asserted as a precondition, instead invoke tz::debug_printf followed by std::abort.

Variables

Variable names should always be in snake_case. All variables should be const if they shouldn't be edited. This includes local variables. Use of extern variables are discouraged unless necessary. There is no issue with using static variables; local and global.

Variable names should be self-documenting. There is no maximum number of characters for a variable name. It is on you to catch the balance of readability and compactness. Use of the variable name describing the type is discouraged.

Global Variables

Use of global variables is discouraged unless necessary. Use of global variables of non-POD types is prohibited.

Local Variables

Local variables should have the narrowest possible scope. It is recommended to use extra braces to minimise the scope of local variables. Shadowing of local variables should be an error, and is strictly prohibited as it is a completely unnecessary, error-prone anti-pattern.

Types

All types should be in PascalCase. This includes structs, classes, typedefs and using declarations. It is recommended to use nouns for type names. Use of the type name describing the type information is discouraged with an exception: Interface types should be preceded with a capital I.

TBC

⚠️ **GitHub.com Fallback** ⚠️