Macros with Parameters (C++)

From RAD Studio
(Redirected from Macros with Parameters)
Jump to: navigation, search

Go Up to The Preprocessor Index

The following syntax is used to define a macro with parameters:

#define macro_identifier(<arg_list>) <token_sequence>

No white spaces can separate the macro_identifier and the ( opening parenthesis. The optional arg_list is a sequence of identifiers separated by commas, like the argument list of a C function. However, any comma within matching inner parentheses in an argument list is treated as part of the argument, not as an argument delimiter. Each comma-delimited identifier plays the role of a formal argument or placeholder.

Such macros are called by writing

macro_identifier<whitespace>(<actual_arg_list>)

in the subsequent source code. The syntax is identical to that of a function call. However, there are some important semantic differences, side effects, and potential pitfalls.

The optional actual_arg_list must contain the same number of comma-delimited token sequences, known as actual arguments, as found in the formal arg_list of the #define line. There must be an actual argument for each formal argument. An error will be reported if the number of arguments in the two lists are different.

A macro call results in two sets of replacements. First, the macro_identifier and the parentheses enclosed arguments are replaced by the token_sequence. Next, any formal arguments occurring in the token_sequence are replaced by the corresponding real arguments appearing in the actual_arg_list.

As with simple macro definitions, rescanning occurs to detect any embedded macro identifiers eligible for expansion.

Nesting Parentheses And Commas

The actual_arg_list can contain nested parentheses provided that they are balanced. Also, commas appearing within parentheses are not treated like argument delimiters.

#define ERRMSG(x, str) printf("Error: %d \n%s", x, str)
#define SUM(x,y)  ((x) + (y))
:
ERRMSG(2, "Press Enter, then ESC");
/*expands to:  printf("Error: %d \n%s", 2, "Press Enter, then ESC"); */
return SUM(f(i, j), g(k, l));
/*expands to:  return ( (f(i, j)) + (g(k, l)) ); */

Side Effects and Other Dangers

The similarities between function and macro calls often obscure their differences. A macro call has no built-in type checking, so a mismatch between formal and actual argument data types can produce bizarre, hard-to-debug results with no immediate warning. Macro calls can also give rise to unwanted side effects, especially when an actual argument is evaluated more than once.

Compare CUBE and cube in the following example:

int cube(int x) {
   return x*x*x;
  }
#define CUBE(x)  ( (x) * (x) * (x) )
...
int b = 0, a = 3;
b = cube(a++);
/* cube() is passed actual argument a = 3; so b = 27; now a = 4 */
a = 3;
b = CUBE(a++);
/* expands as ( (a++) * (a++) * (a++) ); again b = 27 but now a = 6 */

Converting Actual Arguments to Strings with # Operator

You can place the # symbol in front of a formal argument of a macro to convert the actual argument to a string after replacement.

Given the following definition:

#define TRACE(flag) printf(#flag "=%d\n", flag)

the code fragment

int highval = 1024;
TRACE(highval);

becomes

int highval = 1024;
printf("highval" "=%d\n", highval);

which, in turn, is treated as

int highval = 1024;
printf("highval=%d\n", highval);


See Also