2009-11-12 202 views
3

我想知道是否有任何方法將參數動態地傳遞給可變參數函數。即如果我有一個函數動態地將參數傳遞給可變參數函數

int some_function (int a, int b, ...){/*blah*/} 

,我接受了一堆數值從用戶,我想這些值傳遞到函數的一些方法:

some_function (a,b, val1,val2,...,valn) 

我不想寫所有這些功能的不同版本,但我懷疑沒有其他選擇?

+1

如果所有值均爲同一類型(如你的問題似乎暗示 - 糾正我,如果我錯了)我建議不要使用可變參數函數,而是傳遞一個數組。看到這裏的一些宏魔法漂亮起來,當你想傳遞固定數量的參數:http://stackoverflow.com/questions/1375474/variable-arity-in-c/1375636#1375636 – Christoph 2009-11-12 11:59:38

+0

不,他們是不同的類型。 – tommobh 2009-11-12 12:01:32

+0

@tommobh:如果你將值包裝在union中,或者將'void *'數組傳遞給值而不是值本身,但數組方法仍然可以工作 – Christoph 2009-11-12 12:04:10

回答

1

嘗試傳遞一個數組可能會很有趣,然後使用可變參數宏。根據堆棧對齊情況,它可能正常工作(tm)。

這可能不是最佳解決方案,我主要發佈它,因爲我發現這個想法很有趣。 試用後,這種方法在我的Linux x86上工作,但不在x86-64上 - 它可能可以改進。此方法將取決於堆棧對齊,結構對齊和可能更多。

void varprint(int count, ...) 
{ 
    va_list ap; 
    int32_t i; 

    va_start(ap, count); 
    while(count--) { 
     i = va_arg(ap, int32_t); 
     printf("Argument: %d\n", i); 
    } 
    va_end(ap); 
} 

struct intstack 
{ 
    int32_t pos[99]; 
}; 

int main(int argc, char** argv) 
{ 
    struct intstack *args = malloc(sizeof(struct intstack)); 
    args->pos[0] = 1; 
    args->pos[1] = 2; 
    args->pos[2] = 3; 
    args->pos[3] = 4; 
    args->pos[4] = 5; 

    varprint(5, *args); 
    return 0; 
} 
+0

它不會工作,因爲數組沒有通過值傳遞,但作爲指針 – Christoph 2009-11-12 11:55:45

+0

正確 - 當我想出來的時候,我想不知何故推動堆棧上的數組。但是如果你只是將數組傳遞給一個函數,C就不會這麼做 - 你必須自己去做。 – gnud 2009-11-12 11:58:06

+0

當使用一個結構來包裝數組時,它有可能工作,至少:)參見示例 – gnud 2009-11-12 13:16:40

8

變量函數使用調用約定,其中調用者負責從棧中彈出函數參數,所以是的,可以動態執行此操作。它在C中沒有標準化,通常需要一些程序集手動推送所需的參數,並正確調用可變參數函數。

cdecl調用約定要求參數按正確的順序推送,並且在調用之後,在調用之前作爲參數推送的字節被彈出。通過這種方式,被調用的函數可以接收任意數量的參數,因爲調用者將處理將堆棧指針恢復到它的預調用狀態。 ...之前的參數佔用的空間是推送的字節數的安全下限。額外的可變參數在運行時被解釋。

FFCALL是一個庫,它提供了將參數動態傳遞給可變參數函數的包裝器。您感興趣的功能組是avcall。這裏有一個例子給你打電話了以上的功能:

#include <avcall.h> 

av_alist argList; 
int retVal; 
av_start_int(argList, some_function, retval); 
av_int(argList, a); 
av_int(argList, b); 
av_type(argList, val1); 
... 
av_type(argList, valn); 
av_call(argList); 

您也可能會發現this link討論用C產生繞可變參數函數的包裝,對感興趣的理由,爲什麼這不是標準C.

+0

@Anacrolix你是什麼意思「並適當調用可變參數調用」?該函數處理可變參數。 – tommobh 2009-11-12 12:28:00

+0

@tommobh:我編輯解釋 – 2009-11-12 12:33:01

+0

@Anacrolix非常感謝,非常感謝。 – tommobh 2009-11-12 12:55:15

2
部分

一個標準的方法是讓每個可變參數函數伴隨着一個va_list評論對象(如在printf和vprintf中)。該可變參數版本只是將...轉換爲va_list(使用來自stdarg.h的宏),並調用其實際工作的va_list-sister。

+0

謝謝,我會研究這種方法。 – tommobh 2009-11-12 12:06:26

+0

@atzz雖然同樣的問題。我仍然需要動態地將參數添加到該函數,無論它是否調用其va_list-姐妹。你的意思是我將這些值轉換爲va_list,然後使用va_list-taking姐妹函數來代替? – tommobh 2009-11-12 12:54:45

+0

@tommobh - 我想我錯誤地解釋了你的問題。通過閱讀評論,我認爲你有一種獲得不同類型值的方法(例如,來自gui),並且需要將它們傳遞給一個函數;這是對的嗎?在這種情況下,va_list不會有太大的幫助。如果你不想依賴黑客,你將不得不改變功能... – atzz 2009-11-12 16:10:37

0

取決於你傳遞的是什麼,它可能是你在此之後的一個有區別的聯盟(正如評論中暗示的那樣)。這將避免需要可變參數函數或void*的數組,並回答「問題如何some_function知道你實際上通過它」。你可能有代碼是這樣的:

enum thing_code { INTEGER, DOUBLE, LONG }; 

struct thing 
{ 
enum thing_code code; 
union 
{ 
    int a; 
    double b; 
    long c; 
}; 
}; 

void some_function(size_t n_things, struct thing *things) 
{ 
    /* ... for each thing ... */ 
    switch(things[i].code) 
    { 
     case INTEGER: 
     /* ... */ 
    } 
} 

你可以藉此更進一步,通過與一個或多個指針替換code該做些什麼,每個thing有用的功能,避免了開關。例如,如果你想要做的是簡單地打印出每一件事情,你可以有這樣的:

struct thing 
{ 
void (*print)(struct thing*); 
union 
{ 
    ... 
}; 
} 

void some_function(size_t n_things, struct thing *things) 
{ 
    /* .. for each thing .. */ 
    things[i]->print(things[i]); 
    /* ... */ 
} 
+0

@Ned:我正在使用的可變參數函數是在一個庫中,我最好不喜歡改變(即我不想要改變'some_function()')。用戶可以輸入他們喜歡的任何類型的數量(在合理範圍內),因此定義嚴格的聯合似乎不會起作用。一系列void *或Anacrolix的建議似乎是我最好的選擇。無論是我還是我寫一些內聯彙編:S。 – tommobh 2009-11-12 13:52:15

+0

但是,如果你不能改變some_function,你怎麼能通過它一個void *數組?用戶在這裏,我們是在討論一個使用你的代碼的開發者,或者是該程序的最終用戶? – Ned 2009-11-12 13:56:34

+0

@Ned:User =最終用戶。是的,我明白你的意思了。我不想改變函數,因爲函數本身調用另一個可變參數函數,我也必須改變它。它只會雪球。我感覺我回到了我開始的地方。 – tommobh 2009-11-12 14:14:16