< Free Open Study > |
Routines created with preprocessor macros call for a few unique considerations. The following rules and examples pertain to using the preprocessor in C++. If you're using a different language or preprocessor, adapt the rules to your situation. Cross-Reference Even if your language doesn't have a macro preprocessor, you can build your own. For details, see Section 30.5, "Building Your Own Programming Tools." Fully parenthesize macro expressions Because macros and their arguments are expanded into code, be careful that they expand the way you want them to. One common problem lies in creating a macro like this one: C++ Example of a Macro That Doesn't Expand Properly#define Cube( a ) a*a*a If you pass this macro nonatomic values for a, it won't do the multiplication properly. If you use the expression Cube( x+1 ), it expands to x+1 * x + 1 * x + 1, which, because of the precedence of the multiplication and addition operators, is not what you want. A better, but still not perfect, version of the macro looks like this: C++ Example of a Macro That Still Doesn't Expand Properly#define Cube( a ) (a)*(a)*(a) This is close, but still no cigar. If you use Cube() in an expression that has operators with higher precedence than multiplication, the (a)*(a)*(a) will be torn apart. To prevent that, enclose the whole expression in parentheses: C++ Example of a Macro That Works#define Cube( a ) ((a)*(a)*(a)) Surround multiple-statement macros with curly braces A macro can have multiple statements, which is a problem if you treat it as if it were a single statement. Here's an example of a macro that's headed for trouble:
This macro is headed for trouble because it doesn't work as a regular function would. As it's shown, the only part of the macro that's executed in the for loop is the first line of the macro: index = (key - 10) / 5; To avoid this problem, surround the macro with curly braces: C++ Example of a Macro with Multiple Statements That Works#define LookupEntry( key, index ) { \ index = (key - 10) / 5; \ index = min( index, MAX_INDEX ); \ index = max( index, MIN_INDEX ); \ } The practice of using macros as substitutes for function calls is generally considered risky and hard to understand bad programming practice so use this technique only if your specific circumstances require it. Name macros that expand to code like routines so that they can be replaced by routines if necessary The convention in C++ for naming macros is to use all capital letters. If the macro can be replaced by a routine, however, name it using the naming convention for routines instead. That way you can replace macros with routines and vice versa without changing anything but the routine involved. Following this recommendation entails some risk. If you commonly use ++ and as side effects (as part of other statements), you'll get burned when you use macros that you think are routines. Considering the other problems with side effects, this is yet another reason to avoid using side effects. Limitations on the Use of Macro RoutinesModern languages like C++ provide numerous alternatives to the use of macros:
Inline RoutinesC++ supports an inline keyword. An inline routine allows the programmer to treat the code as a routine at code-writing time, but the compiler will generally convert each instance of the routine into inline code at compile time. The theory is that inline can help produce highly efficient code that avoids routine-call overhead. Use inline routines sparingly Inline routines violate encapsulation because C++ requires the programmer to put the code for the implementation of the inline routine in the header file, which exposes it to every programmer who uses the header file. Inline routines require a routine's full code to be generated every time the routine is invoked, which for an inline routine of any size will increase code size. That can create problems of its own. The bottom line on inlining for performance reasons is the same as the bottom line on any other coding technique that's motivated by performance: profile the code and measure the improvement. If the anticipated performance gain doesn't justify the bother of profiling the code to verify the improvement, it doesn't justify the erosion in code quality either. cc2e.com/0792 Cross-Reference This is a checklist of considerations about the quality of the routine. For a list of the steps used to build a routine, see the checklist "The Pseudocode Programming Process" in Chapter 9, page 215.
|
< Free Open Study > |