Search code examples
c++language-lawyerpreprocessor

When *exactly* is a function-like macro invocation actually treated as such?


I'm trying to understand the nitty-gritty details of how the C++ preprocessor works.

Assume we have:

#define LP (
#define F(X) a

The following code then preprocesses to a:

#define G1(Y) F Y
G1(LP) 1 )

However, the following code preprocesses to F ( 1 ):

#define G2(Y) F LP
G2(LP) 1 )

Okay, so I guess this makes sense if the LP argument is already expanded by the time it's fed into the macro.

Except, that's not the case! The following code preprocesses to F "LP" 1 ), not F "(" 1 ):

#define G3(Y) F #Y
G3(LP) 1 )

Okay, now I'm confused. What are the rules behind when an open parenthesis is treated as the beginning of a function-like macro invocation?


Solution

  • The C++ preprocessor considers the file to be made of group-parts. This rule is defined as follows:

    group-part:
        control-line
        if-section
        text-lines
        #conditionally-supported-directive

    control-line:
        [...]
        # define identifier lparen identifier-listopt ) replacement-list new-line
        [...]

    lparen:
        a ( character not immediately preceded by whitespace

    - [cpp.pre]

    This is why #define Z () would not not define a function-like macro, but #define Z() would for example.

    The preprocessor eliminates separating whitespace when expanding macros, so F Y where Y expands to LP ) is treated as F(). Expanding function-like macros is described in [cpp.subst] p1.

    The reason why F #Y behaves differently is that [cpp.subst] exempts #Y from macro substitution, and instead, the # operator is applied, meaning that

    [...] the original spelling of each preprocessing token in the stringizing argument is retained in the character string literal [...]

    - [cpp.stringize] p2

    In your example, #Y where Y expands to "LP", because LP is the original spelling of the argument.