Briefly about C++
Here you'll find just a quick overview of some good basic C++ practices. More can be learned in the MRS Documentation.
Memory Management
-
Be careful about using raw pointers. Do not allocate memory using raw pointers and the
newkeyword, since it is easy to forget freeing the memory, which causes memory leaks. Use smart pointers from#include <memory>instead, since they manage this automatically. However, if you need to pass something to a function without transferring ownership, using a raw pointer is fine. -
Avoid storing references to
std::shared_ptror passing them by non-const reference, since that subverts reference counting and can lead to dangling access if the original instance goes out of scope.// Bad code examplestd::shared_ptr<int> original = std::make_shared<int>(42);std::shared_ptr<int>& ref = original; // reference to the shared_ptr, the reference count is still 1Passing by
const&for short-lived function calls is acceptable when you are certain the caller's instance remains alive. For more information and examples, please refer to CppCoreGuidelines.
Thread Safety
-
When using
MultiThreadedExecutororReentrantCallbackGroup, multiple callbacks may execute in parallel. Protect shared mutable state: If a member variable is read/written by multiple concurrent callbacks, you must protect it with a mutex (e.g.,std::lock_guard).Note: Read-only data (const) or variables accessed by only one thread do not require locking.
Use C++17 scoped_lock which unlocks the mutex after leaving the scope. This way, you can't forget to unlock the mutex.
Templates
-
To keep header files readable, template definitions can be moved into a separate
.tppfile, which is then#included at the bottom of the header. Note that this is purely an organizational convention, the compiler still sees the definitions as part of the header. Use this when template definitions are long enough to obscure the interface.animal.hpp
#pragma oncetemplate <typename T>class Animal {public:void eat(T food);};#include "Animal.tpp"animal.tpp
template <typename T>void Animal<T>::eat(T food) {// consume food}
If you are using clangd for code completion, you might experience clangd not being able to provide it in .tpp files. You can fix this by including the parent header file at the top of the .tpp file. To avoid double inclusion, make sure the header file has #pragma once at the top. This does not affect compilation, it only helps clangd with code navigation and completion.
Testing
- At minimum, write thorough unit tests for core application logic, using mocks where appropriate to isolate the tested functionality. If the application logic is well-separated from the ROS 2 nodes, the nodes themselves do not require unit tests.
- Test ROS 2 nodes, including their communication behavior, as part of integration testing.
- Aim for high test coverage (90-100%).
- Avoid using arbitrary sleeps in tests, as they can make tests non-deterministic and flaky. Instead of sleeping to wait for a ROS message, use synchronization mechanisms or wait for the expected result with a proper timeout.
When doing a PR, and tests are already available, make sure to run them before pushing your code. If you are adding new functionality, make sure to add tests for it and run them before pushing.
Do not push untested code to master branches on Git (or main devel branches)! Doing so can ruin experiments and drones at best!
General points
- Avoid hardcoding values like timeouts, control gains, or buffer sizes. Field conditions often require tuning these values on the fly.
- Rule of Thumb: If a number affects behavior, make it a ROS parameter loaded from
config.yaml. - Why: Recompiling C++ on a drone in freezing weather is miserable. Editing a config file is fast.
- Rule of Thumb: If a number affects behavior, make it a ROS parameter loaded from
If you cannot figure something out, ask in Software Google Spaces for help. If you figure something out that did not work before, note somewhere how you solved it and you can share it in chat. There is a high chance that you (or someone else) will have to do the same thing again.