Embedded C++:To Use Stl or Not

Embedded C++ : to use STL or not?

Super-safe & lose much of what
constitutes C++ (imo, it's more than
just the language definition) and
maybe run into problems later or have
to add lots of exception handling &
maybe some other code now?

We have a similar debate in the game world and people come down on both sides. Regarding the quoted part, why would you be concerned about losing "much of what constitutes C++"? If it's not pragmatic, don't use it. It shouldn't matter if it's "C++" or not.

Run some tests. Can you get around STL's memory management in ways that satisfy you? If so, was it worth the effort? A lot of problems STL and boost are designed to solve just plain don't come up if you design to avoid haphazard dynamic memory allocation... does STL solve a specific problem you face?

Lots of people have tackled STL in tight environments and been happy with it. Lots of people just avoid it. Some people propose entirely new standards. I don't think there's one right answer.

STL in embedded environment

STL has quite a few problems with it(as documented here by EASTL), on an embedded system, or small scale system, the main problem is generally the way in which it manages (its) memory. a good example of this was the PSP port of Aquaria.

My advise though is first test, before following assumptions, if the test are shows your using just too much space/processor cycles, then maybe an optimization or two could push it into the realm of 'usable'.

Finally, boost is template based, so if your looking at the size of generated template code, it'll suffer the same as STL.

Edit/Update:

To clear up my last statement (which was just refering to boost VS STL). In C, you can (ab)use the same code to do the same job on different structures sharing the same header (or layout), but with templates, each type might get its own copy (I've never test if any compilers are smart enough to do this if 'optimize for size' is enbaled), even though it exactly the same(on a machine/assembly level) as one thats just been generated. boost has the advantage of being a lot cleaner to read, and having far more things crammed into it, but that can lead to long compile times due to a copius amount of (somtimes huge) headers. STL gains because you can pass your project around and not require a download/accompanyment of boost.

STL within embedded system with very limited memory

This question is sort of confused and weird. First, let's clear up some misconceptions.

You mention "stack, queue, deque" by name. Well, two of these are not containers. stack and queue are container adapters. See, they don't actually directly store the elements; they simply mediate the interface to them. stack ensures that you can only push, pop, and get the top element. queue ensures that you can only push-back, pop-front, and get the front element (thought it also lets you get the back element).

The container adapters actually take as one of their template parameters the actual container type to use. So you could use a stack with a std::list if you want. I wouldn't necessarily suggest it (depending on your use case), but you could.

The container adapters don't care about memory; it's the containers that they use that allocate memory.

If you're running in such a tightly memory limited system, you're not going to find the standard containers to be terribly friendly. Even if you use allocators to give them fixed-size memory buffers, the only thing those allocators can do to stop the actual container from allocating more memory is to throw an exception.

For example, if you have a vector that needs to work within 2KB of memory, if it has a size of 1KB, and tries to allocate 2.5KB more, the allocator cannot simply return 2KB. It can either return 2.5KB as requested or throw std::bad_alloc. Those are your only two options. There is no way for the allocator to tell the vector that it can get more memory than what it has, but not as much as it wants.

Similarly, the allocator is required to provide new memory, freshly allocated memory that can be copied into. It is not supposed to provide the same spot of memory only with more of it available. Doing so will likely cause problems in some implementations.

Allocators are intended to provide different regions of memory for access; they're not well designed for limiting the size of the container itself.

My suggesting is to track down a copy of EASTL. It's really designed for this sort of thing. The Github repo I linked you to has some bug fixes and so forth, but it's still mostly the same. It's not a bad bit of code. Their STL-like containers provide most of the interface, so they can be mostly drop-in replacements. But they provide special functionality to specifically control memory allocations.

Embedded C++ : to use exceptions or not?

In terms of performance, my understanding is that exceptions actually reduce the size and increase the performance of the normal execution paths of code, but make the exceptional/error paths more expensive. (often a lot more expensive).

So if your only concern is performance, I would say don't worry about later. If today's CPU can handle it, then tomorrows will as well.

However. In my opinion, exceptions are one of those features that require programmers to be smarter all of the time than programmers can be reasonably be expected to be. So I say - if you can stay away from exception based code. Stay away.

Have a look at Raymond Chen's Cleaner, more elegant, and harder to recognize. He says it better than I could.

How to design a good interface for embedded system?

You should look at the concept known as "opaque type" or "opaque pointers". It is a design pattern in C which allows you to use private encapsulation by utilizing forward declaration of the struct. In order for that to work, your functions must be using pointers and allocation must be done inside your code.

In low-end embedded systems, dynamic allocation should never be used. Allocation of opaque types is done with static memory pools. See Static allocation of opaque data types.

Code example of how to implement a static memory pool: https://stackoverflow.com/a/54999410/584518

When should I use type abstraction in embedded systems

I use type abstraction very rarely. Here are my arguments, sorted in increasing order of subjectivity:

  1. Local variables are different from struct members and arrays in the sense that you want them to fit in a register. On a 32b/64b target, a local int16_t can make code slower compared to a local int since the compiler will have to add operations to /force/ overflow according to the semantics of int16_t. While C99 defines an intfast_t typedef, AFAIK a plain int will fit in a register just as well, and it sure is a shorter name.

  2. Organizations which like these typedefs almost invariably end up with several of them (INT32, int32_t, INT32_T, ad infinitum). Organizations using built-in types are thus better off, in a way, having just one set of names. I wish people used the typedefs from stdint.h or windows.h or anything existing; and when a target doesn't have that .h file, how hard is it to add one?

  3. The typedefs can theoretically aid portability, but I, for one, never gained a thing from them. Is there a useful system you can port from a 32b target to a 16b one? Is there a 16b system that isn't trivial to port to a 32b target? Moreover, if most vars are ints, you'll actually gain something from the 32 bits on the new target, but if they are int16_t, you won't. And the places which are hard to port tend to require manual inspection anyway; before you try a port, you don't know where they are. Now, if someone thinks it's so easy to port things if you have typedefs all over the place - when time comes to port, which happens to few systems, write a script converting all names in the code base. This should work according to the "no manual inspection required" logic, and it postpones the effort to the point in time where it actually gives benefit.

  4. Now if portability may be a theoretical benefit of the typedefs, readability sure goes down the drain. Just look at stdint.h: {int,uint}{max,fast,least}{8,16,32,64}_t. Lots of types. A program has lots of variables; is it really that easy to understand which need to be int_fast16_t and which need to be uint_least32_t? How many times are we silently converting between them, making them entirely pointless? (I particularly like BOOL/Bool/eBool/boolean/bool/int conversions. Every program written by an orderly organization mandating typedefs is littered with that).

  5. Of course in C++ we could make the type system more strict, by wrapping numbers in template class instantiations with overloaded operators and stuff. This means that you'll now get error messages of the form "class Number<int,Least,32> has no operator+ overload for argument of type class Number<unsigned long long,Fast,64>, candidates are..." I don't call this "readability", either. Your chances of implementing these wrapper classes correctly are microscopic, and most of the time you'll wait for the innumerable template instantiations to compile.



Related Topics



Leave a reply



Submit