Coding guidelines
BotW has 40MB of code and contributors need to read and modify existing parts of the codebase very often: inconsistencies lead to increased mental overhead and a loss of efficiency which we cannot afford considering our limited manpower.
That is why we have a set of coding style guidelines that contributors are expected to adhere to.
To reduce the amount of time wasted on formatting issues, we use clang-format to automatically enforce a consistent coding style. We also use clang-tidy to detect common style issues, programming errors, and error prone code.
All pull requests are run through clang-format and clang-tidy and must pass both before they can be merged.
Before opening a PR, please format your code with clang-format 12+ and ensure the following guidelines are followed. That will allow your contributions to be reviewed more quickly.
Code style
The most important rule: look around and follow the existing code style (formatting, source organisation, ...) when possible.
General
- Lines should not be longer than 100 characters.
- Use 4 spaces to indent.
- Use
nullptr
, notNULL
or0
. - Only use
auto
if the variable type is obvious, too long to type or if it doesn't matter. - To compare an integer against zero, write
if (value == 0)
, notif (!value)
. (This rule doesn't apply to booleans.) - To compare a value against nullptr, either
if (pointer != nullptr)
orif (pointer)
is fine. - Avoid useless parentheses:
- ✔️
if (x == Type::String || x == Type::Int)
- ❌
if ((x == Type::String) || (x == Type::Int))
Parens are just noise here: most people (especially anyone who has worked on a software project) are used to the lack of parentheses. - ✔️
if ((foo && bar) || (x && y))
- ❌
if (foo && bar || x && y)
While parentheses are technically useless here, most people do not know their operator precedence for && and || by heart, so it's better to just add parens to make it clear what is being meant.
- ✔️
- Avoid
using namespace
at namespace/global scope. - Declare variables close to their usage point: C89 style declarations are an unnecessary antipattern in any modern language and discouraged.
Header files
- Use
#pragma once
for header guards. - Avoid unnecessary includes. Forward declare types when possible to reduce compilation times.
Includes
Use
#include "..."
when including U-King (BotW) header files. KingSystem (ksys) is treated as being part of BotW.- Include paths should be relative to src/.
- ✔️
#include "KingSystem/ActorSystem/actActor.h"
- ❌
#include "actActor.h"
- ✔️
- Include paths should be relative to src/.
Use
#include <...>
for system or library includes. Examples:- Standard C++ library headers (e.g.
<optional>
,<type_traits>
,<limits>
, ...) - sead (e.g.
<prim/seadSafeString.h>
) - Other Nintendo libraries like agl, evfl, eui, ...
- Standard C++ library headers (e.g.
Naming
Type names (classes/structs, enums, typedefs/alias declarations) and compile-time constants should be in UpperCamelCase.
class ActorInfoData
using Manager = ksys::gdt::Manager;
constexpr int NumActors = 42;
Function names should be in camelCase.
void doStuff()
void SomeClass::doThis()
(for a member function)
Variables should be in lowercase_snake_case, except for class member variables, which should be prefixed with 'm' and writtenLikeThis.
int a_dummy_variable = 42;
void test(int first_argument, bool second_argument) { ... }
class SomeClass { int mMemberVariable; };
(m prefix + camelCase)struct SomeStruct { int member_variable; };
(regular snake_case)
Static variables should be prefixed with 's' and globals with 'g'.
s_static_variable
sStaticVariable
if it's a static member variable
Classes
- Ordering
- Put access specifiers in this order:
public
,protected
,private
. - Declare member functions in this order: constructor, destructor, operators, other member functions.
- Declare non-static member variables after function declarations.
- Declare static member variables before non-static variables.
- Virtual functions need to match the original order in the executable, though, so ignore this rule if following it would require reordering virtual functions.
- Put access specifiers in this order:
- If a class uses a macro like
SEAD_SINGLETON_DISPOSER
or one of the SEAD_RTTI macros, put the macro right after the opening brace, beforepublic:
. - Use
= default;
instead of constructors/destructors with an empty body. - Use the
override
keyword instead ofvirtual
when overriding virtual functions from a parent class. - Mark member functions as const if they do not modify any non-static member variables. (This is known as const correctness.)
- Do not use
this->
to refer to member variables or member functions unless it is necessary. (It is usually unnecessary.)
#pragma once
namespace foo {
class Test {
SEAD_RTTI_BASE(Test)
public:
Test();
virtual ~Test() = default;
virtual bool isTest() const { return true; }
void doStuff() {}
private:
static bool sFoo = false;
bool mMemberVariable = true;
};
class TestDerived : public Test {
public:
bool isTest() const override { return true; }
};
} // namespace foo
(Notice the line breaks on the namespace opening and closing lines.)