Saturday, December 24, 2011

Using Compile Time Assertions to create better code

In my last entry, ISO releases new standard for the C language, I lamented how the C++11 standard has static_assert() and C11 has _Static_assert(), to give compile time assertions.

Static Assertions are also known as Compile Time Assertions. Their use allows us to put compile time check in our source files, that cause the compilation process to stop if the assertion is not valid. The alternative is to check conditions at run-time incurring performance costs, or having detectable bugs crashing our systems. What if you want to use Static Assertions on compilers that are not up to the latest, just released, standards? You use code that will always fail to compile such as:

int main( void )
{
 int a[ -1 ]; /* Should always fail to compile */
}

From an exchange David Brown and I had on the AVR-GCC list, we came up with the following:

/*
 * Compile Time Assertion:
 *  Usage: STATIC_ASSERT( (msg_count > last_msg), To_Many_Messages_Defined_for_array_size );
 *
 *  STATIC_ASSERT( 1 == 2, One_Not_Equal_To_Two ); that looks like:
 *   assertion_failed_at_line_767_One_Not_Equal_To_Two
 */

#define STATIC_ASSERT_NAME_(line,message)  STATIC_ASSERT_NAME2_(line,message)
#define STATIC_ASSERT_NAME2_(line,message) assertion_failed_at_line_##line##_##message
#define STATIC_ASSERT(claim, message) \
       typedef struct { \
         char STATIC_ASSERT_NAME_(__LINE__,message) [(claim) ? 1 : -1]; \
       } STATIC_ASSERT_NAME_(__LINE__,message)

Admittedly it is a bit of a kludge, however it is still better than letting known issues make it into working code. The native compiler versions should be used when they exist. For example I use the above code myself to detect when an enumerated value list is about to overflow the size of a 8-bit byte:

enum MessagesTag{
 Message0,
 Message1,
 Message2,
 ...
 Messages_Last
};
STATIC_ASSERT( Messages_Last >= 255U, Messages_No_Longer_Fits_In_Byte );

Some unsuspecting person, such as a future Me, could add a number of Messages over the years, and not realize there are now to many for the code to operate correctly.

For more information on compile time assertions check out:


1 comment:

  1. Bob,

    Nice. I've had my own implementation of a C static assertion macro, but I like yours better.

    Even though C11 is here, it will be a while before it is widely supported in our world (embedded), and it will be even longer before firmware engineers start writing C11 code. Heck, I still see C90 code all the time!

    Bottom line: this set of macros will have a very long -- and useful -- shelf life. Catch problems as early as possible.

    ReplyDelete