【问题标题】:C preprocessor magicC 预处理器魔法
【发布时间】:2010-08-27 00:26:28
【问题描述】:

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

DECLARE(x)

应该扩展到

int _DECLARED_VARIABLE_x_LINE_12

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

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

【问题讨论】:

  • 你应该使用反引号或反斜杠来转义你的下划线,否则markdown会使它们消失
  • 删除初始下划线或您的程序有未定义的行为。
  • 如何引用魔法变量?只是好奇。
  • @Praveen,保留以下划线和大写字母开头的标识符。使用任何已知存在于您的实现中的方法都可以。声明自己不是那么好。
  • @RBerteig :我理解这一点,但我不清楚的是,如果一个变量如上所述声明为行号作为它的唯一值,你如何在代码的其他部分访问它们。

标签: c visual-studio c-preprocessor


【解决方案1】:

在这种情况下,通常的技巧是使用第二个宏。但是,这似乎不适用于 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 基本相同的结果。)

【讨论】:

  • 回答你的其他问题,我不会在代码中引用这些变量;对于我正在修补的一些编译器工具,我需要它。
【解决方案2】:

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

因此,如果你想将行号嵌入到你的宏中,你别无选择,只能通过宏参数传递它

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

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

但是,由于 C/C++ 预处理器规范中的另一个怪癖,这仍然无法按预期工作:与## 相邻的参数名称被替换为相应的参数值没有中的递归宏扩展参数值。 IE。 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__ 将收到完整的行号。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 2017-12-24
        • 1970-01-01
        • 2016-09-16
        • 2010-09-07
        • 1970-01-01
        • 2011-07-02
        • 2014-02-26
        • 2020-02-09
        相关资源
        最近更新 更多