This document gives a short overview over the global coding guidelines for IHTA C++ code.
General C++ guidelines
When in doubt and these guidelines do not cover your problem, checkout cpp-best-practices by Jason Turner.
Formatting
All IHTA C++ projects include a .clang-format
as well as a .cmake-format
.
clang-format is part of the llvm libraries and is usually included in your Visual Studio installation. Your code must be formatted accrodingly. For example, you can quickly format the current selection in Visual Studio using the hotkey combination Ctrl+K, Ctrl+F
.
cmake-format is part of the cmakelang project and needs to be installed separately.
In case you are editing CMake files extensively, please install it and adhere to the format defined in .cmake-format
.
Naming conventions
IHTA mostly follows a lowercase camelCase, Hungarian notation.
This means, that a class member variable of type std::vector<Object*>
would have to be named something like m_vpoNameOfTheVariable
.
Where m_
denotes it as a class member, v
stands for the vector, p
for the fact that it is a pointer and o
because it is a non specific object.
This naming convention extends to class names as well using uppercase camelcase: A class is prefixed with a C
, a struct with a S
and an interface with an I
(e.g. CMyClass
, SMyStruct
, IMyInterface
).
Use const ref for parameters
To significantly increase the performance (especially for large objects), always use const references for input parameters to a method instead of creating copies:
void foo(const std::vector<int>& vec)
instead of void foo(std::vector<int> vec)
.
In some special cases, it might make sense to use a non-const reference (e.g. pseudo output parameter void foo(double& dOutput)
), but this should be used with caution.
Use smart pointers
Raw pointers are evil.
C++11 introduced smart pointers with std::unique_ptr
, std::shared_ptr
and std::weak_ptr
.
Used them over raw pointers.
Preferably, use unique_ptr
s over shared_ptr
s, note however, that this requires you to be careful with object destruction.
nullptr
Use C++11 introduces nullptr
which is a special value denoting a null pointer.
This should be used instead of 0
or NULL
to indicate a null pointer.
Use namespaces
When you write code for a library, at least use a namespace with the name of the library.
Documentation
Document your code!!
When you write a new method or class at least document the interface.
What does this method do?
What are the parameters and, if any, return values.
Follow the doxygen style, preferably using the ///
or //!
notation.
In the best case, the rest of your source code is self explanatory and does not need any further documentation.
Unit test guideline
Going forward IHTA uses catch2 and FakeIt for unit testing.
Any new functionality that is implemented should be unit tested. Try to break your code and test every code path that you can.
An example for a unit test can be found here.
Test case naming and tags
Test cases should be named using the following formula:
[library name]::[class or function under test]/[description of what is tested]
For example leading to ITABase::PiecewisePolynomial/General
.
Also try to reduce whitespace in the test name.
catch2 also supports using tags for a test case. Here also use tags with:
- The library name
- The class or function name
- A test priority tag (
[Required]
,[Recommended]
,[Optional]
)
Interface faking
When you want to test your code and you are dependant on an external interface, it is often easier to fake or mock this object. That way, you can also test if your code is calling the interface correctly. For this, we use FakeIt For example on how to use this, visit the FakeIt quick start
Notes:
-
When verifying a invocation using FakeIt the caller parameters might not be stored correctly. This happened especially for non trivial types. In this case, the following workaround can be employed:
VAVec3 vecCall; int idCall; fakeit::When( Method( VACoreMock, SetSoundReceiverPosition ) ) .AlwaysDo( [&]( const int& id, const VAVec3& vec ) { vecCall = vec; idCall = id; } );
Use generator expression
catch2 implements a powerful feature with its generator expressions. Try to use them as often as you can to generate input data or test cases for your tests.
When you want to test for random input data, also use the random generator. This allows us to repeat a random unit test run using the seeding mechanism of catch2.