2011-02-18 63 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保留。所以最好不要將它們用於你自己的類型。