簡單的答案是沒有,C沒有按不支持它。
你可以將一個可變前端拿到一個函數的地址,並創建一個堆棧框架以用這些參數調用該函數,但它不會是便攜式的。如果你想讓它變得便攜,和可以自由修改你要通過這個前端調用的其他函數(轉換成不適合直接調用的表單),你可以重寫所有這些函數來接收va_alist
作爲他們的唯一參數,並使用va_arg
檢索正確的數量/類型的參數:
// pointer to function taking a va_alist and return an int:
typedef int (*func)(va_alist);
void invoke(func, ...) {
va_alist args;
va_start(args, func);
func(args);
va_end(args);
}
編輯:對不起,因爲@missingno指出,我沒有做這項工作相當它的目的的方式。這實際上應該是兩個功能:一個接受輸入並將它們包裝到一個結構中,另一個接受結構並調用預期的功能。
struct saved_invocation {
func f;
argument_list args;
};
saved_invocation save(func f, ...) {
saved_invocation s;
va_alist args;
s.f = f;
va_start(args, f);
va_make_copy(s.args, args);
va_end(args);
}
int invoke(saved_invocation const *s) {
s->f(s->args);
}
對於va_make_copy
,你會遇到更多非標準的東西。它不會與va_copy
- va_copy
通常只存儲一個指向參數開頭的指針,它只在當前函數返回前保持有效。對於va_make_copy
,您必須存儲所有實際參數,以便稍後檢索它們。假設你使用了argument
結構,我將在下面概述一下,你可以通過使用va_arg
來查看參數,並使用malloc
(或其他)來爲每個參數分配一個節點,並創建某種動態數據結構。,鏈表,動態數組)來保存參數,直到您準備好使用它們。
您還必須添加一些代碼來處理釋放內存,一旦完成特定的綁定函數。它也會實際上改變你的函數簽名,直接取得一個va_list,並採取你設計的任何類型的數據結構來保存你的參數列表。
[編輯完]
這將意味着你要調用所有其他函數簽名將需要:
int function(va_alist args);
...然後每個功能必須通過va_arg
檢索它的參數,所以(例如),這是要帶兩個整數作爲參數,並返回它們的和看起來像這樣的功能:
int adder(va_alist args) {
int arg1 = va_arg(args, int);
int arg2 = va_arg(args, int);
return arg1 + arg2;
}
這有兩個明顯的問題:第一,即使我們不再需要爲每個函數分別包裝,我們仍然最終爲每個函數添加額外的代碼,以便通過單個包裝器調用它。在代碼大小方面,它不可能比盈虧更好,並且可能很容易成爲淨虧損。
然而,更糟糕的是,由於我們現在將所有函數的所有參數作爲變量參數列表檢索,因此我們不再對參數進行任何類型檢查。如果我們想厲害的是,它會(當然)有可能增加一個小包裝類型和代碼來處理,以及:
struct argument {
enum type {CHAR, SHORT, INT, LONG, UCHAR, USHORT, UINT, ULONG, /* ... */ };
union data {
char char_data;
short short_data;
int int_data;
long long_data;
/* ... */
}
}
然後,當然,你會寫更代碼查詢每個參數的枚舉表明它是預期的類型,並且如果是的話從聯合中檢索正確的數據。然而,這會增加一些嚴重的醜陋調用的功能 - 而不是:
invoke(func, arg1, arg2);
...你會得到這樣的:
invoke(func, make_int_arg(arg1), make_long_arg(arg2));
顯然,這可以完成。不幸的是,它仍然沒有好處 - 減少代碼的最初目標幾乎肯定已經完全喪失。這確實消除了包裝函數 - 但是我們不是使用簡單的包裝和簡單調用的簡單函數,而是使用複雜的函數和複雜的調用,以及一小段額外的代碼來將值轉換爲我們的特殊參數類型,另一個從參數中檢索值。
雖然有些案例可以證明這樣的代碼是正確的(例如,爲Lisp寫一些類似的解釋器),但我認爲在這種情況下,它可能會導致淨虧損。即使我們忽略額外的代碼來添加/使用類型檢查,每個函數中的代碼都會檢索參數,而不是直接接收它們,而不是我們試圖替換的包裝器。
- 「將無法移植」幾乎是輕描淡寫 - 你真的不能這樣在 c執行的話 - 你只是需要使用匯編語言,甚至上手。
我不明白部分函數應用程序在哪裏? 'func2'的代碼是什麼? – pajton 2011-04-02 16:23:57
如果我認爲他是對的,他想要咖喱。如果這是你正在尋找看到http://stackoverflow.com/questions/1023261/is-there-a-way-to-do-currying-in-c – flolo 2011-04-02 16:39:43
簡短的回答是,不,C不支持它。你可能會一起攻擊一個帶有函數地址的可變前端,並創建一個堆棧框架以用這些參數調用該函數,但它不會真的是可移植的,並且您將失去對任何函數的類型檢查這樣調用。海事組織,類型檢查的損失使其成爲淨虧損。 – 2011-04-02 16:51:44