Search code examples
cmacrosstringification

Macro stringifies a previous macros identifier instead of its replacement


Why is inner_LOAD_ATOM(buffer, ATOM_MAX) converted to scanf("%" "ATOM_MAX" "s", x) but the wrapped version isnt? I would expect ATOM_MAX(identifier) to be replaced by 10 before it is "passed" to inner_LOAD_ATOM or LOAD_LINE and making the wrapper useless. More elaborated answer why the wrapper is necessary would be very appreciated.

#include <stdio.h>

#define ATOM_MAX 10
#define inner_LOAD_ATOM(x, y) scanf("%" #y "s", x)      /* inner part */
#define LOAD_ATOM(x, y) inner_LOAD_ATOM(x, y)           /* wrapper of inner_LOAD_ATOM */


int main(void)
{
    char buffer[ATOM_MAX] = {0, };

    /* wrapped works fine */
    LOAD_ATOM(buffer, ATOM_MAX);        

    /* [Warning] unknown conversion
    type character 'A' in format [-Wformat=] */
    inner_LOAD_ATOM(buffer, ATOM_MAX);

    printf("%s\n", buffer);


    return 0;
}

Solution

  • It's important to understand that function-like macros don't work like C functions. There is (almost) no sense of a macro call stack or of forwarding arguments from one macro to another. Instead, macro expansion proceeds iteratively -- macro is replaced with its expansion, then the expansion is rescanned for further macros to expand.

    A function-like macro's arguments are completely macro expanded before being inserted into the macro's replacement text, except where they are the operands of the stringification (#) or token-pasting (##) operator. Therefore, if you invoke inner_LOAD_ATOM(buffer, ATOM_MAX) directly, ATOM_MAX is not expanded before the # operator is applied.

    On the other hand, when you invoke LOAD_ATOM(buffer, ATOM_MAX), the ATOM_MAX macro is expanded before LOAD_ATOM() is expanded. The inner_LOAD_ATOM() doesn't factor in at that point -- it's just part of the outer macro's replacement text until LOAD_ATOM()'s expansion is re-scanned for further macro expansions.