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 int
或 enum
(取决于语言)而不是宏,这显然会使用宏的当前值。如果你真的想要宏,Boost 预处理库有一个 implementation 上面繁琐的 #if
s 序列(巧妙地减少了 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
更改或删除。
考虑以下设置:
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 int
或 enum
(取决于语言)而不是宏,这显然会使用宏的当前值。如果你真的想要宏,Boost 预处理库有一个 implementation 上面繁琐的 #if
s 序列(巧妙地减少了 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
更改或删除。