2013-01-15 63 views
2

我希望能夠通過函數將函數傳遞給C中的函數我已經使用了C幾年,並且我意識到實施適當關閉和高階職能的障礙。這幾乎是不可逾越的。在C中實現函數代表與聯合和函數指針

我搜遍StackOverflow的,看看有什麼其他來源不得不對此事說:

...並沒有呈現銀子彈通用的答案,或者使用可變參數或組裝之外。我沒有彙編的骨頭,但如果我能夠有效地實現主機語言的功能,我通常會嘗試。

既然不能有HOF輕鬆...

我很想高階函數,但我會滿足於在緊要關頭的代表。我懷疑的東西,如下面的代碼,我可以在C

得到一個可行的 委託執行

像這樣的實施想到:

enum FUN_TYPES { 
    GENERIC, 
    VOID_FUN, 
    INT_FUN, 
    UINT32_FUN, 
    FLOAT_FUN, 
}; 

typedef struct delegate { 
    uint32 fun_type; 
    union function { 
     int (*int_fun)(int); 
     uint32 (*uint_fun)(uint); 
     float (*float_fun)(float); 
     /* ... etc. until all basic types/structs in the 
      program are accounted for. */ 
    } function; 
} delegate; 

用法示例:

void mapint(struct fun f, int arr[20]) { 
    int i = 0; 
    if(f.fun_type == INT_FUN) { 
     for(; i < 20; i++) { 
      arr[i] = f.function.int_fun(arr[i]); 
     } 
    } 
} 


不幸的是,這種代表方式存在一些明顯的缺點:

  • 沒有類型檢查,通過檢查'fun_type'字段保存你自己做的那些檢查。
  • 類型檢查會在您的代碼中引入額外的條件,使其比以前更混亂,更分散。
  • 函數的(安全)可能排列的數量受'fun_type'變量大小的限制。
  • 函數指針定義的枚舉和列表必須是機器生成的。除了微不足道的情況之外,其他任何事情都會與瘋狂接近。
  • 不幸的是,通過普通的C,效率不如mov - >調用序列,這可能會在彙編中完成(有些困難)。


有誰知道一個更好的方式做一些類似於C代表?

注:的更便攜,高效,更好

另外,還要注意:我聽說過Don Clugston's very fast delegates用於C++。但是,我對C++解決方案不感興趣 - 只是C。

+1

「類型檢查會在您的代碼中引入額外的條件,使得它比以前更混亂,更分散。」這就是爲什麼函數指針類型的聯合沒有多大意義,因爲您已經擁有枚舉,您可以簡單地將它傳遞給switch語句,該語句又調用相應的函數。此外,您似乎沒有考慮參數和返回值在哪裏適合所有這些。最後,作爲一種通用編程經驗法則:通用編程總是會使代碼變慢。 – Lundin

+0

你說得對,我沒有一個簡單的回報類型的答案。至於參數,在C中一般解決這個問題的最可能方法是使用可能類型的另一個聯合。是的,這是醜陋的罪過。當我看到爲已發佈的方法生成的程序集輸出時,我發現它的效率遠遠低於我希望的效率。因此我爲什麼要求提供更好方法的建議。 –

回答

2

您可以爲所有函數添加一個void*參數以允許綁定參數,委派等。不幸的是,你需要爲處理外部函數和函數指針的任何東西編寫包裝器。

+0

這是過去3個月唯一的答案,所以我會將其標記爲海報的禮貌答案。 –

0

有兩個問題,我已經調查了相似的東西提供略有不同版本的基本技術的技術。這樣做的缺點是,由於參數列表是在運行時構建的,因此您會失去編譯時檢查。第一個是my answer to the question of Is there a way to do currying in C。這種方法使用代理函數來調用函數指針和函數的參數。第二個是my answer to the question C Pass arguments as void-pointer-list to imported function from LoadLibrary()

其基本思想是有一個內存區域,然後用於構建參數列表,然後將該內存區域作爲該函數調用的一部分推入堆棧。結果是被調用函數將內存區域看作參數列表。

在C中的關鍵是定義一個struct其中包含一個數組,然後用作內存區域。調用被調用的函數時,整個struct按值傳遞,這意味着將設置到數組中的參數壓入堆棧,以便調用的函數不會看到struct值,而是看到參數列表。

對於咖喱問題的答案,內存區域包含一個函數指針以及一個或多個參數,一種閉包。然後將存儲區域交給代理函數,該代理函數實際上使用閉包中的參數調用該函數。

這是可行的,因爲標準的C函數調用將參數推入堆棧,調用該函數並在函數返回時調用者清理堆棧,因爲它知道實際被推入堆棧的內容。