Favorite Tricks: Static Assert

One of my favorite embedded C/C++ “tricks” is to make frequent use of the compiler to detect certain errors at compile-time rather than at run-time. The mechanism used for this style of error detection is a “static assert,” and embedded software engineers would be wise to sprinkle a few of these in their projects. The benefits include:

  • Save Time
    • An error detected while compiling saves an engineer time versus trying to debug or detect the same issue at run-time. Time is money!
  • Document Assumptions
    • Each static assert communicates additional information to future engineers that inherit the project.
  • Code Maintenance
    • Helps prevent a future embedded software engineer from missing related data or associations that the compiler would not normally be able to enforce.
  • Portability
    • Use a static assert to make sure data structures are packed in the same manner, even if the project changes compilers.

Additional examples and explanations may be found here: http://stackoverflow.com/a/1647921/49956

Before proceeding, let us first note how to create a static assert template or macro. Key point to remember: a static assert is only able to evaluate data or data structures that are defined at compile time.

For pre-C++11 code, developers must create or acquire a custom template such as the example shown in this answer on Stack Overflow: http://stackoverflow.com/a/6765840/49956

For C code, the exact approach to create a static assert macro might vary based on the compiler, but the following site lists various approaches: http://www.pixelbeat.org/programming/gcc/static_assert.html.

For C++11 based code, simply use the officially defined static_assert(), see here for details: cppreference.com.

Now that we have established a handful of approaches to define a static assert macro or template, lets dive into an example.

In this example, a module provides various methods using an enumerated type as a common input parameter. Internally to the module is a static lookup array (or table) with additional data relevant to each enumerated value. As a standard optimization the module uses the enum value itself as an index into the internal table. The author is worried about future code maintenance and wants to ensure that any new enumerated values will be properly reflected in the internal look up table (or array). How? Here is a example showing one approach.

public header:

enum SwitchInputs {
  INPUT_1,
  INPUT_2,
  INPUT_COUNT
};

Internal module code(C++11):

static uint32_t* m_InputAddr[] = {
 (uint32_t*)0xA0000000,
 (uint32_t*)0xA0000010
};
static_assert(sizeof(m_InputAddr)/sizeof(m_InputAddr[0]) == INPUT_COUNT, "internal lookup table size does not match enum count!");

With that static assert in place, the author can now deliver the module with high confidence that a future software engineer will quickly discover that adding another value to the enum is not enough to expand the functionality of the module. The compiler will issue an error and the project will not build, giving the new engineer a clear signal that additional effort is needed! As long as the number of elements in the m_InputAddr[] array matches the enum count, the code compiles with no errors.

With an example behind us, lets enumerate additional ideas for using static asserts in an embedded software project.

  • Hardware Register Definitions
    • This is a very common embedded software concern. For example, a microcontroller may have 3 UART IP blocks, each with the exact same hardware registers merely located at different internal memory addresses.
    • The engineer would likely define a data structure representing a single UART and simply allocate the structure at each UART’s unique address.
    • To verify that the compiler created a matching data structure, use static asserts to check the sizeof() the struct and perhaps use the offsetof() macro to verify that one or two internal members of the struct are located as defined in the device datasheet.
    • Of course, know your compiler: many (if not all) provide for a “packed” pragma or other syntax to force the compiler to pack a data structure, avoiding compiler defined padding between structure members.
  • Packet Size Checks
    • If the device transmits and receives binary data packets defined in an external document, an embedded software engineer may represent a packet with a data structure (e.g. struct Packet) and simply receive and transmit packets directly to/from one or more allocated data structures.
    • Use a static assert on the sizeof(Packet) to confirm the compiler created a data struct that matches the size of the externally documented structure.
    • Consider using the offsetof macro to verify the exact location inside the packet of one or two members of the struct. Again, this verifies that the compiler’s generated addresses for the data structure match the externally documented packet structure.
  • Static Buffer re-use
    • Memory is often at a premium in an embedded system. Perhaps two modules that are known to always operate at different orthogonal time periods are required to use a common static RAM buffer in order to save memory. Each module author could add an internal static assert to verify that the shared static buffer is large enough for the module’s requirements. Again, this is great protection against mistakes by future software engineers taking over maintenance or development of the code.
  • Memory Alignment
    • Some hardware IP cores expect memory to be aligned, sometimes to an extreme, such as a 1MB boundary. Make sure the compiler is placing buffers or data structure members as expected by using a static assert to confirm memory alignment.
  • Versions
    • Use a static assert to verify that an external library meets some minimum version requirement. This assumes the library provides its version via a static compile time mechanism.

Is there a downside to using static asserts? Not really. But, perhaps the only “downside” for me personally: with C++11 formally supporting static_assert(), I tend to always want to use a C++11 supporting compiler, even in my embedded firmware projects! And that is another topic entirely.  🙂

How have you used static asserts in your embedded software and firmware projects?

 


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s