如何防止C中的迭代器宏出现阴影

时间:2013-09-13 04:08:56

标签: c macros suppress-warnings gcc-warning

这是一个在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的-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_BEGINNODE_TREE_TYPES_END。所以我的问题是......

如果在嵌套范围嵌套时在迭代器宏中声明变量,是否存在防止阴影的方法?

1 个答案:

答案 0 :(得分:0)

您不需要在两个地方插入相同的唯一标识符,如果您可以重新构建块以便它永远不需要第二个宏来关闭它 - 那么您只有一个宏调用并且可以使用像{这样的简单解决方案{1}}或__LINE__

您可以通过进一步利用__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标题的结尾之后放置,因为它是单个语句;自由操作放在第三个条款中。由于变量未在任何进一步的语句中使用,我们可以利用它:使用逗号运算符将free与for的显式赋值结合起来,然后使中间子句检查变量不是NULL,确保循环只运行一次。

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

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

for

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


这是丑陋的吗?滥用NODE_TREE_TYPES (nt) { if (nt->ext.free) { nt->ext.free(nt->ext.data); } } 语句和逗号运算符会导致C程序员的平均皮肤爬行吗?哦,主啊。但是,它有点清洁,如果你真的不得不搞砸,这是可以说是“正确”的方式。

有一个插入复合语句中断或隐藏大括号的“close”宏是一个更糟糕的想法,因为它不仅会给你带来标识符和匹配范围的问题,而且它还隐藏了程序的块结构来自读者;滥用for声明至少意味着程序的块结构和可变范围等也不会被肢解。