Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ data containers for embedded systems

I work with embedded systems. Usually that means I have a small microcontroller with 64 - 512 KB of RAM and 128 - 1024 KB of flash memory like STM32. I prefer to use C++ for programming such systems. But I haven't still found acceptable way to deal with common data structures such as stack, queue, map, etc.

Of course, STL has all of them and many other handy things, but most STL containers require support of exceptions and dynamic memory allocation, which are usually undesirable in embedded programming.

I know we can avoid this problem by using a custom allocator, we could allocate memory from a static object pool or something similar. However the main problem I see that we can't reliably handle a case when it's not enough allocated space for inserting new element into container. STL and other stl-like libraries I've met propose only two options:

  • asserts. Which means system fails when it's not enough allocated space.
  • callbacks. A bit better but isn't still convenient for me.

    q.push(newElem); /* fails or just calls predefined callback
                      * when not enough space in queue.
                      */
    

Maybe I'm wrong. But on my opinion the best way is to have returned statuses for notifying caller that it's not enough memory for new element in a container. I would like to decide what to do with that error myself. For example, I would like to drop new element, send a message to debug log and resume normal program flow. It looks more reliable from my perspective.

In other words, I would like to have something like this:

queue<uint32_t, 128> q;
// some code ... 
queue::status sts = q.push(newElem);
if (sts != queue::OK)
    LOG("Not enough space in queue\r\n");

// continue normal program execution ...

Somebody has a suggestion how to deal with that thing?

like image 340
Raman Sakovich Avatar asked Oct 25 '17 14:10

Raman Sakovich


1 Answers

You could use a custom allocator that throws std::bad_alloc when it runs out of space, then in your code wrap the section that depends on the insert succeeding in an exception handling block and deal with the allocation failure in the catch clause.

std::queue <message> Queue;

try
{
  Queue.insert (message {"hello world"});
}
catch (std::bad_alloc const & Error)
{
  LOG (Error.what ());
}

For simple scenarios this is a bit verbose, by with care (using RAII) it can be a powerful tool. In addition, if you have these simplistic scenarios allot, you can hide this pattern in a template.

template <typename fn>
void log_failure_and_contiue (fn Fn)
{
    try
    {
      Fn ();
    }
    catch (std::bad_alloc const & Error)
    {
      LOG (Error.what ());
    }
}

std::queue <message> Queue;

log_failure_and_continue ([&] {
  Queue.insert ({ "Hello World!" });
});

From a performance perspective, most compilers implement zero cost exception handling, so you only take a hit if an exception is thrown, which I would think in a this case should be rare.

like image 125
nate Avatar answered Nov 07 '22 19:11

nate