This document contains
examples that you can click on to open them.
# Git - Please do the following *before* sending a pull request: - Merge upstream code (if any) back into your own branch - Run formatters/linters - Push as often as possible - Development is done on separate branches, these follow a pattern of `name/feature` (i.e. `loek/dll-so-poc` or `jaro/class2`) - The master branch is considered stable, and should always contain a working/compiling version of the project - TODO: tagging / versions # Code style - Formatting nitty-gritty is handled by clang-format/clang-tidy (run `make format` in the root folder of this repository to format all sources files) -
ASCII only
GoodBad
```cpp // crepe startup message std::string message = "Hello, world!"; ``` ```cpp // crêpe startup message std::string message = "こんにちは世界"; ```
-
Class names are always singular
GoodBad
```cpp class Foo {}; ``` ```cpp class Cars {}; ```
- Source files (.cpp, .hpp) contain the following types of comments: - What is the code supposed to do (optional) - Implementation details (if applicable) - Header files (.h) contain the following types of comments: - Usage documentation (required) > [!NOTE] > Constructors/destructors aren't required to have a `\brief` description - Implementation details (if they affect the header) - Design/data structure decisions (if applicable) -
Comments are placed *above* the line(s) they are explaining
GoodBad
```cpp int add(int a, int b) { // add numbers int out = a + b; return out; } ``` ```cpp int add(int a, int b) { int out = a + b; // add numbers return out; } ```
- Header includes are split into paragraphs separated by a blank line. The order is: 1. system headers (using `<`brackets`>`) 2. relative headers NOT in the same folder as the current file 3. relative headers in the same folder as the current file > [!NOTE] > When using libraries of which the header include order is important, make > sure to separate the include statements using a blank line (clang-format > may sort include statements, but does not sort across empty lines).
Example
GoodBad
```cpp #include #include #include "api/Sprite.h" #include "util/log.h" #include "SDLContext.h" ``` ```cpp #include #include "SDLContext.h" #include "util/log.h" #include #include "api/Sprite.h" ```
-
using namespace may not be used in header files (.h, .hpp), only in source files (.cpp).
GoodBad
example.h: ```cpp namespace crepe { void foo(); } ``` example.cpp: ```cpp #include "example.h" using namespace crepe; void foo() {} ``` example.h: ```cpp namespace crepe { template T foo(); } ``` example.hpp: ```cpp #include "example.h" using namespace crepe; template T foo(); ```
-
Getter and setter functions are appropriately prefixed with get_ and set_.
GoodBad
```cpp class Foo { public: int get_speed() const; void set_speed(int speed); private: int speed; }; ``` ```cpp class Foo { public: int speed() const; void set_speed(int speed); private: int speed; }; ```
-
A singleton's instance is always accessed using a getter function that instantiates its own class as a static variable within the getter function scope, instead of storing the instance as a member variable directly.
GoodBad
```cpp class Foo { Foo & get_instance() { static Foo instance; return instance; } }; ``` ```cpp Foo Foo::instance {}; class Foo { static Foo instance; Foo & get_instance() { return Foo::instance; } }; ```
-
Member variable default values should be directly defined in the class/struct declaration instead of using the constructor.
GoodBad
```cpp class Foo { int speed = 0; }; ``` ```cpp class Foo { Foo() : speed(0) {} int speed; }; ```
- Use of the `auto` type is *not* allowed, with the following exceptions: -
When naming the item type in a range-based for loop ```cpp for (auto & item : foo()) { // ... } ```
-
When calling template factory methods that explicitly name the return type in the function call signature ```cpp auto ptr = make_unique(); ```
-
When fetching a singleton instance ```cpp auto & mgr = crepe::api::Config::get_instance(); ```
-
Only use member initializer lists for non-trivial types.
GoodBad
```cpp class Foo { public: Foo() : bar("baz") {} private: std::string bar; }; ``` ```cpp class Foo { public: Foo() : bar(0) {} private: int bar; }; ```
-
C++-style structs should define default values for all non-trivial fields.
GoodBad
```cpp struct Foo { int bar = 0; std::string baz; }; ``` ```cpp struct Foo { int bar; std::string baz; }; ```
-
Declare incomplete classes instead of including the relevant header where possible (i.e. if you only need a reference or raw pointer).
GoodBad
```cpp class Bar; class Foo { Bar & bar; }; ``` ```cpp #include "Bar.h" class Foo { Bar & bar; }; ```
-
Template functions are only declared in a .h header, and defined in a matching .hpp header.
GoodBad
add.h: ```cpp template T add(T a, T b); #include "add.hpp" ``` add.hpp: ```cpp #include "add.h" template T add(T a, T b) { return a + b; } ``` add.h: ```cpp template T add(T a, T b) { return a + b; } ```
-
Where possible, end (initializer) lists with a trailing comma (e.g. with structs, enums)
GoodBad
```cpp enum Color { Red, Green, Blue, }; ``` ```cpp enum Color { Red, Green, Blue }; ```
-
#pragma should be used instead of include guards
GoodBad
```cpp #pragma once // ... ``` ```cpp #ifndef __INCLUDED_H #define __INCLUDED_H // ... #endif ```
-
Variables that are being moved always use the fully qualified std::move
GoodBad
```cpp using namespace std; string foo = "bar"; ref_fn(std::move(foo)); ``` ```cpp using namespace std; string foo = "bar"; ref_fn(move(foo)); ```
-
If possible, classes and structs are passed to functions by (const) reference
GoodBad
```cpp void foo(const Point & p); ``` ```cpp void foo(Point & p); void bar(Point p); ```
-
Follow the rule-of-five
GoodBad
```cpp class Foo { public: Foo(); ~Foo(); Foo(const Foo &); Foo(Foo &&) noexcept; Foo & operator=(const Foo &); Foo & operator=(Foo &&) noexcept; }; ``` ```cpp class Foo { public: Foo(); ~Foo(); Foo(const Foo &); }; ```
-
Ensure const-correctness
GoodBad
```cpp class Foo { public: int get_value() const; void set_value(int new_value); const std::string & get_name() const; void set_name(const std::string & new_name); private: int value; std::string name; }; ``` ```cpp class Foo { public: int get_value(); void set_value(int new_value); std::string get_name(); void set_name(std::string new_name); private: int value; std::string name; }; ```
-
File names (.h, .cpp, .hpp) should be written using CamelCase
GoodBad
```cpp MyClass.h MyClass.cpp MyClass.hpp ``` ```cpp my_class.h myClass.cpp my-class.hpp ```
-
Implementation is not allowed in header files, except if the method only returns a constant value
GoodBad
```cpp class Foo { public: int get_value() const { return 42; } }; ``` ```cpp class Foo { public: int calculate_value() const { int result = 0; // complex calculation return result; } }; ```
-
Use angle brackets (<>) for including libraries and double quotes ("") for including local files.
GoodBad
```cpp #include #include "MyClass.h" ``` ```cpp #include "iostream" #include ```
## CMakeLists-specific - Make sure list arguments (e.g. sources, libraries) given to commands (e.g. `target_sources`, `target_link_libraries`) are on separate lines. This makes resolving merge conflicts when multiple sources were added by different people to the same CMakeLists.txt easier. # Structure - Files are placed in the appropriate directory: |Path|Purpose| |-|-| |`crepe/`|Auxiliary, managers| |`crepe/api/`|User-facing APIs| |`crepe/util/`|Standalone utilities and helper functions| |`crepe/system/`|(ECS) system classes| |`crepe/facade/`|Library façades| - Do not (indirectly) include private *dependency* headers in API header files, as these are no longer accessible when the engine is installed - All code is implemented under the `crepe` namespace. - Header files declare either a single class or symbols within a single namespace. # Documentation - All documentation is written in U.S. English -
Doxygen commands are used with a backslash instead of an at-sign.
GoodBad
```cpp /** * \brief do something * * \param bar Magic number */ void foo(int bar); ``` ```cpp /** * @brief do something * * @param bar Magic number */ void foo(); ```
# Libraries - External libraries should be included as Git submodules under the `lib/` subdirectory - When adding new submodules, please set the `shallow` option to `true` in the [.gitmodules](./.gitmodules) file