1

这是一个在 C 中包装迭代器函数的宏示例,

宏定义:

/* helper macros for iterating over tree types */
#define NODE_TREE_TYPES_BEGIN(ntype) \
{ \
    GHashIterator *__node_tree_type_iter__ = ntreeTypeGetIterator(); \
    for (; !BLI_ghashIterator_done(__node_tree_type_iter__); BLI_ghashIterator_step(__node_tree_type_iter__)) { \
        bNodeTreeType *ntype = BLI_ghashIterator_getValue(__node_tree_type_iter__);
#define NODE_TREE_TYPES_END \
    } \
    BLI_ghashIterator_free(__node_tree_type_iter__); \
} (void)0

示例使用:

NODE_TREE_TYPES_BEGIN(nt)
{
    if (nt->ext.free) {
        nt->ext.free(nt->ext.data);
    }
}
NODE_TREE_TYPES_END;

但是嵌套使用(虽然功能)会导致阴影(gcc's -Wshadow

NODE_TREE_TYPES_BEGIN(nt_a)
{
    NODE_TREE_TYPES_BEGIN(nt_b)
    {
        /* do something */
    }
    NODE_TREE_TYPES_END;
}
NODE_TREE_TYPES_END;

我能想到的避免这种情况的唯一方法是将唯一标识符传递给NODE_TREE_TYPES_BEGINand NODE_TREE_TYPES_END。所以我的问题是...

如果在嵌套范围内的迭代器宏中声明的变量,有没有办法防止隐藏?

4

1 回答 1

0

您不需要在两个地方插入相同的唯一标识符,如果您可以重组块,使其永远不需要第二个宏来关闭它 - 那么您只有一个宏调用并且可以使用简单的解决方案,如__LINE__or __COUNTER__

您可以通过进一步利用for, 在块之前的文本位置插入打算在块之后发生的操作来重组块:

#define NODE_TREE_TYPES(ntype) \
    for (GHashIterator *__node_tree_type_iter__ = ntreeTypeGetIterator(); \
         __node_tree_type_iter__; \
         (BLI_ghashIterator_free(__node_tree_type_iter__), __node_tree_type_iter__ = NULL)) \
        for (bNodeTreeType *ntype = NULL; \
             (ntype = BLI_ghashIterator_getValue(__node_tree_type_iter__), !BLI_ghashIterator_done(__node_tree_type_iter__)); \
             BLI_ghashIterator_step(__node_tree_type_iter__))

原始宏对的外层是一个复合语句,它正好包含三件事:一个声明+初始化、一个封闭的for结构和一个单独的自由操作,之后不再使用声明的变量。

这使得重组为一个独立的而不是显式的复合语句变得非常容易for:声明+初始化进入for(如果你有两个变量,那么它的第一个子句就不会那么容易了,尽管它仍然是可能的); 封闭的for可以放在我们正在构建的标题的末尾之后for,因为它是一个单一的语句;自由操作放在第三个子句中。由于变量没有在任何进一步的语句中使用,我们可以利用它:NULL使用逗号运算符将 free 与 的显式赋值结合起来,然后使中间子句检查变量不是NULL,确保循环运行恰好一次。

嵌套for得到了类似但更小的修改。它的语句体包含一个声明和每个循环的初始化,但我们仍然可以将它提升出来;将声明放在未使用的第一个子句中for(仍将其放在新范围中),并在第二个子句中对其进行初始化,以便它在每次迭代开始时发生;再次使用逗号运算符将该初始化与实际测试结合起来。这从语句块中删除了所有样板,因此意味着您不再有任何大括号,因此不需要第二个宏来关闭大括号。

然后你有一个宏调用,你可以像这样使用:

NODE_TREE_TYPES (nt) {
    if (nt->ext.free) {
        nt->ext.free(nt->ext.data);
    }
}

(然后,您可以使用其他问题中显示的技术将唯一标识符的生成应用于此以轻松摆脱阴影)


这丑吗?滥用for语句和逗号操作符会使普通 C 程序员的皮肤爬行吗?哦主是的。但是,它更干净一些,如果你真的必须搞砸,这是一种有争议的“正确”搞砸方式。

拥有一个插入复合语句中断或隐藏右括号的“关闭”宏是一个更糟糕的主意,因为它不仅会给您带来标识符和匹配范围的问题,而且还会向读者隐藏程序的块结构; 滥用该for语句至少意味着程序的块结构和变量范围等也没有被破坏。

于 2014-07-31T22:22:46.490 回答