Note: this document will be updated regularly clarifying existing rules and adding missing guidelines that will emerge from discussions or PRs being reviewed.
All files should be formatted according to the .clang-format
file using clang-format
version 15.
Github CI will fail on PR that are not properly formatted.
In some specific cases use // clang-format off
and // clang-format on
where a custom formatting can improve code look. For example many template specializations in the SC::Reflection
library are better manually formatted/aligned to highlight the pattern.
All headers must have a trailing newline.
SC.sh format execute
(or SC.bat format execute
) commandCamelCase
must be used for:camelCase
must be used for:SCREAMING_CASE
muse be used for#define
Function and methods returning a value must always be marked as [[nodiscard]]
When using system API try to handle at least the basic error codes in the very first draft implementation and leave TODO:
to remember handling additional errors that can occur.
Always use SC_TRY
family of macros to check report errors to the caller. Some very specific use cases may be exempted from this rule when it's unreasonable and useless. When the specific use case has been approved, the method that doesn't want to do any error checking should use the SC_COMPILER_WARNING_*
macros.
Example:
For example SC::Build
writers disable unused result warnings for ignoring the potential failure of appending strings in the SC::StringBuilder
being used.
If the return value of a function is already returning bool
or SC::Result
, consider adding an out
parameter, that can be a pointer
or a reference.
Example of mandatory out value:
Example of optional out value:
struct
is preferred to class
due to an arbitrary initial choice made during the very first phases of the project.
Using struct
is now necessary for consistency with the existing code.
All functions and structs must be defined in the namespace SC
.
Some libraries defining many related classes may group them in a nested namespace inside SC
.
using namespace
is only allowed inside a function or method scope.
Platform specific code should be isolated with proper #ifdef
inside a specific function if it's a small amount of code.
Bigger platform specific re-implementation should be separated in specific .inl
files included by the library .cpp
file and placed into library-specific Internal
subfolder.
Each header and compile unit should include what's necessary to use or compile it in isolation.
The unity build is a distribution mechanism detail.
Public headers are meant to be included by users of the library. Such headers should have the smallest amount of code that is needed to use them. Everywhere possible, write the implementation code in a .cpp file rather than an header.
Public headers are not allowed to include any OS specific or System / compiler provided header.
Use forward declarations everywhere possible to reduce the number of header dependencies.
Free functions should be defined as static
inside a struct (or nested namespace) with very few specific exceptions.
Member variables should not start with lowercase m
to indicate it's a member variable (so someThing
is good but mSomeThing
is not)
Good:
Bad 1:
Bad 2:
All newly added code must have associated tests.
All public classes should be documented, possibly with an usage example.
Documentation website will be automatically updated when a PR is merged to master.
Comments should in general avoid repeating what's stated in the code.
Please, take some time to find a good variable and method name that can avoid a comment.
Also restructuring code into a function with a proper name can sometimes avoid a comment.
When a comment is necessary, it's better for it to comprehensively explain a piece of code that is doing something not immediately obvious.
TODO:
(without specific attribution) are welcome to signal incomplete error handling, missing code paths and future development.
Braces may be omitted only if a single statement guarded by the if and only if there are no else clauses.
Good:
Good:
Good:
Bad:
Pointer parameters always indicate that such parameter can be null.
Use references to indicate that a parameter is mandatory.
Example:
Try writing code that can avoid casts, by using correct integer types where possible.
When cast is needed, do not use C-style casts but ONLY C++ style casts (
static_cast
,reinterpret_cast
).
Globals and static member variables should be avoided at all costs.
Special exemption allowing usage of globals will need to be discussed and approved on a case by case basis.
Usage of virtual
should be avoided if possible.
Prefer static dispatch (using enums
) and for runtime-only use cases consider using SC::Function
Special exemption allowing
virtual
usage will need to be discussed and approved on a case by case basis.
Exceptions and RTTI are not allowed.