Technology
 

Implementing Asserts

From Software Wiki

Contents

[edit] Swallow the Semicolon

The primary “gotcha” in implementing asserts is to forget to handle the following case...

if( expression) 

   assert( other_expression);

else

   do_stuff;

Whatever implementation you choose, that example should compile correctly without warnings whether asserts are compiled in or compiled out.

Furthermore, when compiled out, it should vanish entirely from the generated machine code.

[edit] Preprocessor asserts.

The simplest version does not permit being switched off...

#if EXPRESSION

#error Whinge, Whinge, Moan, Despair!

#endif

[edit] Compile time asserts.

This version relies on the compiler to warn when a typedef to an array of negative size if declared. The nice thing is it disappears entirely from the compiled code.

#define CompileTimeAssert(ex) do {\

  typedef char COMPILE_TIME_ASSERTION_FAILURE[(ex) ? 1 : -1];\

} while(0)

The above version can only be used within the body of a function.

At file scope, you need this one...

#define FileScopedCompileTimeAssert(ex) \

extern char COMPILE_TIME_ASSERTION_FAILURE[(ex) ? 1 : -1];

[edit] Link Time Asserts.

The gcc linker will check that sections will fit into the assigned memory region. However, if the load memory address (LMA) is different from the virtual memory address (VMA) it will silently overflow your rom region. Use the linker script to place a Magic Number section at the end of rom to force an error.

[edit] Trust Your Exception Handlers to do The Right Thing.

If you are hunting an extra burst of speed from your assert implementation, no code is faster than no code! If your CPU traps invalid pointer dereferences anyway, implement “assert_valid_pointer” as an empty no operation.

Read your CPU specsheet and create a pallet of assertion definitions that can be switched off and entrusted to the hardware.

[edit] Run time Asserts.

The following code also does The Right Thing if you check your code with splint.

#ifndef S_SPLINT_S

   #if ERROR_ENABLE_ASSERT

      #if 1 /* change to 0 to save space */

       #define error_Assert(expression) ((expression) ? (void)NULL : error_LocalAssert(#expression, __FILE__, __LINE__))

      #else

       #define error_Assert(expression) ((expression) ? (void)NULL : error_LocalAssert((char *)NULL, __FILE__, __LINE__))

      #endif

   #else

      #define error_Assert(expression) ((void)NULL)

   #endif

#else

/* to get splint side effect checking */

extern /*@noreturnwhenfalse@*/ void error_Assert(/*@sef@*/ bool_t expression);

#endif

A valid criticism of this implementation from the embedded system point of view is it takes way too much rom space to store all those expressions __FILE__ and __LINE__ string constants.

A better implementation would use a non-portable one assembler instruction hack to just use the PC counter.

Previous: Programming Style with Asserts Next: Maintaining Service in the Presence of Errors