-
-
Notifications
You must be signed in to change notification settings - Fork 19
Code Style
In general RWE is written in a "modern C++" style and looks to borrow ideas and patterns from Rust as much as possible (the author is a big Rust fan).
Use the provided clang-format rules to format code.
RWE uses camelCase variable and function names, with a lower case first letter.
RWE uses CamelCase names for types and constants, with an upper case first letter.
Some existing classes in RWE use a _ prefix for private member variables.
In general however this is not encouraged.
Do not use inheritance. Some existing code in RWE does use inheritance. One day we might get rid of these.
Polymorphic-type behaviour in RWE is generally achieved via std::variant<T1, T2, ...>
and a match function that provides a "pattern-matching" style dynamic dispatch
against each of the possible cases inside the variant.
For function parameters, prefer references over pointers. For class/struct fields, prefer pointers over references.
Pointers are generally assumed to be non-null.
If a pointer could potentially be null, use a std::optional<T*>
instead, with the empty optional representing the null pointer case.
This is slightly less efficient than using null pointers but RWE values
the safety and explicitness here.
Generally speaking, avoid the use of unsigned integers.
RWE uses unsigned integers a lot for values that should not be negative and this is considered a mistake from when the author didn't know any better. We would like to eventually replace these with signed integers.
Refer to the advice from the C++ Core Guidelines here ("Arithmetic" section, ES.100 onwards):
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
For dealing with collections where the stdlib .size() function
returns an unsigned size_t, RWE has getSize() and an Index type.
This is essentially an equivalent to gsl::index from the C++ Core Guidelines
support library (gsl).
The preferred error handling mechanism is to use rwe::Result as the return value
for any function that could emit an error (similar to Rust).
RWE also uses exceptions. RWE mainly uses two types of exceptions:
-
std::logic_errorfor errors that would always indicate a bug if thrown ("should never happen" cases) -
std::runtime_errorfor errors that could reasonably be thrown during normal operation
The std::runtime_error exception is thrown in places where we should be using Result,
but the author did not care to implement robust error handling logic
as feature work was considered a higher priority issue.
Ideally one day all instances of std::runtime_error will instead be replaced with
error handling logic based around returning rwe::Result values.
Do not do any non-trivial work in constructors.
If you require a convenient way to create an instance of a class or struct that does non-trivial work, or worse, could error out, create a factory function (either static method or free function) that does the necessary work and then passes the result into the constructor for the type.
If the function could error out, have it return a rwe::Result
(or throw std::runtime_error with the expectation that one day, perhaps in the far future,
we will come back and use rwe::Result instead).
Prefer plain functions as much as possible.
Existing RWE code uses an object-oriented style in places. In particular lots of code lives in "XxxService" type classes. Ideally however the interesting logic should live in plain old functions, which can be unit tested and then called from inside these classes' methods.
Use prettier.
TODO