I l@ve RuBoard Previous Section Next Section

10.4 Parameterized Macros

So far we have discussed only simple #defines or macros. Macros can take parameters. The following macro computes the square of a number:

#define SQR(x)  ((x) * (x))     /* Square a number */ 

There can be no space between the macro name (SQR in this example) and the open parenthesis.

When used, the macro replaces x with the text of its argument. SQR(5) expands to ((5) * (5)). It is a good rule always to put parentheses around the parameters of a macro. Example 10-7 illustrates the problems that can occur if this rule is not followed:

Example 10-7. sqr/sqr.cpp
#include <iostream>

#define SQR(x) (x * x)

int main(  )
{
    int counter;    // counter for loop

    for (counter = 0; counter < 5; ++counter) {
        std::cout << "x " << (counter+1) << 
                " x squared " << SQR(counter+1) << '\n';
    }
    return (0);
}

Question 10-5: What does the above program output? (Try running it on your machine.) Why did it output what it did? (Try checking the output of the preprocessor.)

The keep-it-simple system of programming prevents us from using the increment (++) and decrement (--) operators except on a line by themselves. When used in an expression, they cause side effects, and this can lead to unexpected results, as illustrated in Example 10-8.

Example 10-8. sqr-i/sqr-i.cpp
#include <iostream>

#define SQR(x) ((x) * (x))

int main(  )
{
    int counter;    /* counter for loop */

    counter = 0;
    while (counter < 5)
        std::cout << "x " << (counter+1) << 
                " x squared " << SQR(++counter) << '\n';
    return (0);
}

Why does this not produce the expected output? How much does the counter go up each time?

In the program shown in Example 10-8, the SQR(++counter) is expanded to ((++counter) * (++counter)) in this case. The result is that counter goes up by 2 each time through the loop. The actual result of this expression is system-dependent.

Question 10-6: Example 10-9 tells us we have an undefined variable, but our only variable name is counter. Why?

Example 10-9. rec/rec.cpp
#include <iostream>

#define RECIPROCAL (number) (1.0 / (number))

int main(  )
{
    float   counter;

    for (counter = 0.0; counter < 10.0; 
         counter += 1.0) {

        std::cout << "1/" << counter << " = " << 
                  RECIPROCAL(counter) << "\n"; 
    }
    return (0);
}

10.4.1 The # Operator

The # operator is used inside a parameterized macro to turn an argument into a string. For example:

#define STR(data) #data
STR(hello)

This code generates:

"hello"

For a more extensive example of how to use this operator, see Chapter 27.

10.4.2 Parameterized Macros Versus Inline Functions

In most cases, to avoid most of the traps caused by parameterized macros, it is better to use inline functions. But there are cases where a parameterized macro may be better than an inline function. For example, the SQR macro works for both float and int data types. We'd have to write two inline functions to perform the same functions, or we could use a template function. (See Chapter 24.)

#define SQR(x) ((x) * (x))  // A parameterized macro
// Works, but is dangerous

// Inline function to do the same thing
inline int sqr(const int x) {
    return (x * x);
}
    I l@ve RuBoard Previous Section Next Section