Macro Routines and Inline Routines

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 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:

Example 7-10. 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:

Example 7-11. 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:

Example 7-12. 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:

Example 7-14. 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 Routines

Modern languages like C++ provide numerous alternatives to the use of macros:

  • const for declaring constant values

  • inline for defining functions that will be compiled as inline code

  • template for defining standard operations like min, max, and so on in a type-safe way

  • enum for defining enumerated types

  • typedef for defining simple type substitutions

Limitations on the Use of Macro Routines

As Bjarne Stroustrup, designer of C++ points out, "Almost every macro demonstrates a flaw in the programming language, in the program, or in the programmer…. When you use macros, you should expect inferior service from tools such as debuggers, cross-reference tools, and profilers" (Stroustrup 1997). Macros are useful for supporting conditional compilation—see Debugging Aids—but careful programmers generally use a macro as an alternative to a routine only as a last resort.

Inline Routines

C++ 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 in "Chapter 9".

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.148.113.229