C预处理器魔法

3

我正在尝试使用预处理器技巧声明一个神奇变量。类似于这样:

DECLARE(x)

应该扩展为:
int _DECLARED_VARIABLE_x_LINE_12

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

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

你应该使用反引号或反斜杠来转义下划线,否则 Markdown 会使它们消失。 - Jasper
删除初始下划线,否则您的程序将具有未定义的行为。 - R.. GitHub STOP HELPING ICE
你如何引用魔术变量?只是好奇。 - Praveen S
@Praveen,这确实是个真正的谜团。当然,对于你永远不想再使用的名称来说,它可能是有用的... - RBerteig
可能是C预处理器和连接的重复问题。 - Jonathan Leffler
显示剩余5条评论
4个回答

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基本相同的结果。)


回答你的另一个问题,我不会在代码中引用那些变量;我需要这个来尝试一些编译器工具。 - redtuna

7
预处理器在尝试查找进一步的宏进行递归替换之前,会从宏替换列表中删除##运算符。这意味着在将__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__ 将接收完整的行号。

1

0

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

  1. 在粘贴之前,## 运算符的操作数不会被扩展。
  2. 如果宏参数与 ## 连接,则不会进行扩展。

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