从预处理器宏中包含

Include from preprocessor macro

我正在尝试包含一个由 pre-processor 宏构建的文件,但似乎 运行 由于有关令牌的规则而进入墙。我在这里使用答案作为参考:Concatenate string in C #include filename,但我的情况不同之处在于我用来构造包含的定义中有小数点。这是我目前无法通过预处理器阶段的内容: main.c:

#include <stdio.h>
#include <stdlib.h>

#define VERSION 1.1.0
#define STRINGIFY(arg) #arg
#define INCLUDE_HELPER(arg) STRINGIFY(other_ ##arg.h)
#define INCLUDE_THIS(arg) INCLUDE_HELPER(arg)

#include INCLUDE_THIS(VERSION)

int main(int argc, char **argv) {

   printf(INCLUDE_THIS(VERSION));
   fflush(stdout);
#if defined (SUCCESS)
   printf("\nSUCCESS!\n");
#endif
   return EXIT_SUCCESS;
}

other_1.1.0.h:

#define SUCCESS

如果我使用 #define VERSION 1_1_0 并相应地重命名 header 它会起作用(但不适合我使用,因为我无法控制 header 文件的名称实际项目使用),但 1.1.0 不是有效的预处理器标记。

编辑: 在仔细阅读文档后,我发现 1.1.0 是一个有效的 preprocessing numberother_1.1.0 的结果串联是无效的。无论如何,无法构建包含的问题仍然存在。

通过一些实验,我想出了一个解决方案,虽然不理想,但可能是可行的。

#define VERSION _1.1.0
#define STRINGIFY(arg) #arg
#define INCLUDE_HELPER(arg) STRINGIFY(other ##arg.h)
#define INCLUDE_THIS(arg) INCLUDE_HELPER(arg)

#include INCLUDE_THIS(VERSION)

我没有将 other_1.1.0 粘贴在一起,而是粘贴了 other_1.1.0。我不确定为什么这是可以接受的,因为生成的标记是相同的,但确实如此。

我仍然希望有一个解决方案,允许我只定义不带下划线的版本号,所以我会推迟接受这个答案,以防有人能想出一个更优雅的解决方案(并且适用于反正不需要下划线的人)

如果您将 -DVERSION=1.1.0 作为 compile-line 参数传递,而不是在源代码中将其 hard-wiring 传递,那么没有什么可以阻止您使用 [=16 传递第二个定义=] 或 shell 进行连接。例如,在 makefile 中,您可能有:

VERSION = 1.1.0
VERSION_HEADER = other_${VERSION}.h

CFLAGS += -DVERSION=${VERSION} -DVERSION_HEADER=${VERSION_HEADER}

然后:

#include <stdio.h>
#include <stdlib.h>

#define STRINGIFY(arg) #arg
#define INCLUDE_HELPER(arg) STRINGIFY(arg)
#define INCLUDE_THIS(arg) INCLUDE_HELPER(arg)

#include INCLUDE_THIS(VERSION_HEADER)

int main(void)
{
   printf("%s\n", INCLUDE_THIS(VERSION));
#if defined (SUCCESS)
   printf("SUCCESS!\n");
#endif
   return EXIT_SUCCESS;
}

这基本上是您的代码,删除了 #define VERSION 行,并使用 VERSION_HEADER 的字符串化版本,而不是尝试在源代码中构造 header 名称。您可能想使用:

#ifndef VERSION
#define VERSION 1.1.0
#endif
#ifndef VERSION_HEADER
#define VERSION_HEADER other_1.1.0.h
#endif

一些合适的默认回退版本,以防运行 编译未在命令行上指定信息。或者您可以使用 #error You did not set -DVERSION=x.y.z on the command line 而不是设置默认值。

编译时(源文件hdr59.c):

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -DVERSION=1.1.0 \
>     -DVERSION_HEADER=other_1.1.0.h hdr59.c -o hdr59
$ ./hdr59
1.1.0
SUCCESS!
$

我会把宏的三行和#include行放在一个单独的小header里,这样在需要header版本的时候就可以包含了。如果还需要默认设置,那么将代码放入单独的 header 以供重用的重要性就会增加。该程序的源代码可能包含:

#include "other_version.h"

并且 header 会安排包含正确的文件,大致如图所示。

一旦您不再考虑令牌串联,这就很容易了。字符串化适用于任何标记序列,因此无需强制其参数成为单个标记。您确实需要一个额外的间接寻址以便扩展参数,但这是正常的。

唯一的技巧是写序列时不要有空格,这就是 ID 的目的:

#define STRINGIFY(arg) STRINGIFY_(arg)
#define STRINGIFY_(arg) #arg
#define ID(x) x

#define VERSION 1.1.0
#include STRINGIFY(ID(other_)VERSION.h)

有关详细说明,请参阅