Search code examples
cc-preprocessorsubstitution

Suppress C Macro Variable Substitution


I have this bit of code (part of an interpreter for a garbage-collected Forth system, actually):

#define PRIMITIVE(name) \
    do \
    { \
        VocabEntry* entry = (VocabEntry*)gc_alloc(sizeof(VocabEntry)); \
        entry->code = name; \
        entry->name = cstr_to_pstr(#name); \
        entry->prev = latest_vocab_entry; \
        latest_vocab_entry = entry; \
    } \ 
    while (false)

PRIMITIVE(dup);
PRIMITIVE(drop);
PRIMITIVE(swap);
// and a lot more

but there's a problem: in the line

entry->name = cstr_to_pstr(#name);

the name field is substituted for dup, drop, swap, and the rest. I want the field name to not be substituted.

So, is there any way to solve this, other than simply renaming the macro argument?

For an answer, please explain if there is, in general, a way to suppress the substitution of a macro argument name in the macro body. Don't answer "just do it this way" (please).


Solution

  • Jim Balter provided this solution in a comment: Change name in the macro replacement list to na##me. The concatenation operator causes this to be replaced by name, but that occurs after scanning for names of the macro parameters, so it will not be replaced in that scan. Code for this is:

    #define PRIMITIVE(name) \
        do \
        { \
            VocabEntry* entry = (VocabEntry*)gc_alloc(sizeof(VocabEntry)); \
            entry->code = name; \
            entry->na##me = cstr_to_pstr(#name); \
            entry->prev = latest_vocab_entry; \
            latest_vocab_entry = entry; \
        } \
        while (false)
    

    My original answer was a bit more cumbersome:

    You can define a different macro to expand to name, like this:

    #define Name name
    

    and change the name field in the PRIMITIVE macro to use the new macro, like this:

    #define PRIMITIVE(name) \
        do \
        { \
            VocabEntry* entry = (VocabEntry*)gc_alloc(sizeof(VocabEntry)); \
            entry->code = name; \
            entry->Name = cstr_to_pstr(#name); \
            entry->prev = latest_vocab_entry; \
            latest_vocab_entry = entry; \
        } \
        while (false)
    

    Other than using something different from the parameter name in the macro body or changing the parameter name, there is no other way to do this in the C language. Per C 2011 (N1570) 6.10.3.1 1, when a function-like macro is recognized, parameter names are immediately substituted except when # or ## is present, and there no other exceptions:

    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.

    The # token changes the parameter name to a string, which is no use in this situation. The ## token expands the parameter name and pastes it together with an adjacent token, which is also no use in this situation.