C预处理器魔术

时间:2010-08-27 00:26:28

标签: c visual-studio c-preprocessor

我正在尝试使用预处理器技巧来声明一个魔术变量。像这样:

DECLARE(x)

应扩展为

int _DECLARED_VARIABLE_x_LINE_12

如果声明在输入源的第12行。我试图使用## token-pasting命令和__LINE__宏,但我在那里得到一个未解释的“__LINE__”,或者预处理器似乎完全忽略了我的行。我目前的猜测是:

 #define DECLARE(x) _DECLARED_VARIABLE_ ## x ## _LINE_ ## __LINE__

4 个答案:

答案 0 :(得分:9)

在这种情况下,通常的技巧是使用第二个宏。但是,这似乎不适用于GCC(MacOS X 10.6.4上的4.5.1),并且需要第三级宏:

#define DECLARE(x) _DECLARED_VARIABLE_ ## x ## _LINE_ ## __LINE__

#define DECLARE42(x, line) _DECLARED_VARIABLE_ ## x ## _LINE_ ## line
#define DECLARE41(x, line) DECLARE42(x, line)
#define DECLARE40(x) DECLARE41(x, __LINE__)

int DECLARE(y);
int DECLARE40(c) = 129;

'gcc -E'的输出:

# 1 "magicvars.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "magicvars.c"






int _DECLARED_VARIABLE_y_LINE___LINE__;
int _DECLARED_VARIABLE_c_LINE_8 = 129;

我不确定我是否有一个很好的解释为什么需要第三级宏。

我也很想知道在你创建这些变量之后你将如何引用这些变量。


我经历了许多变化,然后才能找到有效的方法:

#define DECLARE(x) _DECLARED_VARIABLE_ ## x ## _LINE_ ## __LINE__

#define DECLARE11(x, line) _DECLARED_VARIABLE_ ## x ## _LINE_ ## line
#define DECLARE10(x) DECLARE11(x, __LINE__)

#define DECLARE23(line) _LINE_ ## line
#define DECLARE22(x) _DECLARED_VARIABLE_ ## x
#define DECLARE21(x, line) DECLARE22(x) ## DECLARE23(line)
#define DECLARE20(x) DECLARE21(x, __LINE__)

#define DECLARE32(line) _LINE_ ## line
#define DECLARE31(x, line) _DECLARED_VARIABLE_ ## x ## DECLARE32(line)
#define DECLARE30(x) DECLARE31(x, __LINE__)

#define DECLARE42(x, line) _DECLARED_VARIABLE_ ## x ## _LINE_ ## line
#define DECLARE41(x, line) DECLARE42(x, line)
#define DECLARE40(x) DECLARE41(x, __LINE__)


int DECLARE(y);
int DECLARE10(z) = 12;
int DECLARE20(a) = 37;
int DECLARE30(b) = 91;
int DECLARE40(c) = 129;

玩得开心解决为什么非工作的人无法工作。然而,他们确实指出了我的工作答案。 (我注意到Sun C编译器在同一输入上产生与GCC基本相同的结果。)

答案 1 :(得分:6)

预处理器在尝试寻找进一步的宏以进行递归替换之前,从宏替换列表中删除##运算符。这意味着您对__LINE__的引用会“粘贴”到宏的其余部分之前它有可能被识别为__LINE__并替换为实际的行号。

因此,如果您想将行号嵌入宏中,除了通过宏参数传递它之外别无选择

#define DECLARE_(x, L) _DECLARED_VARIABLE_##x##_LINE_##L
#define DECLARE(x) DECLARE_(x, __LINE__)

这将正式解决原始宏定义中的直接问题。

但是,由于C / C ++预处理器规范中的另一个怪癖,这仍然无法正常工作:##附近的参数名称被替换为相应的参数值而没有递归宏扩展参数值。即L将替换为__LINE__,而不会先将__LINE__更改为实际的行号。

为了确保参数L的递归宏扩展,需要在宏定义中引入另一个“间接层”

#define DECLARE__(x, L) _DECLARED_VARIABLE_##x##_LINE_##L
#define DECLARE_(x, L) DECLARE__(x, L)
#define DECLARE(x) DECLARE_(x, __LINE__)

在这种情况下,处理DECLARE_(x, L)宏时,预处理器将递归处理L:首先将其替换为__LINE__,然后将__LINE__替换为实际的行号。 DECLARE__将收到完整的行号。

答案 2 :(得分:1)

如果使用“编辑并继续”,则在使用Visual Studio的调试模式下__LINE__存在问题。 Here is a reference to it.这个问题虽然已有几年了。如果该问题得到解决,那么Jonathan Leffler's solution将正常工作。

答案 3 :(得分:0)

关于##运算符有两个特殊的扩展规则:

  1. 在粘贴之前,不会扩展操作数到##运算符。
  2. 如果宏参数与##连接,则不会展开它们。