Skip to content

Code Style

Michael Heasell edited this page May 8, 2021 · 4 revisions

C++

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).

Formatting

Use the provided clang-format rules to format code.

Casing Conventions

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.

Inheritance / Polymorphism

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.

Pointers

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.

Integers

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).

Error Handling

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_error for errors that would always indicate a bug if thrown ("should never happen" cases)
  • std::runtime_error for 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.

Constructors

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).

Misc.

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.

TypeScript

Formatting

Use prettier.

Everything Else

TODO

Clone this wiki locally