停止布尔的宏扩展

时间:2019-08-12 12:18:19

标签: c macros c-preprocessor

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

我知道,这通常是出于可维护性而要避免的东西〜但就我而言,它是一组非常相似的回调函数和一个非常苛刻的缩写。

因此,目标实际上是可维护性和可读性,尤其是代码一致性。

#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”。和“类型是布尔”。

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


可能的解决方案

直接使用_Bool类型

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

int main(void) {
  fun__Bool();
}

专业人士

  • 没有足迹

缺点

  • 丑!
  • 两种定义相同功能但打印不同输出的方法

手动展开FUN()

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

专业人士

  • 其他宏类型的通用解决方案

缺点

  • 不符合DRY

typedef for _Bool

typedef _Bool mybool;

FUN_H(mybool);
FUN(mybool);

int main(void) {
  fun_mybool();
}

专业人士

  • 保守

缺点

  • 需要特殊类型的名称

bool更改为typedef

#undef bool
typedef _Bool bool;

FUN_H(bool);
FUN(bool);

int main(void) {
  fun_bool();
}

专业人士

  • 干净

缺点

  • 可能会破坏一些神秘的东西

那我应该做什么?

3 个答案:

答案 0 :(得分:5)

如果FUN_H是宏,则内部bool宏调用将_Bool扩展为bool。如果您丢失了内部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

答案 1 :(得分:3)

如前所述,将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"); \
  }

答案 2 :(得分:1)

无论如何,您需要处理的动态是预处理器通过一系列(重新)扫描/全扩展操作来操作。出现在宏 M 扩展中的宏名称本身仅在重新扫描 M 的完整扩展(包括其所有参数和/或扩展名)的情况下扩展。令牌粘贴和字符串化的应用程序。

因此,如果宏参数是类似对象的宏的名称,则不能将其作为另一个宏的参数本身进行传递-如果您尝试这样做,则将传递该宏的扩展。这里的问题通常是如何确保在字符串化或粘贴令牌时发生这种情况,因此您想解决的问题有点不寻常。

您已经提出了许多您似乎不满意的替代方法,但这是一种具有您到目前为止讨论的所有优点,而又没有缺点的方法:定义一个额外的宏来修补不需要的宏扩张。例如,

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

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

我认为还有其他选择可以让您使用示例的原始样式,但是到目前为止,我已经考虑过的所有此类方法都更加混乱且脆弱。