Nic*_*unt 3 c macros hashtable c-preprocessor
这个问题的前言是,我意识到C宏是一个敏感的主题.很多时候,他们可以通过非宏观解决方案来实现,这种解决方案更安全,不受经典问题的影响,例如增加的参数; 所以,在这种情况下,我在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
...
}
Run Code Online (Sandbox Code Playgroud)
我想提供一种迭代遍历节点的抽象方式.我考虑使用一个函数,它接受一个函数指针并将函数应用于每个节点,但我经常发现这种解决方案非常有限,所以我想出了这个宏:
#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)
Run Code Online (Sandbox Code Playgroud)
哪个可以用作:
tnode_t* n;
tbleach(mytable, n) {
do_stuff_to(n->key, n->value);
}
Run Code Online (Sandbox Code Playgroud)
我能看到的唯一缺点是迭代器索引是表的一部分,所以显然你不能在同一个表中同时进行两个循环.我不知道如何解决这个问题,但我不认为这是一个交易破坏者,考虑到这个小宏将有多大用处.所以我的问题.
**更新**
我收集了Zack和Jens的建议,用"else"删除了问题,并在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
Run Code Online (Sandbox Code Playgroud)
这种做法是不好的形式,如果没有,有没有办法改善它?
唯一真正令人无法接受的是你已经说过的东西 - 迭代器是表格的一部分.你应该这样做:
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);
}
Run Code Online (Sandbox Code Playgroud)
我也把这个if语句简化为for-loop表达式,因为它的方式稍微安全一些(如果循环体的紧支撑之后的下一个标记就不会发生奇怪的事情else).请注意,与通常的约定不同,table->nodes[table->node_count] 将读取数组条目,因此您需要为其分配空间(并确保它始终为NULL).我认为你的版本也是如此.
编辑:更正了表条目为NULL的情况的逻辑.