Search code examples
cmacrosc-preprocessor

C macro expansion order


I have a macro to repeat macros that I use to fill arrays with default values in compile time:

const int array [512] = 
{
     MACRO_REPEAT(512, -2) // this repeats -2, 512  times
     [4] = 10,
     [5] = 2,
     ...
}

The macro repeat will expand to MACRO_REPEAT_512, but now I wanted to use other macros as the array size, like:

#define ARRAY_LENGTH 512
const int array [ARRAY_LENGTH ] = 
{
    MACRO_REPEAT(ARRAY_LENGTH , -2) // this repeats -2, 512  times
    [4] = 10,
    [5] = 2,
     ...
 }

But this expands to MACRO_REPEAT_ARRAY_LENGTH, doesn't expand ARRAY_LENGTH value before concatenating it. Other example would be for multi-dimensional arrays, which involves more levels of expansion:

#define X 512
#define Y 512

const int array [X][Y] = 
{
    MACRO_REPEAT(X*Y , -2) // this repeats -2, 512  times
    [4] = 10,
    [5] = 2,
     ...
 }

This will expand to MARO_REPEAT_X*Y. So, is there a way to expand those values to the final numerical value before concatenating it to other macros?


Solution

  • You can solve the MACRO_REPEAT(ARRAY_LENGTH , -2) case by changing the definition of MACRO_REPEAT to use 2 stage expansion, ie do not use token pasting in MACRO_REPEAT itself, invoke another macro that does.

    Not that this will only work as expected if ARRAY_LENGTH is defined as a single number token and if there is a macro definition for this specific size.

    You cannot handle the more general MACRO_REPEAT(X*Y , -2) case with the standard C preprocessor.

    You can use the gcc extension to initialize simple arrays:

    #define MACRO_REPEAT(n, e)  [ 0 ... (n)-1 ] = e,
    

    But this method cannot be used to handle multidimensional arrays such as MACRO_REPEAT(X*Y , -2).

    You could try this:

    #define MACRO_REPEAT(n, e)  [ 0 ... (n)-1 ] = e,
    #define X 512
    #define Y 512
    
    const int array[X][Y] = { MACRO_REPEAT(X, { MACRO_REPEAT(Y, -2) }) };
    

    But the use of the C preprocessor just obfuscates the intent. If you decide to rely on gcc extensions, just use them directly.