Monday, June 30, 2008

#defines use and abuse

The subject of #defines might seem arcane and anachronistic in the modern world of C++ constants and templates, but if you are doing safety critical real time embedded coding there is a good chance your using a C or C99 compiler and you need to use #defines.

If we are going to use #defines we should have a sense of our requirements

  • We want to be MISRA compliant (no magic numbers)

  • We want the code to be as close to self documenting as possible

  • We want the code to be modular and easily adapted to new projects
An interesting goal for self documenting is what Knuth called literate programming (Wikipedia) we can summarize this as saying the programs should endevor to be human readible. Though, source code is unlikely to end up on anyone's summer reading list we do want it to precisely convey what we are doing.

To support this here are a couple of rules I use

  • Use all caps for #defines.

  • #defines that individually control code paths should be named for their functionality not the application aspects that require their functionality

  • #defines that are used like functions should be named like functions


All caps. We start with this one, because it is simple. One should note that any standard for this is as good as any other. If your code standard already has a convention, just keep using it. If not why not adopt this convention and call it done.

#defines that control code paths should be named for their functionality not the application that motivates their use.

This takes a little more effort to explain. For example imagine we are working on traction control (TC) for a vehicle, let's say a Jeep. Let's also assume that all our previous vehicles were two wheel drive, and to make this example simple front wheel drive. For the case of front wheel drive we will use a very simple estimate of vehicle speed, the two undriven rear wheels (left rear LR, and right rear RR). In this case it might be tempting to do the following

#if VEHICLE != JEEP
speed_ref = (LR+RR)/2
#else
speed_ref = {something more complex}
#endif


This is an example of code that is momentairly fine, but is a disaster waiting to happen. Let's assume our next car is a Lincoln Navigator (also 4 wheel drive). Now we have two options


#if VEHICLE != JEEP && VEHICLE !=LINCOLN


This is ok, but it clearly does not scale well with more vehicles. Even worse, and yes I have seen code like this is at the top of the TC module

#if VEHICLE == LINCOLN
#undef VEHICLE
#define VEHICLE JEEP
#endif



If we look at our rule the define has to be named for what it does not the application at hand. In this case we would have written something like

#if REAR_SPEED_REFERENCE_VALID
speed_ref = (LR+RR)/2
#else
speed_ref = {something more complex}
#endif


The define now looks like

#if VEHICLE == JEEP
#define REAR_SPEED_REFERENCE_VALID FALSE
#endif


#defines that are used like functions should be named like functions

Let's assume we have a #define that returns a varying quantity. For example let's assume we we need to get the state of the transmission (2 wheel drive, all wheel drive). Let's also assume that the name of the function varies in different applications and we want a consistent interface to our functions we could use an inline function, but that is just a recommendation to the compiler. We could also use

#define TRANSMISSION_STATE getTransmissionState()

Unfortunately, in the code this suggests a static quantity.

if TRANSMISSION_STATE==TwoWheelDrive

We can improve this with

#define TRANSMISSION_STATE() getTransmissionState()
if TRANSMISSION_STATE()==TwoWheelDrive


Even better is

#define TRANSMISSION_STATE getTransmissionState

This still requires the () in the code and further won't let you define this as a constant quantity without some effore. For example

#define TRANSMISSION_STATE

Will result in compilation errors where it is used, for example the if above.
So with a couple of simple rules we can write code today that we will still be happy with in a year or two.