这是一个在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_BEGIN
和NODE_TREE_TYPES_END
。所以我的问题是......
如果在嵌套范围嵌套时在迭代器宏中声明变量,是否存在防止阴影的方法?
答案 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
声明至少意味着程序的块结构和可变范围等也不会被肢解。