停止 bool 的宏扩展

Stop macro expansion of bool

通过宏实现函数是 bool 的噩梦。我已经为(通用)函数的头部和 body 定义了宏。使用 bool 类型时,结果很奇怪。

我知道这是为了可维护性通常要避免的东西 ~ 但在我的例子中,它是一个 collection 非常相似的回调函数和一个非常苛刻的缩写。

所以目标实际上是可维护性和可读性,尤其是代码连贯性。

#include <stdio.h>
#include <stdbool.h>

#define FUN_H(TYPE) \
  void fun_##TYPE( void )

#define FUN(TYPE) \
  FUN_H(TYPE) { \
    printf("Type is " #TYPE ".\n"); \
  }

FUN_H(int);
FUN_H(bool);

FUN(int);
FUN(bool);

int main(void) {
  fun_int();
  fun_bool();
}

阅读代码,我希望输出 "Type is int." 和 "Type is bool."

实际上,要使代码编译(当前 gcc/clang)最后一次调用必须更改为 fun__Bool(); 并将 header 更改为 FUN_H(_Bool); —— 这实际上是有道理的如果您知道您的 C99,但会生成不连贯的代码。


可能的解决方案

使用_Bool直接输入

FUN_H(_Bool);
FUN(_Bool); // or FUN(bool);

int main(void) {
  fun__Bool();
}

PRO

CON


手动展开FUN()

#define FUN(TYPE) \
  void fun_##TYPE( void ) { \
    printf("Type is " #TYPE ".\n"); \
  }

PRO

CON


typedef 对于 _Bool

typedef _Bool mybool;

FUN_H(mybool);
FUN(mybool);

int main(void) {
  fun_mybool();
}

PRO

CON


bool改成typedef

#undef bool
typedef _Bool bool;

FUN_H(bool);
FUN(bool);

int main(void) {
  fun_bool();
}

PRO

CON


那么我应该做什么?

如果 bool 是一个宏,则它是将 bool 扩展为 _Bool 的内部 FUN_H 宏调用。如果丢掉里面的FUN_H宏,直接写FUN这样:

#include <stdio.h>
#include <stdbool.h>

#define FUN_H(TYPE) \
  void fun_##TYPE( void )

#define FUN(TYPE) \
  void fun_##TYPE( void ) { \
    printf("Type is " #TYPE ".\n"); \
  }

FUN_H(int);
FUN_H(bool);

FUN(int);
FUN(bool);

int main(void) {
  fun_int();
  fun_bool();
}

那么你会得到预期的 fun_bool,即使 bool 是一个宏。

如前所述,将 TYPE 传递给任何中间宏将在进一步处理之前强制其展开。所以连接必须尽早发生,在任何地方传递 TYPE 之前。为了在不重复太多的情况下实现它,我们可以委托给第三个宏

#define FUN_H(TYPE) \
  FUN_H_(fun_##TYPE)

#define FUN_H_(NAME) \
  void NAME( void )

#define FUN(TYPE) \
  FUN_H_(fun_##TYPE) { \
    printf("Type is " #TYPE ".\n"); \
  }

无论如何,您必须处理的动态是预处理器通过一系列(重新)扫描/完全展开操作进行操作。出现在宏扩展 M 中的宏名称本身仅在重新扫描 M 的完整扩展的上下文中扩展,包括所有扩展它的参数和/或它的标记粘贴和字符串化的应用,视情况而定。

因此,如果宏参数是类对象宏的名称,那么它不能作为参数本身传递给另一个宏——如果您尝试这样做,那么它将是该宏的扩展获得通过。这里的问题更典型的是如何确保在面对字符串化或标记粘贴时发生这种情况,所以你的问题有点不寻常,想要做相反的事情。

您已经提出了很多您似乎不满意的可行替代方案,但这里有一个具有您迄今为止讨论的所有优点和 none 个缺点:定义一个额外的修补不需要的扩展的宏。例如,

#include <stdio.h>
#include <stdbool.h>

// This macro corrects the unwanted expansion to the wanted one:
#define fun__Bool fun_bool

// The rest of the code is identical to your initial, non-working example

#define FUN_H(TYPE) \
  void fun_##TYPE( void )

#define FUN(TYPE) \
  FUN_H(TYPE) { \
    printf("Type is " #TYPE ".\n"); \
  }

FUN_H(int);
FUN_H(bool);

FUN(int);
FUN(bool);

int main(void) {
  fun_int();
  fun_bool();
}

或者,您可以改为引入一个宏,以相反的方式交换函数标识符,这样被更改的是 调用,而不是声明。

我认为还有其他替代方法可以让您使用示例的原始样式,但到目前为止我考虑过的所有这些方法都更加混乱和脆弱。