使用可变宏动态地为宏名称添加前缀

Dynamically prefix macro names with a variadic macro

背景

I've utilized a set of preprocessor macros from another question that allows me to prefix symbol names(枚举、函数名称、结构名称等)在我的源代码中,即:

#include <stdio.h>
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y)  PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)

void NAME(func)(int i);

int main(void)
{
    NAME(func)(123);

    return 0;
}

void NAME(func)(int i)
{
    printf("i is %d in %s.\n", i, __func__);
}

问题

这按预期工作,输出如下:i is 123 in func_3.

编辑

我想要这个代码:

#define NAME(SOME_MACRO_CONST)  (123)
#define NAME(SOME_MACRO_CONST2) (123)

扩展为:

#define 3SOME_MACRO_CONST  (123)
#define 3SOME_MACRO_CONST2 (123)

我知道宏不应该以数字开头。在最终代码中,我将使用 LIB_A_LIB_B_ 等名称作为前缀。

/编辑

但是,如果我尝试对宏做同样的事情作为我的 NAME 可变参数宏的参数,它会像这样失败:

重新使用 NAME 宏:

代码

#define NAME(MY_CONST)  (3)

输出

test.c:7:0: warning: "NAME" redefined
 #define NAME(MY_CONST) 3

手动粘贴前缀:

代码:

#define VARIABLE ## MY_CONST    (3)

输出:

test.c:8:18: error: '##' cannot appear at either end of a macro expansion
 #define VARIABLE ## MY_CONST (3)

问题

如何创建简单的宏定义(名称 + 值),所有宏都有一个共同的前缀?目标是能够制作源文件的多个副本并使用不同的标志编译它们,以便所有版本都可以链接到同一个最终二进制文件中而不会 symbol/macro 名称冲突(宏稍后将移动到头文件中).最终文件太大,无法用 M4 或模板语言等语言编写。 理想情况下,解决方案将涉及能够对所有用例使用单个 macro-function/variadic-macro,但我可以接受一个宏用于符号前缀,另一个宏用于宏名称前缀。

I would like this code:

 #define NAME(SOME_MACRO_CONST)  (123)
 #define NAME(SOME_MACRO_CONST2) (124)

To expand to:

 #define 3SOME_MACRO_CONST  (123)
 #define 3SOME_MACRO_CONST2 (124)

(为了便于阅读,我将第二个数字更正为 124,使其与第一个不同)

C 预处理器不可能做到这一点

有几个原因:

  • 3SOME_MACRO_CONST 不是有效标识符(对于预处理器和 C 编译器本身),因为它不是以字母或下划线开头。因此,假设您希望将代码扩展为:

      /// new desired expansion
      #define THREE_SOME_MACRO_CONST  (123)
      #define THREE_SOME_MACRO_CONST2 (124)
    
  • 这仍然是不可能,因为预处理器先于其他任何东西工作并且不能生成任何预处理器指令(例如#define)。

一个解决方法,如果你只想#define一些数字(可在编译时计算!!!)可能会扩展到一些匿名的 enum like

 enum {
   THREE_SOME_MACRO_CONST= 123,
   THREE_SOME_MACRO_CONST2= 124,
 };

而且你知道如何在细节上做到这一点。另请阅读 X-macros.


但是,即使您可以将您的要求更改为 可能 的内容,也可能不推荐,因为您的代码变得非常不可读(恕我直言)。有时您可以考虑编写一些简单的脚本(例如 sedawk ...),或使用其他一些预处理器,如 GPP,从其他东西生成 C 文件。

注意最严重的 build automation tools (like GNU make or ninja) -or even IDEs (they can be configured to) permit quite easily (by adding extra targets, recipes, commands, etc...) to generate some C (or C++) code from some other file, and that meta-programming practice has been routinely used since decades (e.g. bison, flex, autoconf, rpcgen, Qt moc, SWIG ...) 所以我很惊讶你不能这样做。生成一个包含许多 #define-s 的头文件是一种很常见的做法,我很惊讶你被禁止这样做。也许您只需要与您的经理或同事讨论。也许你需要找一些更有趣的工作。

就个人而言,我非常喜欢这种元编程方法(我在 1990 年攻读了这些方法的博士学位,我会在 每次 工作面试时讨论它们;一份工作禁止元编程不适合我。例如查看我在 POSIX 上的过去 GCC MELT project, and my future project also will have metaprogramming). Another way of promoting that approach is to defend domain specific languages (and the ability to make your DSL inside some large software project; for example the GCC compiler has about a dozen of such DSLs inside it....). Then, your DSL can (naturally) be compiled to C which is a common practice. On modern operating systems that generated C code could be compiled at runtime and dynamically loaded as a (generated) plugin (using dlopen...)


有时,您可以欺骗编译器。对于由 GCC 编译的项目,您可以考虑编写 GCC plugin.....(这比添加生成 C 代码的命令要多得多;您的插件可以提供额外的魔法编译指示或内置函数或属性其他一些宏)。

您还可以配置 gccspec file 来专门处理某些 C 文件。当心,这可能会影响以后的每次编译!