C++ Logo

std-proposals

Advanced search

C++ Standard and macro argument substitution

From: mircan <mircan_at_[hidden]>
Date: Tue, 20 Oct 2020 20:27:32 +0200
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

 2. 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

Received on 2020-10-20 13:27:40