aboutsummaryrefslogtreecommitdiff
path: root/contributing.md
diff options
context:
space:
mode:
Diffstat (limited to 'contributing.md')
-rw-r--r--contributing.md1057
1 files changed, 1005 insertions, 52 deletions
diff --git a/contributing.md b/contributing.md
index 775119a..5555892 100644
--- a/contributing.md
+++ b/contributing.md
@@ -1,94 +1,1043 @@
-# Contributing new code
+This document contains
+<details><summary>
+examples
+</summary>
+that you can click on to open them.
+</details>
+
+# Git
- Please do the following *before* sending a pull request:
- Merge upstream code (if any) back into your own branch
- Run formatters/linters
-
-# Git
-
- 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
+- Pull requests for new code include either automated tests for the new code or
+ an explanation as to why the code can not (reliably) be tested
+- Non-bugfix pull requests must be approved by at least 2 reviewers before being
+ merged
+<!--
- TODO: tagging / versions
-
+-->
# Code style
-- ASCII only
-- Class names are always singular
-- Explanatory comments are placed above the line(s) they are explaining
-- Source files should only contain comments that plainly state what the code is
- supposed to do
-- Explanatory comments in headers may be used to clarify implementation design
- decisions
- 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)
-- Header includes are split into paragraphs separated by a blank line. The
- order is:
+ format` or `make lint`)
+- <details><summary>
+ ASCII only
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ // crepe startup message
+ std::string message = "Hello, world!";
+ ```
+ </td><td>
+
+ ```cpp
+ // crêpe startup message
+ std::string message = "こんにちは世界";
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Class names are always singular
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ class Foo {};
+ ```
+ </td><td>
+
+ ```cpp
+ class Cars {};
+ ```
+ </td></tr></table></details>
+- 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](#documentation) (required)
+ - Implementation details (if they affect the header)
+ - Design/data structure decisions (if applicable)
+- <details><summary>
+ Comments are placed *above* the line(s) they are explaining
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ int add(int a, int b) {
+ // add numbers
+ int out = a + b;
+ return out;
+ }
+ ```
+ </td><td>
+
+ ```cpp
+ int add(int a, int b) {
+ int out = a + b; // add numbers
+ return out;
+ }
+ ```
+ </td></tr></table></details>
+- Header includes (at the top of files) 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
-- 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).
-- All engine-related code is implemented under the `crepe` namespace,
- user-facing APIs under `crepe::api` (the folder structure should also reflect
- this).
-- `using namespace` may not be used in header files, only in source files.
-- Do not (indirectly) include private *dependency* headers in API header files,
- as these are no longer accessible when the engine is installed
-- Getter and setter functions are appropriately prefixed with `get_` and
- `set_`.
-- Doxygen commands are used with a backslash instead of an at-sign (i.e.
- `\brief` instead of `@brief`)
-- 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:
+
+ > [!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).
+
+ <details><summary>Example</summary>
+ <table><tr><th>Good</th><th>Bad</th></tr><tr><td>
```cpp
- class Bad {
- static Bad instance;
- Bad & get_instance() { return instance; }
+ #include <SDL2/SDL.h>
+ #include <iostream>
+
+ #include "api/Sprite.h"
+ #include "util/log.h"
+
+ #include "SDLContext.h"
+ ```
+ </td><td>
+
+ ```cpp
+ #include <SDL2/SDL.h>
+ #include "SDLContext.h"
+ #include "util/log.h"
+ #include <iostream>
+ #include "api/Sprite.h"
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ If there is one, the matching template header (<code>.hpp</code>) is included
+ at the bottom of the regular header (<code>.h</code>)
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ Foo.h:
+ ```cpp
+ #pragma once
+
+ template <typename T>
+ void foo();
+
+ #include "Foo.hpp"
+ ```
+
+ Foo.hpp:
+ ```cpp
+ #pragma once
+ #include "Foo.h"
+
+ template <typename T>
+ void foo() {
+ // ...
+ }
+ ```
+ </td><td>
+
+ Foo.h:
+ ```cpp
+ #pragma once
+
+ template <typename T>
+ void foo();
+ ```
+
+ Foo.hpp:
+ ```cpp
+ #pragma once
+ #include "Foo.h"
+
+ template <typename T>
+ void foo() {
+ // ...
+ }
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ <code>using namespace</code> may not be used in header files (.h, .hpp), only
+ in source files (.cpp).
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ example.h:
+ ```cpp
+ namespace crepe {
+ void foo();
+ }
+ ```
+
+ example.cpp:
+ ```cpp
+ #include "example.h"
+ using namespace crepe;
+ void foo() {}
+ ```
+ </td><td>
+
+ example.h:
+ ```cpp
+ namespace crepe {
+ template <typename T>
+ T foo();
+ }
+ ```
+
+ example.hpp:
+ ```cpp
+ #include "example.h"
+ using namespace crepe;
+ template <typename T>
+ T foo();
+ ```
+ </td></tr></table></details>
+
+- <details><summary>
+ Getter and setter functions are appropriately prefixed with <code>get_</code>
+ and <code>set_</code>.
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ class Foo {
+ public:
+ int get_speed() const;
+ void set_speed(int speed);
+ private:
+ int speed;
};
+
+ ```
+ </td><td>
- class Good {
- Good & get_instance() {
- static Good instance;
+ ```cpp
+ class Foo {
+ public:
+ int speed() const;
+ void set_speed(int speed);
+ private:
+ int speed;
+ };
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ 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.
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ class Foo {
+ Foo & get_instance() {
+ static Foo instance;
return instance;
}
};
```
-- Member variable default values should be directly defined in the class
+ </td><td>
+
+ ```cpp
+ Foo Foo::instance {};
+
+ class Foo {
+ static Foo instance;
+ Foo & get_instance() { return Foo::instance; }
+ };
+
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Member variable default values should be directly defined in the class/struct
declaration instead of using the constructor.
-- Header files declare either a single class or symbols within a single
- namespace.
-- Use of the `auto` type is not allowed, with the following exceptions:
- - When naming the item type in a range-based for loop
- - When calling template factory methods that explicitly name the return type
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ class Foo {
+ int speed = 0;
+ };
+
+ ```
+ </td><td>
+
+ ```cpp
+ class Foo {
+ Foo() : speed(0) {}
+ int speed;
+ };
+ ```
+ </td></tr></table></details>
+- Use of the `auto` type is *not* allowed, with the following exceptions:
+ - <details><summary>
+ When naming the item type in a range-based for loop
+ </summary>
+
+ ```cpp
+ for (auto & item : foo()) {
+ // ...
+ }
+ ```
+ </details>
+ - <details><summary>
+ When calling template factory methods that explicitly name the return type
in the function call signature
- - When fetching a singleton instance
-- Only use member initializer lists for non-trivial types.
-- C++-style structs should define default values for all non-trivial fields.
-- Declare incomplete classes instead of including the relevant header where
- possible (i.e. if you only need a reference or pointer type).
-- Template functions are only declared in a `.h` header, and defined in a
- matching `.hpp` header.
-- Where possible, end (initializer) lists with a trailing comma (e.g. with
+ </summary>
+
+ ```cpp
+ auto ptr = make_unique<Foo>();
+ ```
+ </details>
+ - <details><summary>
+ When fetching a singleton instance
+ </summary>
+
+ ```cpp
+ auto & mgr = crepe::api::Config::get_instance();
+ ```
+ </details>
+
+- <details><summary>
+ Only use member initializer lists for non-trivial types.
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ class Foo {
+ public:
+ Foo() : bar("baz") {}
+ private:
+ std::string bar;
+ };
+
+ ```
+ </td><td>
+
+ ```cpp
+ class Foo {
+ public:
+ Foo() : bar(0) {}
+ private:
+ int bar;
+ };
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ C++-style structs should define default values for all non-trivial fields.
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ struct Foo {
+ int bar = 0;
+ std::string baz;
+ };
+ ```
+ </td><td>
+
+ ```cpp
+ struct Foo {
+ int bar;
+ std::string baz;
+ };
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Declare incomplete classes instead of including the relevant header where
+ possible (i.e. if you only need a reference or raw pointer).
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ class Bar;
+ class Foo {
+ Bar & bar;
+ };
+
+ ```
+ </td><td>
+
+ ```cpp
+ #include "Bar.h"
+ class Foo {
+ Bar & bar;
+ };
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Template functions are only <i>declared</i> in a <code>.h</code> header, and
+ <i>defined</i> in a matching <code>.hpp</code> header.
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ add.h:
+ ```cpp
+ template <typename T>
+ T add(T a, T b);
+
+ #include "add.hpp"
+ ```
+
+ add.hpp:
+ ```cpp
+ #include "add.h"
+
+ template <typename T>
+ T add(T a, T b) {
+ return a + b;
+ }
+ ```
+ </td><td>
+
+ add.h:
+ ```cpp
+ template <typename T>
+ T add(T a, T b) {
+ return a + b;
+ }
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Where possible, end (initializer) lists with a trailing comma (e.g. with
structs, enums)
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ enum Color {
+ Red,
+ Green,
+ Blue,
+ };
+
+ ```
+ </td><td>
+
+ ```cpp
+ enum Color {
+ Red,
+ Green,
+ Blue
+ };
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ <code>#pragma</code> should be used instead of include guards
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ #pragma once
+
+ // ...
+ ```
+ </td><td>
+
+ ```cpp
+ #ifndef __INCLUDED_H
+ #define __INCLUDED_H
+
+ // ...
+
+ #endif
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Variables that are being moved always use the fully qualified
+ <code>std::move</code>
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ using namespace std;
+ string foo = "bar";
+ ref_fn(std::move(foo));
+ ```
+ </td><td>
+
+ ```cpp
+ using namespace std;
+ string foo = "bar";
+ ref_fn(move(foo));
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ If possible, classes and structs are passed to functions by (const) reference
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ void foo(const Point & p);
+ ```
+ </td><td>
+
+ ```cpp
+ void foo(Point & p);
+ void bar(Point p);
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Follow the rule of five
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ class Foo {
+ public:
+ Foo();
+ ~Foo();
+ Foo(Foo &&) noexcept;
+ Foo & operator = (const Foo &);
+ Foo & operator = (Foo &&) noexcept;
+ };
+ ```
+ </td><td>
+
+ ```cpp
+ class Foo {
+ public:
+ Foo();
+ ~Foo();
+ };
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Ensure const-correctness
+
+ > [!IMPORTANT]
+ > C-style APIs that work on (possibly internal) references to structs can be
+ > called from const member functions in C++. If the compiler allows you to
+ > mark a function as `const` even though it has side effects, it should
+ > **not** be marked as `const`.
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```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;
+ };
+ ```
+ </td><td>
+
+ ```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;
+ };
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Files should be named after the class/struct/interface they implement
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ MyClass.h
+ MyClass.cpp
+ MyClass.hpp
+ ```
+ </td><td>
+
+ ```cpp
+ my_class.h
+ myClass.cpp
+ my-class.hpp
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Implementations are not allowed in header files, except if the implementation
+
+ - is `= default`
+ - is `= delete`
+ - is `{}` (empty)
+ - only returns a constant literal
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ class Foo {
+ public:
+ int get_value() const { return 42; }
+ };
+ ```
+ </td><td>
+
+ ```cpp
+ class Foo {
+ public:
+ int calculate_value() const {
+ int result = 0;
+ // complex calculation
+ return result;
+ }
+ };
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Use angle brackets (<code><></code>) only for including system headers and
+ double quotes (<code>""</code>) for including other engine files.
+
+ > [!NOTE]
+ > Only files in the examples folder should include engine headers with angle
+ > brackets
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ #include <iostream>
+
+ #include "facade/Sound.h"
+ ```
+ </td><td>
+
+ ```cpp
+ #include <iostream>
+ #include <crepe/facade/Sound.h>
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Ensure exception safety by using RAII classes
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ auto foo = std::make_unique<Foo>();
+ ```
+ </td><td>
+
+ ```cpp
+ Foo* foo = new Foo();
+ // ...
+ delete foo;
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Do not use C-style memory management APIs (<code>malloc</code>,
+ <code>calloc</code>, <code>free</code>)
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ Foo * foo = new Foo();
+ delete foo;
+ ```
+ </td><td>
+
+ ```cpp
+ Foo * foo = (Foo *) malloc(sizeof(Foo));
+ free(foo);
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Prefix all class members with <code>this-></code>
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ void Foo::bar() { }
+
+ void Foo::set_value(int value) {
+ this->value = value;
+ this->bar();
+ }
+ ```
+ </td><td>
+
+ ```cpp
+ void Foo::bar() { }
+
+ void Foo::set_value(int new_value) {
+ value = new_value;
+ bar();
+ }
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Assigning booleans should be done with the
+ <code>true</code>/<code>false</code> literals instead of
+ <code>0</code>/<code>1</code>
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ bool foo = true;
+ bool bar = false;
+ ```
+ </td><td>
+
+ ```cpp
+ bool foo = 1;
+ bool bar = 0;
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ The reason for <code>friend</code> relations are documented
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ //! ComponentManager calls the private constructor of this class
+ friend class ComponentManager;
+ ```
+ </td><td>
+
+ ```cpp
+ friend class ComponentManager;
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Do not <i>pick</i> fixed-width integer types (unless required)
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ unsigned long long foo();
+ ```
+ </td><td>
+
+ ```cpp
+ uint64_t foo();
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Utilize standard exceptions where appropriate (those found in <code>&lt;stdexcept&gt;</code>)
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ #include <stdexcept>
+
+ // ...
+
+ if (foo == nullptr) {
+ throw std::runtime_error("What is wrong");
+ }
+ ```
+ </td><td>
+
+ ```cpp
+ if (foo == nullptr) {
+ std::cout << "What is wrong" << std::endl;
+ exit(1);
+ }
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Mention the name of the class when throwing an exception
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ Foo::bar() {
+ if (...)
+ throw std::runtime_error("Foo: big error!");
+ }
+ ```
+ </td><td>
+
+ ```cpp
+ Foo::bar() {
+ if (...)
+ throw std::runtime_error("big error!");
+ }
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Constructors of classes derived from <code>Component</code> should be
+ protected and <code>ComponentManager</code> should be declared as a friend
+ class.
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ class MyComponent : public Component {
+ protected:
+ MyComponent(...);
+ //! Only ComponentManager is allowed to create components
+ friend class ComponentManager;
+ };
+ ```
+ </td><td>
+
+ ```cpp
+ class MyComponent : public Component {
+ public:
+ MyComponent(...);
+ };
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ C++ <code>std::format</code> should be used instead of C-style format specifiers
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ std::string message = std::format("Hello, {}", name);
+
+ dbg_logf("Here too: {}", 3);
+
+ throw std::runtime_error(std::format("Or here: {}", 5));
+ ```
+ </td><td>
+
+ ```cpp
+ char message[50];
+ sprintf(message, "Hello, %s", name);
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Argument names should be added in <code>.h</code> files (not only in
+ <code>.cpp</code> and <code>.hpp</code> files)
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ Foo.h:
+ ```cpp
+ void foo(int bar);
+ ```
-## CMakeLists specific
+ Foo.cpp:
+ ```cpp
+ void foo(int bar) {
+ // ...
+ }
+ ```
+ </td><td>
+
+ Foo.h:
+ ```cpp
+ void foo(int);
+ ```
+
+ Foo.cpp:
+ ```cpp
+ void foo(int bar) {
+ // ...
+ }
+ ```
+ </td></tr></table></details>
+- Do not implement new classes as singletons
+- <details><summary>
+ Retrieving the first or last indices for iterators with a known or expected
+ size should be done using <code>.front()</code> or <code>.back()</code>
+ instead of by index
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ vector<int> foo = { 1, 2, 3 };
+ int bar = foo.first();
+ ```
+ </td><td>
+
+ ```cpp
+ vector<int> foo = { 1, 2, 3 };
+ int bar = foo[0];
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Always explicitly check against <code>NULL</code> (for C APIs) or
+ <code>nullptr</code> (for C++ APIs) when checking if a pointer is valid
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ string foo = "Hello world";
+ if (foo.c_str() == nullptr)
+ // ...
+
+ void * bar = malloc();
+ if (bar == NULL)
+ // ...
+ ```
+ </td><td>
+
+ ```cpp
+ string foo = "Hello world";
+ if (!foo.c_str())
+ // ...
+
+ void * bar = malloc();
+ if (!bar)
+ // ...
+ ```
+ </td></tr></table></details>
+
+## 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.
+## GoogleTest-specific
+
+- Unit tests are not *required* to follow all code standards
+- <details><summary>
+ Private/protected members may be accessed using preprocessor tricks
+ </summary>
+
+ ```cpp
+ // include unrelated headers before
+
+ #define private public
+ #define protected public
+
+ // headers included after *will* be affected
+ ```
+ </details>
+- Each test source file defines tests within a single test suite (first
+ parameter of `TEST()` / `TEST_F()` macro)
+- Test source files match their suite name (or test fixture name in the case of
+ tests that use a fixture)
+- Tests that measure time or use delays must be [disabled][gtest-disable] (by
+ prepending `DISABLED_` to the suite or case name).
+
+ These tests will still be compiled, but will only run when the `test_main`
+ binary is run with the `--gtest_also_run_disabled_tests` flag.
+
+# 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
+[Doxygen commands](https://www.doxygen.nl/manual/commands.html)
+
- All documentation is written in U.S. English
+- <details><summary>
+ Doxygen commands are used with a backslash instead of an at-sign.
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ /**
+ * \brief do something
+ *
+ * \param bar Magic number
+ */
+ void foo(int bar);
+ ```
+ </td><td>
+
+ ```cpp
+ /**
+ * @brief do something
+ *
+ * @param bar Magic number
+ */
+ void foo();
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ The default constructor and destructor aren't required to have a
+ <code>\brief</code> description
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ Foo();
+ virtual ~Foo();
+ ```
+ </td><td>
+
+ ```cpp
+ //! Create instance of Foo
+ Foo();
+ //! Destroy instance of Foo
+ virtual ~Foo();
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Parameter direction shouldn't be specified using Doxygen
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ /**
+ * \param bar Reference to Bar
+ */
+ void foo(const Bar & bar);
+ ```
+ </td><td>
+
+ ```cpp
+ /**
+ * \param[in] bar Reference to Bar
+ */
+ void foo(const Bar & bar);
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Deleted functions shouldn't have Doxygen comments
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ // singleton
+ Foo(const Foo &) = delete;
+ Foo(Foo &&) = delete;
+ Foo & operator=(const Foo &) = delete;
+ Foo & operator=(Foo &&) = delete;
+ ```
+ </td><td>
+
+ ```cpp
+ //! Deleted copy constructor
+ Foo(const Foo &) = delete;
+ //! Deleted move constructor
+ Foo(Foo &&) = delete;
+ //! Deleted copy assignment operator
+ Foo & operator=(const Foo &) = delete;
+ //! Deleted move assignment operator
+ Foo & operator=(Foo &&) = delete;
+ ```
+ </td></tr></table></details>
+- Do not use markdown headings in Doxygen
+
+## Documenting features
+
+Engine features are small 'building blocks' that the user (game developer) may
+reference when building a game with the engine. Features do not necessarily map
+1-1 to engine components or systems. If a component or system has a single,
+distinct feature it should be named after that feature, not the component or
+system itself.
+
+The sources for these pages are located under `src/doc/feature/`, and have the
+following format:
+
+- A feature description which explains—
+ - the purpose and function of the feature (focus on what it enables or
+ achieves for the user)
+ - additional information about when to implement the feature, such as specific
+ use cases or scenarios
+- A list of 'see also' references to relevant classes and/or types
+- A **minimal** example to demonstrate how the feature is used. The example
+ should be written such that the following is clear to the reader:
+ - Which headers need to be included to utilize the feature
+ - *Why* the example works, not what is happening in the example
+ - Where is this code supposed to be called (e.g. inside scene/script
+ functions)
+ - Which restrictions should be kept in mind (e.g. copy/move semantics, max
+ component instances, speed considerations)
+
+Features should be documented as clear and concise as possible, so the following
+points should be kept in mind:
+
+- <details><summary>
+ If a page expands on an example from another page, directly reference the
+ other page using a cross-reference (`\ref`) in a `\note` block at the top of
+ the page.
+ </summary>
+
+ ```
+ \note This page builds on top of the example shown in \ref feature_script
+ ```
+ </details>
+- When explaining the usage of specific functions, qualify them such that
+ Doxygen is able to add a cross-reference or manually add a reference using the
+ `\ref` command.
+- Users will likely copy-paste examples as-is, so do this yourself to check if
+ the example code actually works!
# Libraries
@@ -96,4 +1045,8 @@
subdirectory
- When adding new submodules, please set the `shallow` option to `true` in the
[.gitmodules](./.gitmodules) file
+- When adding new libraries, please update the library version table in
+ [readme\.md](./readme.md)
+
+[gtest-disable]: https://google.github.io/googletest/advanced.html#temporarily-disabling-tests