2016-09-18 36 views
2

我試圖在C. 寫鏈表在通用搜索功能的功能如下:在C編寫鏈表的通用搜索函數?

void* get_when_true(linked_list *l, 
        int (*condition)(const void*)) { 
     node **walk = &l->head; 
     while (*walk != NULL && !condition((*walk)->data)) { 
       walk = &((*walk)->next); 
     } 
     return (*walk)->data; 
} 

基本上條件是一個函數指針,這將針對每個元件進行測試的功能的鏈表,並且只要這個函數返回true就返回特定的節點。

我的鏈表中的每個節點都存儲一些ID。我的問題是,如果我不想搜索特定的ID,則必須編寫一個單獨的搜索功能,該功能只能檢查該特定的ID。基本上,如果我wan't找ID 10.我必須寫一個函數

​​

如果我想測試一些其他的ID說30,我會寫一個不同的功能again..I認爲你明白了我的觀點。

我只能想到currying來解決這個問題..但不幸的是C不提供currying,我不能使用一些編譯器特定的解決方法,因爲我的代碼應該在所有平臺上編譯。

使用全局變量作爲搜索ID是一種解決方案,但我不想避免它。

如何解決這個問題?

+0

但明天,如果我用我的鏈表存儲一些結構,整數和字符,我想比較它們都那麼結構格式將發生變化並且我的get_when_true函數將不會保持通用 – Nullpointer

回答

3

您應該向傳遞給您的回調的get_when_true添加另一個參數。這可以是另一個void *,因此它可以指向任何類型的結構。

void* get_when_true(linked_list *l, void *ctx, 
        int (*condition)(const void* data, void *ctx)) { 
     node **walk = &l->head; 
     while (*walk != NULL && !condition((*walk)->data, ctx)) { 
       walk = &((*walk)->next); 
     } 
     return (*walk)->data; 
} 

這裏有一個回調:

int isID(const void *data, void *ctx) { 
    int id = (int)ctx; 
    const struct whatever *a = data; 
    return a->ID == id; 
} 

像這樣來使用:

get_when_true(list, (void*)10, isID); 

在這裏,我甚至沒有需要的結構,所以我「欺騙」,只是轉換爲單個整數一個void*。因爲C本身不支持閉包,那裏有數據「附加」到(回調)函數指針,所以我寫的接受回調的函數也需要傳遞給回調函數的void*


雖然C不支持閉包,但有一個GNU擴展提供了可以訪問父函數作用域中的變量的嵌套函數。因此,使用您的原始代碼(沒有我的附加參數):

int get_when_id(linked_list *l, int id) { 
    int condition(const void *data) { 
     const struct whatever *a = data; 
     return a->ID == id; // id is "closed over" from parent 
    } 

    return get_when_true(list, condition); 
} 

這需要一個可執行堆棧。如果你看看反彙編,你會發現實際傳遞給get_when_true的函數指針不是condition本身的地址。而是在傳遞堆棧時生成可執行文件thunk的地址。該thunk將一個指向閉合變量的指針傳遞給實際的condition函數,以便它們自動在那裏可用。

參見:

+0

是的..這看起來是一個不錯的選擇!謝謝 – Nullpointer

+1

謝謝,提及關閉與void指針的關係! – Nullpointer

+1

不客氣。我花了很長時間才意識到兩者是直接相關的。使用GCC的嵌套函數查看我的附加示例。 –