Search code examples
cconcatenationc-preprocessorstringification

C Preprocessor generate macros by concatenation and stringification


I have a set of target macros for which I want to generate aliases based on a choosing macro, like so:

Choosing macro:

#define I2C_MODULE 1

Alias macros (conceptual form):

#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>

Target macros (from an external file out of my control):

#define INT_I2C0   24 
#define INT_I2C1   53
...
#define I2C0_BASE  0x40020000
#define I2C1_BASE  0x40021000
...   

I wanted to have the preprocessor generate the alias macros I2C_MODULE_BASE and I2C_MODULE_NVIC based on the choosing macro I2C_MODULE, but after much reading Q1, P1 and many other references I lost track of, I ended up hard-coding their values. Below I show my current working definitions, and then my last failed attempts at generating the macros:

What works:

#define I2C_MODULE 1
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1

what did not work:

#define I2C_MODULE 1
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)

/* Attempt 1 */
#define I2C_MODULE_BASE "I2C" STR(I2C_MODULE) "_BASE"
#define I2C_MODULE_NVIC "INT_I2C" STR(I2C_MODULE)

/* Attempt 2 */
#define _I2C_MODULE_BASE "I2C" STR(I2C_MODULE) "_BASE"
#define _I2C_MODULE_NVIC "INT_I2C" STR(I2C_MODULE)
#define I2C_MODULE_BASE _I2C_MODULE_BASE
#define I2C_MODULE_NVIC _I2C_MODULE_NVIC

EDIT: I expanded upon the accepted answer to get to where I wanted, as follows:

#define PASTE2(a, b) a ## b
#define PASTE3(a, b, c) a ## b ## c

#define _I2C_MODULE_BASE(x) PASTE3(I2C, x, _BASE)
#define _I2C_MODULE_NVIC(x) PASTE2(INT_I2C, x)

#define I2C_MODULE_BASE _I2C_MODULE_BASE(I2C_MODULE)
#define I2C_MODULE_NVIC _I2C_MODULE_NVIC(I2C_MODULE)

Solution

  • This seems to work:

    #define I2C_MODULE 1
    
    //Alias macros (conceptual form):
    //#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
    //#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>
    
    //Target macros (from an external file out of my control):
    
    #define INT_I2C0   24 
    #define INT_I2C1   53
    
    #define I2C0_BASE  0x40020000
    #define I2C1_BASE  0x40021000
    
    #define PASTE2(a, b) a ## b
    #define PASTE3(a, b, c) a ## b ## c
    
    #define I2C_MODULE_BASE(x) PASTE3(I2C, x, _BASE)
    #define I2C_MODULE_NVIC(x) PASTE2(INT_I2C, x)
    
    extern int i2c_module_base = I2C_MODULE_BASE(I2C_MODULE);
    extern int i2c_module_nvic = I2C_MODULE_NVIC(I2C_MODULE);
    
    extern int i2c_module_base_0 = I2C_MODULE_BASE(0);
    extern int i2c_module_nvic_0 = I2C_MODULE_NVIC(0);
    
    extern int i2c_module_base_1 = I2C_MODULE_BASE(1);
    extern int i2c_module_nvic_1 = I2C_MODULE_NVIC(1);
    

    Sample output (from cpp):

    # 1 "xx.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "xx.c"
    # 21 "xx.c"
    extern int i2c_module_base = 0x40021000;
    extern int i2c_module_nvic = 53;
    
    extern int i2c_module_base_0 = 0x40020000;
    extern int i2c_module_nvic_0 = 24;
    
    extern int i2c_module_base_1 = 0x40021000;
    extern int i2c_module_nvic_1 = 53;
    

    It is closely based on my answer to C preprocessor and token concatenation.

    There are undoubtedly other ways that the I2C_MODULE_BASE and I2C_MODULE_NVIC macros could be written, but the key points are:

    1. Using the ## token pasting operator (not the # stringifying operator).
    2. Using two levels of macro (for example, I2C_MODULE_BASE and PASTE3).