Programming Style Guide
This document describes some rules concerning the style of the code used when writing code for SLMotion. Unless specified otherwise, the rules apply for both C++ and Python code.
C preprocessor macros
- Macro names should always be written in all caps. Word should be separated by underscores.
Example: #define MY_MACRO myexpansion
Header files
- Header files should always begin with an #include guard. This guard should have the SLMOTION_ prefix.
- Header files should never contain using namespace statements in namespace scope. using namespace std is particularly prohibited. These are, however, permitted within function definitions.
- Header files should always have the .hpp extension.
Source files
- Source files should always have the .cpp extension.
Including headers
- Include order should be as follows: the associated header (i.e. the header with the same name as the source file), then other slmotion-related headers, then third party headers, and finally standard library headers. This order is to help make sure that everything is included within the header itself, so that include order should be free when others use the headers.
- C standard library headers should be included without
the .h extension and with
the c prefix, i.e. C++ style.
Example: #include <cstdio>
Namespaces
- Everything slmotion-related should be declared within the slmotion namespace .
- Some groups of classes and functions which form a well-defined subgroup may be declared in a subnamespace of their own, e.g. xml, gui, and python
- Avoid using anonymous namespaces. Prefer static declaration for internal helper functions and variables.
Constants
- Global constants should be declared as actual variables, not as preprocessor macros, where appropriate.
- Do not declare globals in the global namespace.
- Follow the same naming conventions as specified for the macros: all caps and underscore-separation for words.
Pointers and arrays
- Avoid C-style arrays. They can almost always be replaced by an std::vector. Vectors have contiguous memory allocation, so a corresponding pointer can be found by calling &foo[0].
- If you do need a C-style array, new[] should be preferred to malloc() for creating the array. Remember to free it with delete[].
- The obvious exception is, of course, when using a third party library written in C which will take care of free()ing the array; in this case, malloc() may be acceptable.
- Avoid creating plain pointers with new. You should always use smart pointers introduced in C++11, i.e. std::shared_ptr and std::unique_ptr. Never use std::auto_ptr - it has been deprecated by the new standard and it has unsafe copy constructor and assignment functions.
Functions
- Functions should always begin with a lowercase letter.
- Use camel caps: every subsequent word should begin with an uppercase letter.
- Camel caps should be applied even in the case of acronyms: only the first letter should be capitalised.
Example: void getAamInstance() - Always make the arguments const references unless there is a reason not to do so. Integral types form an exception: they should always be passed by value unless there is a reason not to do so.
- Functions should only be declared inline if they have fewer than five relevant lines of code.
Variables
- Non-constant variables should follow the same camel caps rules as functions: the first letter should be lowercase and the first letter of each subsequent word should be uppercase (including acronyms). All other letters should be lowercase.
- Prefer references to pointers.
Classes
- Class names should also use camel caps, but the first letter should be capitalised.
- Member functions should follow standard function conventions.
- Member functions should be declared const unless there is a reason not to do so.
- Class functions (static functions within class scope) should also follow standard function conventions.
- Use RAII! The user should never have to explicitly release resources.
- Use smart pointers as members instead of plain pointers where appropriate. Try to avoid manual memory management (delete statements).
- C++11 has introduced the rule of five instead of the former rule of three: in addition to declaring the destructor, the copy constructor and the copy assignment, you should also declare the move constructor and the move assignment where appropriate.
- If the class has a default constructor, or a copy constructor, or a copy assignment, or a move constructor, or a move assignment operator, declare it explicitly default
Example: Foo() = default; - If the class should not be copied or moved, declare the
constructors and assignment operators explicitly deleted. Making
them private is not enough.
Example: Foo& operator=(Foo&&) = delete; - Single argument constructors should always be declared explicit.
- Implicit type conversion operators should be avoided. Prefer
explicit type conversion operators (introduced in C++11).
Example: explicit operator int(); - Always prefer initialisation lists to initialisation by assignment in the constructor.
- struct should only be used for POD types. If you need member functions or need to specify visibility, use class.
- Each class should contain at most one public: block and at most one private:. Each of these should be explicitly declared, so even if the class only contains private members, they should be declared as such.
- The public block should precede the private block.
- Avoid public variables in classes; structs, on the other hand, should always be completely public.
- Place public member functions first. Then place private member functions. Finally, place private member variables at the very bottom.
- Class declarations should be placed in headers named after the class, i.e. class Foo should be declared in Foo.hpp, and its functions defined in Foo.cpp. Some trivial utility or helper classes (e.g. class-related exception classes) may be exempt from this rule.
Templates
- Template argument names should be all caps. In typical cases, a single character name for a typename argument should be sufficient.
- Use typename for type arguments. Never use class.
Indentation and line length
- Each level of indendation should be exactly two empty space characters.
- Never use tab (\t) for indendation.
- Lines should not exceed 80 characters in length.
Comments
- Avoid excessive comments within code. Commenting on trivial matters makes the code harder to read, and -- in the worst case -- the comments may become out of date.
- Try to make the code self-commenting by using well-selected names for variables, functions and classes. However, do not go too far, for brevity is often good: a single-character name is sufficient for an array index variable in a for-loop.
- Each function declaration in the header should be preceeded by a small comment describing the function. This comment should begin with /**, and each line should begin with an asterisk (*). Parameters should be described with @param and return values with @return.
Example:
/**
* Computes foo from the arguments and never throws.
*
* @param foo The first foo
* @param bar The second bar
*
* @return The int-representation of foo
*/
int foo(float foo, double bar);
Assertions and exceptions
- Use assertions for detecting programmer errors. Use them to verify assumptions that should always be true.
- Use a lot of exceptions. Always throw an exception when the user tries to mishandle a function.
- Exceptions should be derived from SLMotionException.
- Pay attention to exception safety (RAII!)
Miscellaneous
- Prefer prefix increment and decrement to postfix equivalents (for performance reasons!)
- Leave a space around operators and keywords where appropriate
Example:for (int i = 0; i < 10; ++i) - The build process has been intentionally made warn about a lot of things. Try to address them. This applies to comparisons between signed and unsigned numbers and unused variables as well.
- Blocks should be placed so that the initial curly brackets are on the same line as the name of the function, and the final bracket is placed on a line of its own.
Example:
int foo() {
...
} - Do not place a space between function or class name and the parentheses surrounding the arguments.
- Add three empty lines between function definitions and declarations.