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#defineFunction 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
virtualusage will need to be discussed and approved on a case by case basis.
Exceptions and RTTI are not allowed.