C 预处理器:尽早评估宏

C Preprocessor: Evaluate macro early

考虑以下设置:

a.h

#define A 5
#define B A
#undef A
#define A 3

a.c

#include "a.h"
#include <stdio.h>

int main()
{
    printf("%d\n", B);
    return 0;
}

虽然这非常合理地打印 3,但有没有办法让它打印 5,即在 a.h 的第二行用 5 替换 A?

不,没有办法做到这一点。除非你知道 A 的所有可能值,并且它们总是整数,在这种情况下你可以费力地依次测试每个值:

#if A == 0
# define B 0
#elif A == 1
# define B 1
#elif A == 2
# define B 2
/*  ... and a very long etc. */
#endif

如果你的用例只涉及整数,你有更多的选择。例如,您可以将 B 声明为 static const intenum(取决于语言)而不是宏,这显然会使用宏的当前值。如果你真的想要宏,Boost 预处理库有一个 implementation 上面繁琐的 #ifs 序列(巧妙地减少了 log(N) 而不是 N 所需的预处理器语句的数量).


#define预处理器指令中没有宏替换; §6.10 段涵盖了这一事实。 C 标准的第 7 条(C++ 标准的第 16 段第 6 段,措辞相同):

The preprocessing tokens within a preprocessing directive are not subject to macro expansion unless otherwise stated.

#if#include 指令的描述中,标准指定确实会发生宏替换,这就是为什么上面的 #if 解决方案有效(以及 Boost 实现,它也使用计算 #include).

是的。 Boost 的预处理器库(一组可移植的包含,而不是扩展的预处理器)包括 support for "mutable" macro definitions。您可以将其定义为扩展为可变槽的引用,而不是直接定义一个宏来扩展为一个值,这可以更改,因为它提前将值 "assigned" 扩展到它。在这种情况下,您对更改值的能力不太感兴趣,因为这种早期扩展意味着它可以在使用 [=12= 之前的某个时刻从 A 中获取值] 或 A.

的重新定义
#include <boost/preprocessor/slot/slot.hpp>

#define A 5
#define B BOOST_PP_SLOT(1)

// "assign" A to B
#define BOOST_PP_VALUE A
#include BOOST_PP_ASSIGN_SLOT(1)

#undef A
#define A 3

#include "a.h"
#include <stdio.h>

int main()
{
    printf("%d\n", B);  // 5
    return 0;
}

只支持整数。它利用了 #if 指令强制扩展任何包含的宏这一事实(#line#error 也是如此,尽管它们对此目的不是很有用),并使用它们来构建为要分配给的插槽创建一个 等效整数值 ,存储在隐藏的后端宏中。这样它可以 "extract" 来自 A 的值,然后 B 可以引用该值本身,即使 A 更改或删除。