5.11.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 very 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 we will see in Example 5.19. Errors caused by macros can lead to very strange (and unclear) compiler errors or, more dangerously, to invalid results. This example 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)
            << "\n i = " << i
            << "\ncube(++j) = " << cube(++j)
            << "\nj = " << j
            << "\nBADABS(++k) = " << BADABS(++k)
            << "\nk = " << k
            << "\nabsval(++n) = " << absval(++n)
            << "\nn = " << n << endl;
}


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 us 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 occured with BADSQR. Here is what happened with BADCUBE(++i).

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

In general, code substitution macros should be avoided. They are regarded as evil by most serious C++ programmers. 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

As a rule, we use inline functions in favor of macros for code substitutions. The exception to this rule is the use of Qt macros that insert code into programs that use certain Qt classes. It is easy to see why some C++ experts look very suspiciously at Qt's use of macros.