diff options
Diffstat (limited to 'contributing.md')
-rw-r--r-- | contributing.md | 255 |
1 files changed, 254 insertions, 1 deletions
diff --git a/contributing.md b/contributing.md index 3090727..5555892 100644 --- a/contributing.md +++ b/contributing.md @@ -15,12 +15,19 @@ that you can click on to open them. `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 - 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) + format` or `make lint`) - <details><summary> ASCII only </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td> @@ -491,6 +498,12 @@ that you can click on to open them. </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 @@ -622,15 +635,21 @@ that you can click on to open them. </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> @@ -678,6 +697,162 @@ that you can click on to open them. uint64_t foo(); ``` </td></tr></table></details> +- <details><summary> + Utilize standard exceptions where appropriate (those found in <code><stdexcept></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); + ``` + + 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 @@ -686,6 +861,32 @@ that you can click on to open them. 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: @@ -704,6 +905,8 @@ that you can click on to open them. # 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. @@ -789,6 +992,52 @@ that you can click on to open them. 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 @@ -796,4 +1045,8 @@ that you can click on to open them. 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 |