Search code examples
c++containersstd

Standard library container that doesn't require object reallocation / move operations?


I built a C++ wrapper around the FreeRTOS timer API. My class statically allocates the timer control block which is operated by a FreeRTOS thread in the background. This means that if I move or copy this object, the control block will be moved/copied as well BUT the thread wont notice that. Because of that I consider the object non-copyable and non-movable.

Here's the outline:

#include <cstdint>
#include <concepts>

template <std::invocable Cb>
class timer
{
public:
    timer() = default;
    timer(Cb cb, TickType_t timer_period, bool auto_reload = false)
        : cb_{ cb }
    {
        xTimerCreateStatic("timer", timer_period, auto_reload, static_cast<void*>(this), &timer::timer_expired_cb, &buf_);
    }

    timer(const timer&) = delete;
    timer(timer&&) = delete;
    auto operator=(const timer&) = delete;
    auto operator=(timer&&) = delete;
    // ...
private:
    Cb cb_;
    TimerHandle_t handle_;
    StaticTimer_t buf_;
};

Now I want to push multiple of this timer objects into a C++ container which I can dynamically extend or shrink as objects enter or leave the container. Is there a stdlib container that doesn't require objects to be moveable or copyable and still provides all the functionality?


Solution

  • I see four basic options:

    • std::list<timer>: This might be one of the very rare cases when using std::list is the best option. Insertion into the list must be done via one of the emplace member functions, as you cannot move in an already existing object.
    • std::vector<std::unique_ptr<timer>>: In case construction of timer objects is not directly associated with keeping them in your container. This has the disadvantage that accessing and removing entries slightly more work than std::list. But your timer factory doesn't need to know how you plan to store the object.
    • std::set<timer>: You stated that you have new objects arriving and departing. If you have a lot of them, maybe you want to avoid finding them in a linear container. std::set also has no reallocations and offers you a nice interface for finding and erasing objects. But clearly it has more overhead than the other two suggestions.
    • std::array<std::optional<timer>, N>: If you know the maximal number N of timers you will see at runtime, consider a fixed-size array with optionals in it. This has no pointer-indirections but you likely have a number of branches when searching an item.