1

背景

我使用了另一个问题中的一组预处理器宏,这些宏允许我在源代码中为符号名称(枚举、函数名称、结构名称等)添加前缀,即:

#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)

问题

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

4

1 回答 1

2

我想要这段代码:

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

扩展至:

 #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

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

你知道如何在细节中做到这一点。另请阅读有关X-macros 的信息。


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

请注意,大多数重要的构建自动化工具(如GNU makeninja) - 甚至IDE(它们可以配置为)允许很容易(通过添加额外的目标、配方、命令等......)生成一些 C(或C++) 来自其他文件的代码,并且几十年来一直在常规使用元编程实践(例如bisonflexautoconfrpcgenQtmocSWIG ...),所以我很惊讶你不能这样做。生成包含许多的头文件#define-s 是一种如此普遍的做法,我很惊讶你被禁止这样做。也许您只需要与您的经理或同事讨论。也许你需要寻找一些更有趣的工作。

就个人而言,我非常喜欢这种元编程方法(我在 1990 年获得了关于这些的博士学位,我会在每次工作面试时讨论它们;元编程被禁止的工作不适合我。看看我过去的GCC MELT项目,我未来的项目也将有元编程)。推广这种方法的另一种方法是保护特定领域的语言(以及在一些大型软件项目中制作 DSL 的能力;例如,GCC编译器里面有大约一打这样的 DSL……)。然后,您的 DSL 可以(自然地)编译为 C,这是一种常见的做法。在生成 C 代码的现代操作系统上,可以在运行时编译并动态加载作为(生成的)插件(在 POSIX 上使用dlopen ...)


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

您还可以配置您的规范文件gcc来专门处理一些 C 文件。请注意,这可能会影响以后的每次编译!

于 2017-11-29T16:28:23.827 回答