简体   繁体   English

C宏 - 避免宏扩展

[英]C Macros - avoiding macro expansion

I have the following 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); \
}

I would like the REGISTER field to not be expanded in the following line 我希望REGISTER字段不会在以下行中展开
VERBOSE(#REGISTER " :0x%02X\\n", var); \\

For example, When I write 例如,我写的时候
WRITE_REGISTER_VERBOSE(i2c_dev_fd, REG_PWR_CTRL, 0x1A);
WRITE_REGISTER_VERBOSE(i2c_dev_fd, REG_FIFO_CTL, 0xC6);
I get the output 我得到了输出
0x2D :0x1A
0x38 :0xC6
I would like to obtain 我想获得
REG_PWR_CTRL :0x1A
REG_FIFO_CTL :0xC6

I came across a lot of posts that spoke about adding an extra level of indirection. 我遇到了许多关于添加额外级别间接的帖子。
I tried the answer described here https://stackoverflow.com/a/2653351/1761555 ..although I believe that that answer is for a different problem altogether.. 我尝试了这里描述的答案https://stackoverflow.com/a/2653351/1761555 ..尽管我相信这个答案完全是针对不同的问题..

What I did was 我做的是

#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); \
}

But this still gives me the same output as before 但这仍然给我与以前相同的输出

Is there any way to achieve this? 有没有办法实现这个目标?

You could make REG_PWR_CTRL and REG_FIFO_CTL a value of some enum, like 您可以使REG_PWR_CTRLREG_FIFO_CTL成为某些枚举的值,例如

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

Then REG_PWR_CTRL becomes a real identifier of some enum value, and is not macro-expanded in something else (because an enum definition is not a macro definition, and is not handled by the cpp preprocessor ). 然后REG_PWR_CTRL成为某个枚举值的实际标识符,并且不会在其他内容中进行宏扩展(因为enum定义不是宏定义,并且不由cpp预处理器处理 )。

So define such an enum, and preprocess your source code (eg with gcc -C -E yoursource.c > yoursource.i ) then look (eg with less yoursource.i ) inside the preprocessed file. 因此,定义这样的枚举,并预处理您的源代码(例如,使用gcc -C -E yoursource.c > yoursource.i )然后在预处理文件中查看(例如,使用less yoursource.i )。 All occurrences of REG_PWR_CTRL will still be there. 所有出现的REG_PWR_CTRL仍然存在。

Be aware the the preprocessor is conceptually the first phase of a compiler: even in compilers like current GCC 4.8 where the preprocessor is not an external program but implemented thru a libcpp internal library, the compiler works by first preprocessing the source code and obtaining a stream of lexemes , and then occurrences of REG_PWR_CTRL stay as lexemes (not as literal constants 0x2d as when you #define REG_PWR_CTRL 0x2d ...). 请注意,预处理器在概念上是编译器的第一阶段:即使在当前GCC 4.8等编译器中,预处理器不是外部程序,而是通过libcpp内部库实现,编译器首先预处理源代码并获取流的词位 ,然后的出现REG_PWR_CTRL留为词位(而不是字面常量0x2d当你为#define REG_PWR_CTRL 0x2d ...)。

You need to read more about the preprocessor cpp , and take the habit to look into the preprocessed form. 您需要阅读有关预处理器cpp的更多信息,并养成查看预处理表单的习惯。

Another advantage of enum -s is that if you compile with debugging info (eg gcc -g ) the debugging info hence the debugger gdb knows about enum . enum -s的另一个优点是,如果使用调试信息(例如gcc -g )编译调试信息,则调试器gdb知道enum

I modified your code for simplicity: 为简单起见,我修改了代码:

#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;
}

Output: 输出:

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

Use macros for simple things. 使用宏来处理简单的事情。

This is because: 这是因为:

  • You have more readable code 你有更多可读的代码
  • Easier to debug as the debugger has symbols you help you 更容易调试,因为调试器具有帮助您的符号
  • They are very difficult to debug. 它们很难调试。

So just use functions for complex stuff. 所以只需将函数用于复杂的东西。 Use macros for simple stuff 使用宏来表示简单的东西

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM