Cmacros – 避免macros观扩展

我有以下macros

#define REG_PWR_CTRL 0x2D #define REG_FIFO_CTL 0x38 #define VERBOSE(...) \ if(verbose) \ printf(__VA_ARGS__); #define READ_REGISTER(i2c_dev_file, REGISTER, variable) \ { \ variable = i2c_smbus_read_byte_data(i2c_dev_file, REGISTER); \ } #define WRITE_REGISTER_VERBOSE(i2c_dev_file, REGISTER, value) \ { \ short int var = 0; \ i2c_smbus_write_byte_data(i2c_dev_file, REGISTER, value); \ usleep(100); \ READ_REGISTER(i2c_dev_file, REGISTER, var); \ VERBOSE(#REGISTER " :0x%02X\n", var); \ } 

我希望REGISTER字段不能在下面的行中展开
VERBOSE(#REGISTER " :0x%02X\n", var); \

例如,当我写
WRITE_REGISTER_VERBOSE(i2c_dev_fd, REG_PWR_CTRL, 0x1A);
WRITE_REGISTER_VERBOSE(i2c_dev_fd, REG_FIFO_CTL, 0xC6);
我得到输出
0x2D :0x1A
0x38 :0xC6
我想获得
REG_PWR_CTRL :0x1A
REG_FIFO_CTL :0xC6

我遇到了很多关于增加额外间接级别的post。
我尝试了这里描述的答案https://stackoverflow.com/a/2653351/1761555虽然我相信这个答案是完全不同的问题..

我做的是

 #define STRINGIFY(label) (#label) #define WRITE_REGISTER_VERBOSE(i2c_dev_file, REGISTER, value) \ { \ short int var = 0; \ i2c_smbus_write_byte_data(i2c_dev_file, REGISTER, value); \ usleep(100); \ READ_REGISTER(i2c_dev_file, REGISTER, var); \ VERBOSE("%s :0x%02X\n", STRINGIFY(REGISTER), var); \ } 

但是这仍然给我和以前一样的输出

有没有办法做到这一点?

您可以使REG_PWR_CTRLREG_FIFO_CTL值为某些枚举,如

  enum registers_en { REG__NONE, REG_PWR_CTRL = 0x2d, REG_FIFO_CTL = 0x38, }; 

然后, REG_PWR_CTRL成为一些枚举值的真实标识符,而不是在其他东西中进行宏扩展(因为enum定义不是宏定义,并且不由cpp预处理器处理 )。

因此,定义这样一个枚举,并预处理您的源代码(例如使用gcc -C -E yoursource.c > yoursource.i ),然后在预处理文件中查看(例如使用less yoursource.i )。 所有出现的REG_PWR_CTRL将仍然在那里。

请注意,预处理器在概念上是编译器的第一个阶段:即使在像GCC 4.8这样的编译器中,预处理器不是外部程序,而是通过libcpp内部库实现,编译器通过首先预处理源代码并获得流的词位 ,然后出现的REG_PWR_CTRL保持为词位(不像当你#define REG_PWR_CTRL 0x2d …那样文字常量为#define REG_PWR_CTRL 0x2d )。

您需要阅读更多关于预处理器cpp的信息 ,并习惯于查看预处理过的表单。

enum -s的另一个优点是,如果你用调试信息(例如gcc -g )编译调试信息,那么调试器gdb知道enum

我为了简单而修改了你的代码:

 #include <stdio.h> #define REG_PWR_CTRL (0x2D) #define GET_VAR_NAME(var) (#var) #define VERBOSE(...) (printf(__VA_ARGS__)) #define ANOTHER_LAYER(arg) ( \ VERBOSE("%s = %#X; %s = %#X\n", \ GET_VAR_NAME(REG_PWR_CTRL), REG_PWR_CTRL, \ GET_VAR_NAME(arg), arg) \ ) \ int main(void) { int num = 5; VERBOSE("%s = %#X\n", GET_VAR_NAME(REG_PWR_CTRL), REG_PWR_CTRL); ANOTHER_LAYER(num); return 0; } 

输出:

 REG_PWR_CTRL = 0X2D REG_PWR_CTRL = 0X2D; num = 0X5 

使用宏来做简单的事情。

这是因为:

  • 你有更可读的代码
  • 调试器有符号帮助你,因此更容易调试
  • 他们很难调试。

所以只需使用复杂的东西的功能。 使用宏来做简单的事情