【问题标题】:C Macros - avoiding macro expansionC 宏 - 避免宏扩展
【发布时间】:2023-03-07 12:04:01
【问题描述】:

我有以下宏

#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

我遇到了很多关于添加额外间接级别的帖子。
我尝试了这里描述的答案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); \
}

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

有什么办法可以做到吗?

【问题讨论】:

  • 查看C #define macro for debug printing 讨论为什么if (verbose) 测试不如书面以及如何处理。如果您使用 VERBOSE 的唯一上下文是宏 WRITE_REGISTER_VERBOSE,那么您会没事的;如果它在其他地方使用,则需要小心,因为它可能会导致程序被重新解释(尽管它很可能只会产生语法错误,但原因可能并不明显!)。

标签: c linux macros


【解决方案1】:

您可以将 REG_PWR_CTRLREG_FIFO_CTL 设为某个枚举的值,例如

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

然后REG_PWR_CTRL 成为某个枚举值的真实标识符,并且不会在其他内容中进行宏扩展(因为enum 定义不是宏定义,并且不由cpp preprocessor 处理)。

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

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

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

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

【讨论】:

  • 我很抱歉,但我不太明白这将如何帮助我进行反向映射..所以在这种情况下,我仍然能够获得 0x2D 的值REG_PWR_CTRL 但我不知道这将如何帮助我打印 'REG_PWR_CTRL'.. 另外,我没有提到它,但我定义了很多寄存器..不仅仅是一个。
  • 在我编辑时,只需尝试使用我建议的 enum 并查看预处理的表单。
  • 谢谢!您的解决方案就像一个魅力!在我接受你之前,我只是在等待看看这是否可以使用#define 来实现
  • 是否可以使用 functions 而不是 macros 来实现此功能?如果可以,能否提供一个模板?
【解决方案2】:

为简单起见,我修改了您的代码:

#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

【讨论】:

  • 这太奇怪了..您的代码作为独立的..但是当我在我的程序中执行相同操作时,它不起作用..我能想到的唯一(愚蠢)原因:发生这种情况是因为 1)宏是在不同的头文件中定义的 2)我的 makefile 在将它们组装在一起之前正在编译小块 3)其他头文件包含导致这种情况 除此之外,我认为您的代码和我的代码没有区别。 . 编辑我的原始问题以展示我对您的方法的实施
  • @user1761555:您需要像GET_VAR_NAME 这样的中间宏,而您的问题没有。
  • 我现在已经编辑了我的帖子以显示中间宏的使用。正如我最初发布的那样,我确实尝试了我引用的链接中的解决方案。但它没有成功
  • @user1761555 你的宏在哪里并不重要#defined,只要它们在使用之前是#defined。如果宏在使用前不是 #defined,则会出现错误。
  • @user1761555 我扩展了我的答案,包括嵌套一个宏。我没有得到你遇到的问题......
【解决方案3】:

将宏用于简单的事情。

这是因为:

  • 你有更多可读的代码
  • 调试器具有可帮助您的符号,因此更易于调试
  • 它们很难调试。

所以只需将函数用于复杂的东西。对简单的东西使用宏

【讨论】:

  • 更多的是评论(或意见)而不是答案!您没有为原始问题提供任何解决方案!
  • @BasileStarynkevitch - 我认为首先使用长宏很愚蠢,因此我的回答是采用不同的方法
  • 对于是否是评论但 +1 持怀疑态度,因为宏的复杂性应该非常有限。
猜你喜欢
  • 2010-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-27
  • 2014-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多