使用## 连接宏常量
Macro constants concatenation using ##
我正在为微控制器编写一些库,为此,我使用了类似宏的函数。例如,启用 I2C 模块的类宏函数定义为:
#define I2C_MODULE_ENABLE(_x) \
I2C##_x##CONLbits.I2CEN = 1
其中 _x
是模块编号(例如,1
或 2
在我的例子中)。
如果用户将这个类似宏的函数调用为 I2C_MODULE_ENABLE(1)
,它会被预处理器扩展为 I2C1CONLbits. I2CEN = 1
。
但是,如果用户将这个类似宏的函数调用为 I2C_MODULE_ENABLE(MY_I2C)
,其中 MY_I2C
是在我的包含的用户定义的 config.h
文件中定义的宏常量i2c.h
库(例如,宏常量定义为 #define MY_I2C 1
),类宏函数将扩展为 I2CMY_I2CCONLbits. I2CEN = 1
.
我知道我需要在串联之前以某种方式评估 MY_I2C
宏常量,我可以通过添加另一个宏级别来做到这一点:
#define __I2CxCONLbits(_x) I2C##_x##CONLbits
#define I2C_MODULE_ENABLE(_x) \
__I2CxCONLbits.I2CEN = 1
我的问题是:这个问题有没有更优雅的解决方案,因为我有多个寄存器,例如 CONLbits
寄存器。使用这种方法,我需要为每个寄存器定义一个特殊的 __I2CxREGISTER(_x)
宏。
我试过这样做:
#define __I2Cx(_x) I2C##_x
#define I2C_MODULE_ENABLE(_x) \
__I2Cx(_x)##CONLbits.I2CEN = 1
但这会产生如下输出:I2C1 CONLbits .I2CEN = 1
,我的编译器抱怨 I2C1
和 CONLbits
标记之间的空白。
正如我所见,您没有正确添加宏关卡。通常的习惯用法是定义一个包装器,它只转发参数。这样,如果参数本身是一个宏,它将在传递给被包装的宏之前展开:
#define I2C_MODULE_ENABLE__(x_) \
I2C##x_##CONLbits.I2CEN = 1
#define I2C_MODULE_ENABLE(x_) \
I2C_MODULE_ENABLE__(x_)
我冒昧地重命名了您的宏参数,因为带有前导下划线的标识符被定义为保留用于实现,我认为安全总比后悔好。
为了解决你的 space 问题,我会使用众所周知的间接级别,并使用像宏这样的函数来生成正确的前缀令牌,并将它传递到两个级别以确保它是正确展开:
#define I2Cx__(x_) I2C##x_
#define I2C_MODULE_ENABLE__(IC_) \
IC_##CONLbits.I2CEN = 1
#define I2C_MODULE_ENABLE_(IC_) \
I2C_MODULE_ENABLE__(IC_)
#define I2C_MODULE_ENABLE(x_) \
I2C_MODULE_ENABLE_(I2Cx__(x_))
现场观看here
整个小技巧是确保预处理器在每一步都能看到并生成有效的标记。这可能有点烦人。
我正在为微控制器编写一些库,为此,我使用了类似宏的函数。例如,启用 I2C 模块的类宏函数定义为:
#define I2C_MODULE_ENABLE(_x) \
I2C##_x##CONLbits.I2CEN = 1
其中 _x
是模块编号(例如,1
或 2
在我的例子中)。
如果用户将这个类似宏的函数调用为 I2C_MODULE_ENABLE(1)
,它会被预处理器扩展为 I2C1CONLbits. I2CEN = 1
。
但是,如果用户将这个类似宏的函数调用为 I2C_MODULE_ENABLE(MY_I2C)
,其中 MY_I2C
是在我的包含的用户定义的 config.h
文件中定义的宏常量i2c.h
库(例如,宏常量定义为 #define MY_I2C 1
),类宏函数将扩展为 I2CMY_I2CCONLbits. I2CEN = 1
.
我知道我需要在串联之前以某种方式评估 MY_I2C
宏常量,我可以通过添加另一个宏级别来做到这一点:
#define __I2CxCONLbits(_x) I2C##_x##CONLbits
#define I2C_MODULE_ENABLE(_x) \
__I2CxCONLbits.I2CEN = 1
我的问题是:这个问题有没有更优雅的解决方案,因为我有多个寄存器,例如 CONLbits
寄存器。使用这种方法,我需要为每个寄存器定义一个特殊的 __I2CxREGISTER(_x)
宏。
我试过这样做:
#define __I2Cx(_x) I2C##_x
#define I2C_MODULE_ENABLE(_x) \
__I2Cx(_x)##CONLbits.I2CEN = 1
但这会产生如下输出:I2C1 CONLbits .I2CEN = 1
,我的编译器抱怨 I2C1
和 CONLbits
标记之间的空白。
正如我所见,您没有正确添加宏关卡。通常的习惯用法是定义一个包装器,它只转发参数。这样,如果参数本身是一个宏,它将在传递给被包装的宏之前展开:
#define I2C_MODULE_ENABLE__(x_) \
I2C##x_##CONLbits.I2CEN = 1
#define I2C_MODULE_ENABLE(x_) \
I2C_MODULE_ENABLE__(x_)
我冒昧地重命名了您的宏参数,因为带有前导下划线的标识符被定义为保留用于实现,我认为安全总比后悔好。
为了解决你的 space 问题,我会使用众所周知的间接级别,并使用像宏这样的函数来生成正确的前缀令牌,并将它传递到两个级别以确保它是正确展开:
#define I2Cx__(x_) I2C##x_
#define I2C_MODULE_ENABLE__(IC_) \
IC_##CONLbits.I2CEN = 1
#define I2C_MODULE_ENABLE_(IC_) \
I2C_MODULE_ENABLE__(IC_)
#define I2C_MODULE_ENABLE(x_) \
I2C_MODULE_ENABLE_(I2Cx__(x_))
现场观看here
整个小技巧是确保预处理器在每一步都能看到并生成有效的标记。这可能有点烦人。