Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 268
rnunes Avatar asked Jul 15 '16 12:07

rnunes


2 Answers

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.

like image 80
chqrlie Avatar answered Nov 02 '22 15:11

chqrlie


I'm not sure if this counts as the "right" answer as it doesn't answer the OP's question directly, but it is a suggested workaround for the problem. It also is not standard C as it makes use of a GCC extension.

In the GNU C Compiler (gcc), a range of array elements can be initialized to the same value using the form [FIRST ... LAST] = VALUE. It also appears to allow more than one designated initializer for an element, so it is possible to initialize a range of elements to the same value and then initialize elements contained within that range to different values, something like this:

#define ARRAY_LENGTH 512
const int array[ARRAY_LENGTH] =
{
    [0 ... ARRAY_LENGTH - 1] = -2,
    [4] = 10,
    [5] = 2,
    /* ... */
};
like image 25
Ian Abbott Avatar answered Nov 02 '22 16:11

Ian Abbott