Macro Functions

The #define directive can also be used to create macro functions. A macro function is created using #define. Macros take arguments much like functions do. The preprocessor will substitute the substitution string for whatever argument it is given. For example, you can define the macro TWICE as

#define TWICE(x) ( (x) * 2 )

and then write in your code

TWICE(4)

The entire string TWICE(4) will be removed, and the value 8 will be substituted! When the precompiler sees the 4, it will substitute ( (4) * 2 ), which will then evaluate to 4 * 2 or 8.

A macro can have more than one parameter, and each parameter can be used repeatedly in the replacement text. Two common macros are MAX and MIN:

#define MAX(x,y) ( (x) > (y) ? (x) : (y) )
#define MIN(x,y) ( (x) < (y) ? (x) : (y) )

Note that in a macro function definition, the opening parenthesis for the parameter list must immediately follow the macro name, with no spaces. The preprocessor is not as forgiving of white space as is the compiler.

If you were to write

#define MAX (x,y) ( (x) > (y) ? (x) : (y) )

and then to try to use MAX like this,

int x = 5, y = 7, z;
z = MAX(x,y);

the intermediate code would be

int x = 5, y = 7, z;
z = (x,y) ( (x) > (y) ? (x) : (y) ) (x,y)

A simple text substitution would be done, rather than invoking the macro function. So the token MAX would have substituted for it (x,y) ( (x) > (y) ? (x) : (y) ), and then that would be followed by the (x,y) that followed MAX.

When the space between MAX and (x,y) is removed, however, the intermediate code becomes

int x = 5, y = 7, z;
z = 7;

Why All the Parentheses?

You may be wondering why there are so many parentheses in many of the macros presented so far. The preprocessor does not demand that parentheses be placed around the arguments in the substitution string. However, the parentheses help you to avoid unwanted side effects when you pass complicated values to a macro. For example, if you define MAX as

#define MAX(x,y) x > y ? x : y

and pass in the values 5 and 7, the macro works as intended. But if you pass in a more complicated expression, you'll get unintended results, as shown in Listing 21.2.

Listing 21.2. Using Parentheses in Macros
 0:  // Listing 21.2 Macro Expansion
 1:  #include <iostream>
 2:
 3:  #define CUBE(a) ( (a) * (a) * (a) )
 4:  #define THREE(a) a * a * a
 5:
 6:  int main()
 7:  {
 8:      long x = 5;
 9:      long y = CUBE(x);
10:      long z = THREE(x);
11:
12:      std::cout << "y: " << y << std::endl;
13:      std::cout << "z: " << z << std::endl;
14:
15:      long a = 5, b = 7;
16:      y = CUBE(a+b);
17:      z = THREE(a+b);
18:
19:      std::cout << "y: " << y << std::endl;
20:      std::cout << "z: " << z << std::endl;
21:      return 0;
22:  }


y: 125
z: 125
y: 1728
z: 82
						

On line 3 the macro CUBE is defined, with the argument a put into parentheses each time it is used. On line 4 the macro THREE is defined without the parentheses.


In the first use of these macros, the value 5 is given as the parameter, and both macros work fine. CUBE(5) expands to ( (5) * (5) * (5) ), which evaluates to 125, and THREE(5) expands to 5 * 5 * 5, which also evaluates to 125.

In the second use, on lines 15–17, the parameter is 5 + 7. In this case, CUBE(5+7) evaluates to

( (5+7) * (5+7) * (5+7) )

which evaluates to

( (12) * (12) * (12) )

which in turn evaluates to 1,728. THREE(5+7), however, evaluates to

5 + 7 * 5 + 7 * 5 + 7

Because multiplication has a higher precedence than addition, this becomes

5 + (7 * 5) + (7 * 5) + 7

which evaluates to

5 + (35) + (35) + 7

which finally evaluates to 82.

Macros Versus Functions and Templates

Macros suffer from four problems in C++. The first is that they can be confusing if they get large, because all macros must be defined on one line. You can extend that line by using the backslash character (), but large macros quickly become difficult to manage.

The second problem is that macros are expanded in line each time they are used. This means that if a macro is used a dozen times, the substitution will appear 12 times in your program, rather than once as a function call will. On the other hand, they are usually quicker than a function call because the overhead of a function call is avoided.

The fact that they are expanded inline leads to the third problem, which is that the macro does not appear in the intermediate source code used by the compiler, and thus is unavailable in most debuggers. This makes debugging macros tricky.

The final problem, however, is the biggest: Macros are not type-safe. While it is convenient that absolutely any argument can be used with a macro, this completely undermines the strong typing of C++ and so is anathema to C++ programmers. Templates overcome this problem, as you'll see in two hours.

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

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