Coding rules are subject to change over time, whenever we come across something that needs to be defined properly. In case of doubt, the existing code should give a best practice suggestion.
TDD
The most important single coding rule is the use of test-driven development (TDD). This is explained in depth on another page, and is mandatory.
Coding
A few rules to make the code look pretty and somehow consistent:
- Opening brackets always start on the line of the indenting clause (if/for/while statement) or on the next line for function declarations. If the opening bracket on the same line looks confusing, put it on the next line for clarity, though.
- The code following a flow control statement (if/for/while) should always be put in a separate code block (i.e., curly brackets), even if it is a single statement.
- Document classes and functions with doxygen-style comments. Exceptions are functions that override those in a base class or are self-explanatory(basic getters, for example).
- try to use as self-explaining function names as possible. We do not use punch cards any longer, so a function name may exceed 8 characters.
- Use the new C++-11-style unified initialization wherever possible, i.e., with curly brackets instead of normal brackets for constructor parameters. The exception are tests, where the test macros sometimes do not like it; this you will notice.
- put template parameters on a separate line
- if you override a function, always declare this function with the "override" keyword
- whenever possible, use the new C++-11 auto syntax. There are two exceptions to the rule: the type should be explicitly declared for primitive data types (to specify the precision), and for tensor classes (the tensor libraries uses some type-casting functions to guarantee coherence of data).
- never hold onto a std::function object. It may refer to Python objects/functions, in which case there may be lifetime issues. If a std::function is given as function argument, it must be used directly in the called function.
Design
Some guidelines when creating new classes and such:
- Use abstract base classes for marker interfaces as in Java from which you then derive the implementations.
- Try to avoid creating templated classes or functions.
- All references between entities should be via pointers, and all such pointers should be wrapped in shared_ptr. This relieves the final user of most memory management. Also, use std::make_shared as default when creating objects (e.g., in tests).
- Code should be typically defensive, and capture errors aggressively
- Try to be aggressive with making functions const, and using const references as arguments.
- Make functions short (<= 20 lines or so), and seriously consider redesigning if this does not work.
- As a general rule, use forward declarations in header files instead of including other headers. Try to encapsulate external dependencies and do not include then in the header (exception: the tensor library) to keep compile times short.
Testing
Coding rules specific to tests:
- Use asserts instead of expects; if a test fails, it should fail hard.
- Make short self-explanatory tests. Every single test clause should usually have one assert statement (or a few that logically belong together, like checking both values of a pair).
- if an assertion is not self-explanatory, add a failure message. Try to avoid that, though.
- Always use the appropriate assert. E.g., if you check for equality, do not ASSERT_TRUE(x == y), but use ASSERT_EQ(x,y).