Object-oriented approach to embedded programming (with C++)

In my real, daytime job I have been working with object-oriented languages like Java, C++, C# and python for several years. On the contrary, hobby stuff with microcontrollers has always been pure C or assembly. Although I understand perfectly resource constraints of the embedded world, the limits are not totally as strict as they used to be: modern microcontrollers tend to have more and more memory and processing power. Because I also very firmly believe that object-oriented design really rules when creating complex systems, I decided to do a little expermenting what it would mean in Pico]OS embedded world.

msp430_LaunchpadI decided to concentrate on code and data size, not worrying about  performance (yet). For test system I selected a MSP430 Launchpad with msp430g2533 processor, which has 16 kB flash and 512 B ram. I already had a small test program which uses two Pico]OS threads to blink board leds so it would be easy to convert that to oo-style and see what it yields.

Despite of it’s dangers, I decided to use C++ as it is probably easiest language to get started with in my environment: I use GCC, which provides g++ compiler for all target CPUs I have and calling C apis is also very easy and efficient from C++.

I decided to create a thin C++ wrapper for Pico]OS task and semaphore functions, which are the only ones required by test program. The C api has functions like this:

POSTASK_t posTaskCreate(POSTASKFUNC_t funcptr,
                        void *funcarg,
                        VAR_t priority,
                        UINT_t stacksize);
void posTaskYield(void);

POSSEMA_t posSemaCreate(INT_t initcount);
void posSemaDestroy(POSSEMA_t sema);

Now the “pos” prefix becomes a C++ namespace, “Task” and “Sema” become classes and rest of function names are left as methods. Functions without a “handle” become static methods. Like this:


namespace pos {

 class Sema
 {
 public:
   inline void create(INT_t initcount) {

     handle = ::posSemaCreate(initcount);
   };

   inline void destroy();

 private:
   POSSEMA_t handle;
 };

 class Task
 {
 public:
   inline void create(POSTASKFUNC_t funcptr,...);
   static inline void yield();

 private:
   POSTASK_t handle;
 };
}

For wrapper like this, data size should be the same as with using C-style handle pointer. Also, use of inline functions should allow compiler to optimize wrapper call to something similar as done with plain old C.

Ok, time for first tests. I compiled the original test program without optimization (debug version) and with gcc optimization (release version, -Os in this case). This was what I got:

        text  data  bss   dec   hex   filename
debug   7086  2     172   7260  1c5c  msp430-deb/out/test.elf
release 3682  2     160   3844  f04   msp430-rel/out/test.elf

After converting all posTask* and posSema* function calls to wrapper class method calls, I compiled the program again:

text  data  bss   dec   hex   filename
debug   7488  2     172   7662  1dee  msp430-deb/out/plus.elf
release 3682  2     160   3844  f04   msp430-rel/out/plus.elf

Ram size usage is the same for C and C++ versions. There is a slight increase of code size in debug version, probably because compiler is not inlining the wrapper methods. However, the C++ release version (compiled with g++ -Os) has same sizes as the original.

This shows that at least for this case C++ is very usable for an embedded system also. But I think that if you get carried away and start using dynamic memory allocation or virtual methods heavily it won’t be very lightweight anymore. But it might still be suitable for some designs, at least when a more powerful microcontroller (for example Arm Cortex-M) is used.

Wrapper library used in this test is available at github.

Ari Suutari

Father of three 🙂
{ Electronics | Music | Computer | Motorbike } hobbyist.
Factory IT professional.
FreeBSD since day one.

Facebook LinkedIn