5.10.1.  Inlining Versus Macro Expansion

[ fromfile: functions.xml id: inlinevsmacro ]

Macro expansion is a mechanism for placing code inline by means of a preprocessor directive:

#define MACRO_ID expr
   

This is different from an inline function.

Macro expansion provides no type-checking for arguments. It is essentially an editing operation: each occurrence of MACRO_ID is replaced by expr. Careful use of parentheses in macros is necessary to avoid precedence errors, but parentheses won't solve all the problems associated with macros, as you see in Example 5.19. Errors caused by macros can lead to strange (and unclear) compiler errors or, more dangerously, to invalid results. Example 5.19 demonstrates the latter situation.

Example 5.19. src/functions/inlinetst.cpp

// Inline functions vs macros

#include <iostream>
#define  BADABS(X)   (((X) < 0)? -(X) : X)
#define  BADSQR(X) (X * X)
#define  BADCUBE(X) (X) * (X) * (X)

using namespace std;

inline double square(double x) {
    return x * x ;
}

inline double cube(double x) {
    return x * x * x;
}

inline int absval(int n) {
    return (n >= 0) ? n : -n;
}

int main() {
    cout << "Comparing inline and #define\n" ;
    double  t = 30.0;
    int i = 8, j = 8, k = 8, n = 8; 
    cout << "\nBADSQR(t + 8) = " << BADSQR(t + 8) 
            << "\nsquare(t + 8) = " << square(t + 8)
            << "\nBADCUBE(++i) = " << BADCUBE(++i)
            << "\ni = " << i
            << "\ncube(++j) = " << cube(++j)
            << "\nj = " << j
            << "\nBADABS(++k) = " << BADABS(++k)
            << "\nk = " << k
            << "\nabsval(++n) = " << absval(++n)
            << "\nn = " << n << endl;
}

<include src="src/functions/inlinetst.cpp" href="src/functions/inlinetst.cpp" id="inlinetstcpp" mode="cpp"/>


Here is its output.

Comparing inline and #define

BADSQR(t + 8) = 278
square(t + 8) = 1444
BADCUBE(++i) = 1100
 i = 11
cube(++j) = 729
j = 9
BADABS(++k) = 10
k = 10
absval(++n) = 9
n = 9

BADSQR(t+8) gives the wrong results because

    BADSQR(t + 8)
=   (t + 8 * t + 8)         (preprocessor)
=   (30.0 + 8 * 30.0 + 8)   (compiler)
=   (30 + 240 + 8)          (runtime)
=   278

More troubling, however, are the errors produced by BADCUBE and BADABS which both have sufficient parentheses to prevent the kind of error that occurred with BADSQR. Here is what happened with BADCUBE(++i).

     BADCUBE(++i)
=   ((++i) * (++i)) * (++i)   // left associativity 
=   ((10) * (10)) * (11)
=   1100

Preprocessor macros are used mostly for the following:

  1. #ifndef/#define/#endif wrapping around header files to avoid multiple inclusion

  2. #ifdef/#else/#endif to conditionally compile some parts of code but not others

  3. __FILE__ and __LINE__ macros for debugging and profiling

  4. In Qt, macros are used with code-generation to add dynamic features to QObjects, such as properties, signals and slots.

When possible, inline functions (or template functions) should be used instead of macros for code substitutions.