C宏-避免宏扩展

C宏-避免宏扩展,c,linux,macros,C,Linux,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

我有以下宏

#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); \
}
我希望
寄存器
字段不要在下一行展开
VERBOSE(#寄存器: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\u PWR\u CTRL:0x1A

REG\u FIFO\u CTL:0xC6

我遇到了很多关于增加间接层次的帖子。
我尝试了这里描述的答案尽管我相信这个答案是针对一个完全不同的问题的

我所做的是

#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\u PWR\u CTRL
REG\u FIFO\u CTL
设置为某个枚举的值,如

  enum registers_en {
     REG__NONE,
     REG_PWR_CTRL = 0x2d,
     REG_FIFO_CTL = 0x38,
  };
然后,
REG\u PWR\u CTRL
成为某个枚举值的真实标识符,并且不会在其他对象中进行宏扩展(因为
enum
定义不是宏定义,也不会由宏处理)

因此,定义这样一个枚举,并预处理您的源代码(例如使用
gcc-C-e yoursource.C>yoursource.i
),然后查看(例如使用
less yoursource.i
)预处理文件内部。所有出现的
REG\u PWR\u CTRL
仍将存在

请注意,从概念上讲,预处理器是编译器的第一阶段:即使在像current这样的编译器中,预处理器不是外部程序,而是通过内部库实现的,编译器的工作方式也是首先预处理源代码并获得,然后,
REG\u PWR\u CTRL
的出现保持为词素(而不是像您定义REG\u PWR\u CTRL 0x2d
时那样保持为文字常量)

您需要阅读更多关于预处理器的内容,并养成查看预处理表单的习惯


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\u PWR\u CTRL
获得
0x2D
的值,但我不知道这将如何帮助我打印“
REG\u PWR\u CTRL
”。。另外,我没有提到它,但我定义了很多寄存器。不仅仅是一个。在我编辑时,请尝试使用我建议的
enum
,然后查看预处理表单。谢谢!你的解决方案很有魅力!在我接受你之前,我只是想看看是否可以使用
#define
来实现这个功能。是否可以使用函数而不是宏来实现这个功能?如果是这样的话,你能提供一个模板吗?这太奇怪了。你的代码可以作为一个独立的。但是当我在我的程序中做同样的事情时,它就不起作用了。唯一的(愚蠢的)我能想到的原因:之所以会发生这种情况,是因为1)宏是在不同的头文件中定义的2)我的makefile是在将它们组装在一起之前编译小片段3)其他头文件中包含的内容是造成这种情况的原因,除此之外,我看不出您的代码和我的代码之间有什么区别。。编辑我的原始问题以显示我对approach@user1761555:您需要一个中间宏,如
GET\u VAR\u NAME
,但您的问题没有。我现在编辑了我的帖子,以显示中间宏的用法。正如我最初发布的一样,我确实尝试了我引用的链接中的解决方案。它不成功though@user1761555宏在哪里并不重要,只要它们在使用前是定义的。如果宏在使用前不是
#define
d,您会得到一个错误。@user1761555我扩展了我的答案以包括嵌套宏。我不明白你遇到的问题……与其说是答案,不如说是评论(或意见)!您没有为原始问题提供任何解决方案@BasileStrynkevitch-我认为首先使用长宏是愚蠢的,因此我的答案是在是否为注释而不是+1的问题上采取不同的方法,因为宏的复杂性应该非常有限。请看下面的讨论,了解为什么
if(verbose)
测试写得不好,该怎么办。如果使用
VERBOSE
的唯一上下文是宏WRITE\u REGISTER\u VERBOSE,那么就可以了;如果它在其他地方被使用,您需要小心,因为它可能会导致程序被重新解释(尽管它很可能只会生成语法错误,但原因可能并不明显!)。
REG_PWR_CTRL = 0X2D
REG_PWR_CTRL = 0X2D; num = 0X5