C Preprocessor generate macros by concatenation an

2020-02-13 06:18发布

问题:

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)

回答1:

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).


回答2:

I suspect that you are writing a I2C driver which can generically handle multiple I2C hardware peripherals in the same micro-controller without rewriting all the same code multiple times.

In that case, what you are really looking for probably is something like this:

#define I2C1 ((volatile uint8_t*)0x12345678)  // address of first hw register for I2C1
#define I2C2 ((volatile uint8_t*)0x55555555)  // address of first hw register for I2C2

/* map all registers used for I2C, they will have same register layout for every 
   peripheral no matter which one:  */
#define I2C_CONTROL(base) (*(base + 0))
#define I2C_DATA(base)    (*(base + 1))
...


// create some dummy typedef to make your functions look nice:
typedef volatile uint8_t* I2C_t; 


// define whatever functions you need in the driver:
void i2c_init (IC2_t bus);
void i2c_send (I2C_t bus, const uint8_t* data, size_t n);
...

// implement functions in a bus-independent way:
void i2c_init (IC2_t bus)
{
  I2C_CONTROL(bus) = THIS | THAT; // setup registers
}


// caller code:

i2c_init(I2C1);
i2c_init(I2C2);
...
i2c_send(I2C1, "hello", 5);
i2c_send(I2C2, "world", 5);


回答3:

Just use #if / #else / #endif

#if (I2C_MODULE == 0)
#define I2C_MODULE_BASE I2C0_BASE
#define I2C_MODULE_NVIC INT_I2C0
#elif (I2C_MODULE == 1)
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1
#else
#error Unknown configuration
#endif