C++ Logo

std-proposals

Advanced search

Re: C++ Standard and macro argument substitution

From: Robert J. Simpson <robert.simpson.lists_at_[hidden]>
Date: Tue, 20 Oct 2020 20:13:22 +0100
On 20/10/2020 19:27, mircan via Std-Proposals wrote:

> 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

Have you seen the reference below?

https://www.spinellis.gr/blog/20060626/cpp.algo.pdf

It's a long time (10+ years) since I looked at this but I found the C++
spec to be lacking when I tried to implement a pre-processor. The above
reference was written by Dave Prosser who, I believe, was one of the
original contributors. My understanding is that it was intended to
remove some undefined behaviour in the original spec. It's quite
possible the spec has since been updated since though.

Rob.





Received on 2020-10-20 14:13:28