使用##

时间:2017-02-19 15:27:40

标签: c macros header-files libraries

我正在为微控制器编写一些库,为此,我使用类似宏的函数。例如,启用I2C模块的类宏函数定义为:

#define I2C_MODULE_ENABLE(_x) \
    I2C##_x##CONLbits.I2CEN = 1

其中_x是模块编号(例如,在我的情况下为12)。

如果用户将此类宏函数称为I2C_MODULE_ENABLE(1),则它将由预处理器扩展为I2C1CONLbits. I2CEN = 1

但是,如果用户将此类宏函数称为I2C_MODULE_ENABLE(MY_I2C),则其中MY_I2C是在我的{{config.h文件中定义的宏常量。 1}}库(例如,宏常量定义为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 = 1I2C1令牌之间的空格。

1 个答案:

答案 0 :(得分:5)

我没有正确添加宏级别,正如我所看到的那样。通常的习惯用法是定义一个除了转发参数之外什么都不做的包装器。这样,如果参数本身就是一个宏,它将在传递给包装的宏之前进行扩展:

#define I2C_MODULE_ENABLE__(x_) \
    I2C##x_##CONLbits.I2CEN = 1

#define I2C_MODULE_ENABLE(x_) \
    I2C_MODULE_ENABLE__(x_)

我冒昧地重命名你的宏参数,因为带有前导下划线的标识符被定义为为实现保留,我认为安全比抱歉更好。

为了解决你的空间问题,我会选择间接的众所周知的水平,并使用像宏这样的函数来生成正确的前缀标记,并将其传递到两个级别,以确保它正确扩展:

#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

整个shtick是确保预处理器在每一步都看到并生成有效的令牌。这可能有点令人讨厌。