Variadic 宏不起作用

Variadic macros didn't work

我想做的是用宏访问代码。但是编译器给我这个错误

identifier "BUTTON___button" is undefined

#define BUTTON_1                HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8)
#define BUTTON_2                HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9)
#define BUTTON_3                HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
#define BUTTON_4                HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5)
#define BUTTON_5                HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)
#define BUTTON_6                HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_14)
#define BUTTON_7                HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_12)
#define BUTTON_8                HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_11)
#define BUTTON_9                HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15)
#define BUTTON_10               HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_0)
#define BUTTON_11               HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_10)
#define BUTTON_12               HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15)
#define BUTTON_13               HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)
#define BUTTON_14               HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_2)
#define BUTTON_15               HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11)
#define BUTTON_16               HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2)
#define BUTTON_17               HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_0)
#define BUTTON_18               HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_1)
#define BUTTON_19               HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5)
#define BUTTON_20               HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_4)

#define BUTTON(...)             BUTTON_##__VA_ARGS__


for(uint8_t _button = 1; _button < 21; _button++)
    BUTTON(__button) //=> This should give me BUTTON(1) , BUTTON(2) ... each iteration.But not working

通过使用可变宏,我能得到我想要的吗?

preprocessor works at compile-time, and is involved very early (read about translation phases). So you can't generate macros with a for loop. Read documentation of cpp and later your C standard (e.g. n1570).

您可以使用 不同的 程序生成您的 C 代码 - 也许是一些其他预处理器,例如 GPP or m4, or some script (or your own other program), and generating C files is a common practice since the previous century (for examples, look into yacc or rpcgen, but you'll find many others). You then want to configure your build automation tool (perhaps make or ninja) 以适当地调用这样的 C 代码生成器。

您可以获得预处理后的表格,例如如果使用 GCC,则使用 gcc -C -E。所以你可以理解编译器(它的翻译阶段 预处理之后)正在得到什么。

By using variadic macros, can I get what I want?

不,你不能。阅读 documentation of variadic macros.

您必须记住,预处理器的事情发生在代码编译之前,并且是严格的 text-only 替换。

因此,依赖于因 run-time 循环而具有不同值的变量是没有意义的,也不起作用。

正确的做法是将端口地址(GPIOA 等)连同每个端口对应的引脚一起放在一个数组中:

static const struct {
  const GPIO_TypeDef *port;
  uint32_t            pin;
} buttons[] = {
  { GPIOB, GPIO_PIN_8 },
  { GPIOB, GPIO_PIN_9 },
  ...
};

然后遍历数组中的数据:

for (size_t i = 0; i < sizeof buttons / sizeof *buttons; ++i)
{
  if (HAL_GPIO_ReadPin(buttons[i].port, buttons[i].pin))
  {
     ...
  }
}

Variadic 宏不会解决这里的任何问题。您有两个选择,look-up tables 或 X 宏。 Look-up table 更可取,因为它们最易读。但是如果您需要尽量减少代码重复,X 宏可能会很有用,尽管它们有点难读。

Look-up table 版本:

typedef struct // I'm not sure about the exact types used here
{
  volatile uint8_t* port;
  uint8_t pin;
} button_t;

const button_t BUTTON[20] =
{
  {&GPIOB, 8},
  {&GPIOB, 9},
  ...
};


for(uint8_t i = 0; i<20; i++)
{
  HAL_GPIO_ReadPin(BUTTON[i].port, BUTTON[i].pin);
}

X宏版本:

#define BUTTON_LIST         \
  /*button  port  pin */    \
  X(1,      B,     8)       \
  X(2,      B,     9)       \
  X(3,      B,     4)       \
  X(4,      B,     5)       \
  X(5,      C,    13)

...

for(uint8_t button = 0; button<20; button++)
{
  #define X(button, port, pin) HAL_GPIO_ReadPin(GPIO##port, GPIO_PIN_##pin);
    BUTTON_LIST
  #undef X
}