chipKIT® Development Platform

Inspired by Arduino™

Efficient Use of C++ in Embedded Systems

Posted 2014-03-27 18:05:07 by Majenko

Those of you planning to begin a project in using C++, hopefully using the MPLAB XC32++ compiler or the chipKIT platform, already know about the object-oriented programming concept of polymorphism. Essential to polymorphism is the concept of a virtual method, a method whose behavior can be overridden within an inheriting class by a method with the same signature.

Virtual methods are implemented using a virtual method table (vtable). The compiler creates a vtable for each class. When an object is created, a pointer to this table is added as a hidden member of the object. The compiler also generates code to initialize the vpointers of its objects to the address of the corresponding vtable. When designing your classes for your embedded application, you will want to keep the extra overhead of the vtable in mind.

Along the same lines, as an embedded-systems developer, you may be surprised by the overhead required by a pure virtual method, since it has no implementation and can therefore never be called directly. Well, the compiler generates error-handling code to handle the case where a pure virtual function is called. In the current XC32 v1.20 compiler release, when a pure virtual method is called, an error message is printed and a C++ exception is thrown, bringing in a fairly large amount of code that should never be executed. While the error handling is useful, we plan to look into simplifying it in future compiler releases to reduce the overhead. For now, you will find that avoiding a pure virtual method will help keep your code size down.

A pure virtual method assigns the function the value 0 rather than providing an implementation. For example:

class Base {
public:

// a normal non-virtual method
    const char* SayHi() {
        return "Hi";
    }

// a normal virtual method
    virtual const char* GetName() {
        return "Base";
    }

// a pure virtual method
    virtual int GetValue() = 0;
};

To avoid the extra overhead, provide a default implementation like this:

class Base {
public:
    // a normal non-virtual method
    const char* SayHi() {
        return "Hi";
    }

    // a normal virtual method
    virtual const char* GetName() {
        return "Base";
    }

    // a normal virtual method
    virtual int GetValue() {
        while(1); /* Error */
    }
};

Another hidden consumer of Flash space to look out for is Run-Time Type Information (RTTI). This C++ mechanism exposes information about an object's data type at runtime. The dynamic_cast<> operation and typeid operator are part of RTTI. When this feature is enabled, additional the type information gets stored in Flash and can consume space even when the application does not use it. When you're not using RTTI, be sure to disable it in your project settings (-fno-rtti). This compiler option can be added to the chipKIT IDE (MPIDE) default compiler option in <install_dir>/hardware/pic32/platforms.txt

C++ exception handling also causes additional code to be generated. Although exceptions provide a way to react to exceptional circumstances (like runtime errors) in an application, they come at a fairly substantial code and data memory cost. When your application doesn't require exception support, you can disable it in your project settings (-fno-exceptions). This option is specified by default when the chipKIT IDE (MPIDE) invokes the chipKIT compiler.

[ Thanks to Jason Kajita for this helpful information ]