Dear C++ Community,

I have recently came across an interesting problem that might be related to a subject for improvement in the C++ Standard. It is related to macro replacement, so rather an "old C stuff". But not only C++ shares this features with C, but also C++ Standard shares the wording with C standard in this area, especially below paragraph.

16.3.1 Argument substitution

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

One can interpret this paragraph as if the standard required to:

  1. First identify macro arguments (comma separated) and then expand all the macros contained in each argument separately,

or

  1. Expand all the macros contained in the argument list and then identify each argument.

In fact, the first approach is represented by one of the most widely-used commercial static analyzers (for C), while the second one is apparently applied by GCC as well as some other commercial compiler vendors.

This sample code:


#define CONDITION (0)

#if (CONDITION > 0)
#define FunctionAlias(par_a, par_b, par_opt, par_c) \
          FunctionName(par_a, par_b, par_opt, par_c)
#else
#define FunctionAlias(par_a, par_b, par_c) \
          FunctionName(par_a, par_b, par_c)
#endif

int global_a, global_b, global_c;
#if (CONDITION > 0)
int global_opt;
#endif

void FunctionName(int a, int b, int c)
{
}
 
void AnotherFunction()
{
   FunctionAlias(
                  global_a,
                  global_b,
                  #if (CONDITION > 0)
                  global_opt,
                  #endif
                  global_c
                );
}

is transformed by GCC preprocessor to valid C code:

int global_a, global_b, global_c;

void FunctionName(int a, int b, int c)
{
}

void AnotherFunction()
{
   FunctionName(global_a, global_b, global_c)
                 ;
}

However, the preprocessor embedded in the analyzer produces:

/*    12 */ int global_a, global_b, global_c;
/*    17 */ void FunctionName(int a, int b, int c)
/*    18 */ {
/*    19 */ }
/*    21 */ void AnotherFunction()
/*    22 */ {
/*    30 */    FunctionName(global_a, global_b, #if ((0) > 0) global_opt);
/*    31 */ }

1. What is your opinion on which of the interpretations above is in line with the intentions of the authors of the quoted paragraph?

2. Do you share my impression that the paragraph is not precise enough for the reader to strongly decide which approach is the right one?

Thank you in advance for your answers.

Best regards,

Marcin