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
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.
No comments:
Post a Comment