C 预处理器通过连接和字符串化生成宏
C Preprocessor generate macros by concatenation and stringification
我有一组目标宏,我想根据选择的宏为其生成别名,如下所示:
选择宏:
#define I2C_MODULE 1
别名宏(概念形式):
#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>
目标宏(来自我无法控制的外部文件):
#define INT_I2C0 24
#define INT_I2C1 53
...
#define I2C0_BASE 0x40020000
#define I2C1_BASE 0x40021000
...
我想让预处理器根据
选择宏 I2C_MODULE
,但在大量阅读 Q1, P1 和许多我忘记的其他参考资料之后,我最终对它们的值进行了硬编码。下面我展示了我当前的工作定义,然后是我最后一次失败的生成宏的尝试:
什么有效:
#define I2C_MODULE 1
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1
什么不起作用:
#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
编辑:我扩展了 以到达我想要的位置,如下所示:
#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)
只需使用 #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
这似乎有效:
#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);
示例输出(来自 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;
它与我对 C preprocessor and token concatenation 的回答密切相关。
毫无疑问,I2C_MODULE_BASE
和I2C_MODULE_NVIC
宏可以有其他的写法,但关键是:
- 使用
##
标记粘贴运算符(不是 #
字符串化运算符)。
- 使用两级宏(例如,
I2C_MODULE_BASE
和 PASTE3
)。
我怀疑您正在编写一个 I2C 驱动程序,它通常可以在同一个微控制器中处理多个 I2C 硬件外围设备,而无需多次重写所有相同的代码。
在那种情况下,您真正要找的可能是这样的:
#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);
我有一组目标宏,我想根据选择的宏为其生成别名,如下所示:
选择宏:
#define I2C_MODULE 1
别名宏(概念形式):
#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>
目标宏(来自我无法控制的外部文件):
#define INT_I2C0 24
#define INT_I2C1 53
...
#define I2C0_BASE 0x40020000
#define I2C1_BASE 0x40021000
...
我想让预处理器根据
选择宏 I2C_MODULE
,但在大量阅读 Q1, P1 和许多我忘记的其他参考资料之后,我最终对它们的值进行了硬编码。下面我展示了我当前的工作定义,然后是我最后一次失败的生成宏的尝试:
什么有效:
#define I2C_MODULE 1
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1
什么不起作用:
#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
编辑:我扩展了
#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)
只需使用 #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
这似乎有效:
#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);
示例输出(来自 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;
它与我对 C preprocessor and token concatenation 的回答密切相关。
毫无疑问,I2C_MODULE_BASE
和I2C_MODULE_NVIC
宏可以有其他的写法,但关键是:
- 使用
##
标记粘贴运算符(不是#
字符串化运算符)。 - 使用两级宏(例如,
I2C_MODULE_BASE
和PASTE3
)。
我怀疑您正在编写一个 I2C 驱动程序,它通常可以在同一个微控制器中处理多个 I2C 硬件外围设备,而无需多次重写所有相同的代码。
在那种情况下,您真正要找的可能是这样的:
#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);