【问题标题】:scoped anonymous variable declaration within a macro宏中的作用域匿名变量声明
【发布时间】:2020-06-15 18:55:35
【问题描述】:

我有一个用于记录的宏,我想在宏中定义一个临时记录,如下所示:

#define MACRO_LOG(...)             \
temprecord t;                      \
logger->logmessage(__VA_ARGS___); 

使用temprecord() { logger->increaseIndent() } 并在析构函数中使用decreaseIndent()

与名称的关联(例如,变量) 给出一个控制其生命周期的对象范围。通过不命名 对象,它的生命周期是绑定的

来源 Why do un-named C++ objects destruct before the scope block ends?

因为 temprecord 具有范围的生命周期,如果我有的话

{
   MACRO_LOG("stackoverflow example");
   MACRO_LOG("stackoverflow example");
}

在范围之外,我将有 0 缩进,在范围 1 和 2 缩进。 但是,当我命名我的 temprecord 时,我会重新定义变量名。如果我不声明它,我就得不到我想要的范围。

如何解决这个问题?我想用一个容器来放临时记录,但似乎无法解决这个难题..

我希望完成的是在我提到的范围内的双缩进,而不是 MACRO_LOG 范围内的单缩进。

【问题讨论】:

  • 如果您观察到使用宏等同于将宏的内容直接插入到它被引用的位置,并且如果您使用笔和纸进行计算,这在您的示例中意味着什么,原因因为你的重复符号错误变得非常明显,以及它的简单解决方案。

标签: c++ variables scope macros anonymous


【解决方案1】:

宏替换只是就地文本替换。因此,鉴于您显示的#define,代码:

{
   MACRO_LOG("stackoverflow example");
   MACRO_LOG("stackoverflow example");
}

扩展为:

{
    temprecord t;
    logger->logmessage("stackoverflow example");;
    temprecord t;
    logger->logmessage("stackoverflow example");;
}

这应该很明显为什么宏在同一范围内多次使用时不起作用,因为t 被多次声明。

有一些不同的方法可以解决这个问题:

1) 将MACRO_LOG() 的内容用大括号括起来,这样temprecord t 就在它自己的范围内,例如:

#define MACRO_LOG(...) { \
    temprecord t;                      \
    logger->logmessage(__VA_ARGS___);  \
}

这会将代码扩展为:

{
    {
        temprecord t;
        logger->logmessage("stackoverflow example");
    };
    {
        temprecord t;
        logger->logmessage("stackoverflow example");
    };
}

2) 将__LINE__ 附加到t 以赋予temprecord 的每个副本一个更独特的名称,例如:

(见Creating C macro with ## and __LINE__ (token concatenation with positioning macro)

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)

#define MACRO_LOG(...) \
temprecord TOKENPASTE2(t_, __LINE__); \
logger->logmessage(__VA_ARGS___);

这会将代码扩展为:

{
    temprecord t_12345;
    logger->logmessage("stackoverflow example");;
    temprecord t_12347;
    logger->logmessage("stackoverflow example");;
}

3) 使用comma operator 在调用logmessage() 的同一表达式中定义一个无名 变量,该变量在logmessage() 返回之前不会超出范围,例如:

#define MACRO_LOG(...) temprecord(), logger->logmessage(__VA_ARGS___);

这会将代码扩展为:

{
    temprecord(), logger->logmessage("stackoverflow example");;
    temprecord(), logger->logmessage("stackoverflow example");;
}

【讨论】:

  • 我将尝试 2,但 1) 和 3) 是我在发布此之前最初所做的解决方案(这不起作用,因为它们会在宏范围关闭时关闭)。
  • 太棒了,2) 非常感谢标记化!但是 1) 不起作用,因为您在范围内获得了 2 个单独的范围,我看到您刚刚对其进行了编辑。并且 3 不起作用,因为在每行的分号之后调用了析构函数。
  • 他们都工作,只是不是你想要的方式。坦率地说,我根本不会将 temprecord t; 放在 MACRO_LOG() 中,例如:#define MACRO_LOG(...) logger->logmessage(__VA_ARGS___); ... { temprecord t; MACRO_LOG("stackoverflow example"); MACRO_LOG("stackoverflow example"); } 但是,假设您还有 MACRO_LOG() 的其他用途,可以保证 t 变量,然后我将为这个特定用例创建另一个宏:#define MACRO_LOG_NO_T(...) logger->logmessage(__VA_ARGS___); ... { temprecord t; MACRO_LOG_NO_T("stackoverflow example"); MACRO_LOG_NO_T("stackoverflow example"); }
  • 在这种情况下,如果我理解正确,所有项目中所有当前使用的 MACRO_LOG 都必须附有该范围内的temprecord t; 声明?这是我不适应它的唯一原因……但也许这就是要走的路。感谢您的思考!
  • @dejoma 仅在同一作用域内多次调用MACRO_LOG() 的地方,以避免同一作用域内出现多个t 变量。当 1 个变量就足够时,为什么要声明多个变量?但这取决于你,这就是为什么我也给了你__LINE__ 替代方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-27
相关资源
最近更新 更多