Skip to main content

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 new keyword, 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_ptr or 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 example
    std::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 1

    Passing 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 MultiThreadedExecutor or ReentrantCallbackGroup, 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.

Scoped lock

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 .tpp file, 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 once

    template <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
    }
clangd

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.
Best Practice

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.

Always test

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.
Support and Troubleshooting :D

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.