2011-02-18 64 views
3

这个问题的前言是,我意识到C宏是一个敏感的话题。很多时候,他们可以通过非宏观解决方案来实现,这种解决方案更安全,不会遇到经典问题,如增加的参数;所以在那里,我有一个散列表实现在C与链接节点的冲突。我相信大多数人已经看过这一百万次,但它有点像这样。这个foreach C宏有多恶心?

typedef struct tnode_t { 
    char* key; void* value; struct tnode_t* next; 
} tnode_t; 

typedef struct table_t { 
    tnode_t** nodes; 
    unsigned long node_count; 
    unsigned long iterator; // see macro below 
     ... 
} 

我想提供一种通过节点迭代的抽象方式。我认为使用一个函数,它接受一个函数指针和功能适用于每一个节点,但我经常发现这种解决方案非常有限,所以我想出了这个宏:

#define tbleach(table, node) \ 
    for(node=table->nodes[table->iterator=0];\ 
     table->iterator<table->node_count;\ 
     node=node?node->next:table->nodes[++table->iterator])\ 
      if (node) 

因而可作,如:

tnode_t* n; 
tbleach(mytable, n) { 
    do_stuff_to(n->key, n->value); 
} 

我能看到的唯一缺点是迭代器索引是表的一部分,所以显然你不能在同一个表中同时发生两个循环。我不知道如何解决这个问题,但我不认为这是一个交易断路器,考虑这个小宏观有多大用处。所以我的问题。

** 更新 **

我纳入扎克和Jens的建议,删除与“其他”,并宣布迭代里面for语句的问题。一切似乎都奏效,但visual studio抱怨使用宏的地方“不允许使用类型名称”。我想知道这里发生了什么,因为它编译和运行,但我不知道迭代器的作用域。

#define tbleach(table, node) \ 
    for(node=table->nodes[0], unsigned long i=0;\ 
     i<table->node_count;\ 
     node=node?node->next:table->nodes[++i])\ 
     if (!node) {} else 

是这种方法不好的形式,如果不是有什么办法改进呢?

+2

我忍不住打量着邪恶博士。 “这是一个邪恶的宏?” – James 2011-02-18 16:02:06

+0

难道你不能将迭代器传入宏吗?例如:tbleach(mytable,iterator,n)...这将允许您运行多个循环。 – Nick 2011-02-18 16:03:21

回答

7

只有真的不可接受这里有一件事你已经说过 - 迭代器是表的一部分。你应该拉说出来,像这样:

typedef unsigned long table_iterator_t; 
#define tbleach(table, iter, node) \ 
    for ((iter) = 0, (node) = (table)->nodes[(iter)]; \ 
     (iter) < (table)->node_count; \ 
     (node) = ((node) && (node)->next) \ 
        ? (node)->next : (table)->nodes[++(iter)]) 

// use: 
table_iterator_t i; 
tnode_t *n; 
tbleach(mytable, i, n) { 
    do_stuff_to(n->key, n->value); 
} 

我也吸if语句转换成for循环expresssions,因为它是稍微更安全的这种方式(奇怪的事情就不会发生,如果循环结束括号后的下一个标记身体是else)。请注意,数组条目table->nodes[table->node_count]从常规惯例中读取,因此您需要为它分配空间(并确保它始终为NULL)。我认为,你的版本也是如此。

编辑:纠正了表条目为NULL的情况下的逻辑。

1

除了扎克伯格关于迭代以及有关终止if可能混淆语法答案:

如果你有C99,使用局部变量在for循环。这样可以避免周围范围的变量带来不好的意外,这些变量会包含悬挂指针。使用类似这样的宏内部:

for(nodeS node = ... 

,并在结束与_t名称由POSIX保留。所以最好不要将它们用于你自己的类型。