如何在 #ifdef 内进行宏展开?

6

我有一些通用代码,使用预处理宏将某个前缀添加到其他宏中。下面是一个简化的示例:

#define MY_VAR(x) prefix_##x

"

"prefix_"实际上是在其他地方定义的,因此每次包含文件时都会有所不同。它运行良好,但现在我有一些代码,如果其中一个标记不存在,我想跳过它,但这并不起作用:

"
#if defined MY_VAR(hello)

我希望它能扩展成这样:

我想要的是将其扩展为:

#ifdef prefix_hello

但我不知道该怎么做。我需要使用MY_VAR()宏来进行扩展,所以我不能硬编码名称。(实际上是为了一些测试代码,同一段代码每次都会包含不同的前缀来测试一堆类,我想跳过一些类的几个测试。)C++预处理器能实现这个吗?
更新: 下面是一些半可编译代码,以进一步证明问题:(为避免将其挤入下面的注释中)
#define PREFIX hello

#define DO_COMBINE(p, x)  p ## _ ## x
#define COMBINE(p, x)     DO_COMBINE(p, x)
#define MY_VAR(x)         COMBINE(PREFIX, x)

// MY_VAR(test) should evaluate to hello_test

#define hello_test "blah blah"

// This doesn't work
#ifdef MY_VAR(test)
  printf("%s\n", MY_VAR(test));
#endif

请参阅:https://dev59.com/wHI_5IYBdhLWcg3wMf5_#1489985;另外,也可以查看https://dev59.com/43VC5IYBdhLWcg3wvT7g#196018以了解字符串化的相关资料(在很多方面都非常相似)。 - Jonathan Leffler
1
字符串化工作正常,问题出在 #ifdef 上,这似乎没有被其他问题涵盖。 - Malvineous
这些答案中有任何一个是可以接受或有帮助的吗? :) - Joseph Quinsey
抱歉,是的,答案似乎是“不可能,但你可以绕过它”,所以我标记了最简单的解决方法作为被接受的答案。 - Malvineous
3个回答

3

你的程序是否比这个问题描述的更为复杂?指令

#define MY_VAR(x) prefix_##x

定义了一个预处理器标识符。调用

blah ^&* blah MY_VAR(hello) bleh <>? bleh

只需将代码放入预处理器的一端,然后从另一端输出,而无需定义任何内容。

如果没有其他魔法发生,您不能指望预处理器记住在先前的源代码中传递了哪些宏参数。

您可以尝试使用以下方法:

#define prefix_test 1
#if MY_VAR(test) == 1
#undef prefix_test // optional, "be clean"
...
#endif
#undef prefix_test

查询前缀当前是否具有特定值prefix_。(未定义的标识符将被替换为零。)


你试过编译吗?我的编译器无法处理 "#if MY_VAR(test) == 1"。 - Malvineous
@Malvineous:是的,对我来说很好用。GCC。你遇到了什么问题? - Potatoswatter
@Potatoswatter:test.c:2:11:错误:缺少二进制运算符“(”之前的标记,这是GCC 4.4.2。 - Malvineous
@Malvineous: MY_VAR 定义了吗?我的意思是你可以在你的示例代码中使用它,而不是 MY_VAR 是一个标准函数。 - Potatoswatter
当然,抱歉。我进一步调查了您的解决方案,发现它之所以有效是因为它没有使用#ifdef。如果我使用#if并检查该值是否等于某个值,则可以工作,但由于我不知道该值是什么,因此需要检查它是否存在。对于字符串来说,它似乎也有点不可靠。我已经更新了问题,并附上了一些示例代码,希望能更好地解释这个问题! - Malvineous
@Malvineous:预处理器并不是那么灵活。我建议您将我的解决方案应用于您的程序,并定义一个“标签宏”,对于每个未知的宏,如hello_test,将其等于1,而不是尝试黑客#ifdefdefined()来(仅部分地)扩展其参数,这是标准明确禁止的。 - Potatoswatter

2

我认为你已经达到了预处理器无法再满足的水平;它真的相当简单。你考虑过使用模板吗?(当然,假设它们对你的问题有意义。)


当然,如果它能够做到,那我就得重写一些代码...;-) - Donal Fellows
谢谢建议。我一直在使用宏,因为它们很快速和简单,但如果它们不能满足需求,我想我将不得不调查诸如模板之类的替代方案! - Malvineous
请注意,宏可以在#if指令中展开;只是在这种情况下您不能这样做,可能是因为编译器无法解析defined参数中的括号。 - Joey Adams
@Joey:那也是我理解的。 - Donal Fellows

0
这是针对您更新的问题的回复。对于每个可能的hello_test,在任何#define hello_test“blah blah”的可能性之后,添加以下行:
#ifndef hello_test
#define hello_test (char*)0
#endif

然后将您的测试更改为:

if (MY_VAR(test))
    printf("%s\n", MY_VAR(test));

或者,作为另一种选择,在所有的 #define …“blah blah”的前面,添加以下行:
static const char * const MY_VAR(test);

对于 MY_VAR 和 test 的所有可能值进行测试。这将避免硬编码 "hello_test"。(这里的第二个 const 可以消除 gcc 警告:“hello_test”已定义但未使用。)


有趣的想法 - 但我不太喜欢为可能被编译进或编译出的东西声明一堆变量 - 即使它们未使用,我认为编译器也不够聪明,无法从编译后的代码中省略这些变量。 - Malvineous

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接