我的代码必须使用前C99编译器编译(我们正在进行更新,但这是一项艰巨的任务),正在考虑使用C99设计的实用程序库。特别是,这些实用程序定义了一个hashmap类型,并提供了一个宏来迭代它,类似于以下内容:
#define MAP_FOREACH(key, val, map) \
for (struct _map_iterator iter __attribute__((cleanup(_map_iter_cleanup))); \
(key) = iter->pair->key, \
(value) = iter->pair->value; \
iter = iter->get_next_cb())
实际代码还有一些(确保迭代器名称是唯一的功能等),但这涵盖了我的问题,即1999年之前的C版本不支持初始化变量在for循环中。现在明显的解决方法是将初始化移到循环之外,代码如下:
// Doesn't work
#define MAP_FOREACH(key, val, map) \
struct _map_iterator iter __attribute__((cleanup(_map_iter_cleanup)));
for (; \
(key) = iter->pair->key, \
(value) = iter->pair->value; \
iter = iter->get_next_cb())
问题是__attribute__((cleanup(_map_iter_cleanup)))
位。根据{{3}},它定义了附加到变量的清理代码,当变量超出范围时,它会运行。因为我们已经在for循环之外移动了迭代器声明,所以它的范围已经改变,清理代码在别处运行。不幸的是,库的其他部分依赖迭代器立即清理 - 映射会跟踪已启动的迭代器数量,并在它们全部消失之前抛出错误。
我一直在尝试并且没有想到现在这几天干净利落的方式,但我很短暂。我真的不想在C99之前的代码中重新实现宏加清理,但是这个库遍布整个地方并且更改API以包含迭代后清理调用会很痛苦,而不是提到不优雅。
有没有人以前遇到过这种事情并且知道解决方法?
编辑:我们使用GCC 4.2.2和-std = c89选项
答案 0 :(得分:3)
我不完全清楚C99之前标准缺少的功能,但我认为你可以像下面这样做:
#define MAP_FOREACH(key, val, map) \
{
struct _map_iterator iter __attribute__((cleanup(_map_iter_cleanup)));
for (; \
(key) = iter->pair->key, \
(value) = iter->pair->value; \
iter = iter->get_next_cb())
然后无论你在哪里使用它,你都必须用一个额外的大括号}
附加循环。
答案 1 :(得分:2)
一种可能性是创建一个必须在C89代码中使用的替代宏。您必须扩展范围并封装操作,例如通过创建一组新的宏,如下所示:
/* Force a compiler error if non-C99 code uses the C99 macro. */
#if __STDC_VERSION__ >= 199901L
/* C99 code */
#define MAP_FOREACH(key, val, map) \
for (struct _map_iterator iter __attribute__((cleanup(_map_iter_cleanup))); \
(key) = iter->pair->key, \
(value) = iter->pair->value; \
iter = iter->get_next_cb())
#endif
/* C89-compatible macro */
#define MAP_FOREACH_DO(key, val, map, statement) \
do{ struct _map_iterator iter __attribute__((cleanup(_map_iter_cleanup))); \
for (; \
(key) = iter->pair->key, \
(value) = iter->pair->value; \
iter = iter->get_next_cb()) \
{ \
statement; \
} \
}while(0)
对于简单的陈述,你可以这样做:
MAP_FOREACH_DO(..., printf(%s=%s\n", key, value));
...对于更复杂的陈述,你可以调用一个函数。
理想情况下,当然,您只需切换到C99(至少是相关代码)并完成它。
答案 2 :(得分:1)
你可能能够逃脱:
#define MAP_FOREACH(key, val, map) \
struct _map_iterator iter;
for (; \
(key) = iter->pair->key, \
(value) = iter->pair->value ? 1 : _map_iter_cleanup(&iter), 0; \
iter = iter->get_next_cb())
当循环条件为false时显式调用清理函数。
不幸的是,如果有任何MAP_FOREACH
循环通过break;
退出,则会失败 - 迭代器将不会被清除。您可以通过添加另一个宏来解决这些问题:
#define MAP_FOREACH_BREAK { _map_iter_cleanup(&iter); break; }
并用此宏替换所有有问题的中断。
答案 3 :(得分:0)