2008-08-14 21 views
9

假設我正在創建一個國際象棋程序。我有一個功能在C函數指針中傳遞更多參數

void foreachMove(void (*action)(chess_move*), chess_game* game); 

這將調用每個有效的移動函數指針操作。這一切都很好,但如果我需要將更多參數傳遞給動作函數呢?例如:

chess_move getNextMove(chess_game* game, int depth){ 
    //for each valid move, determine how good the move is 
    foreachMove(moveHandler, game); 
} 

void moveHandler(chess_move* move){ 
    //uh oh, now I need the variables "game" and "depth" from the above function 
} 

重新定義函數指針並不是最佳解決方案。 foreachMove函數是多功能的,代碼中的許多不同的地方引用它。這些引用中的每一個必須更新它們的函數以包含它們不需要的參數是沒有意義的。

如何將額外的參數傳遞給我通過指針調用的函數?

回答

8

您可能需要重新定義函數指針以獲取其他參數。

void foreachMove(void (*action)(chess_move*, int), chess_game* game) 
11

啊,如果是僅含有C支持關閉...

安東尼奧是正確的;如果您需要傳遞額外參數,則需要重新定義函數指針以接受其他參數。如果你不知道你需要什麼參數,那麼你至少有三個選擇:

  1. 你的原型中的最後一個參數是void *。這給你傳遞任何你需要的靈活性,但它絕對不是類型安全的。
  2. 使用可變參數(...)。鑑於我在C中的可變參數缺乏經驗,我不確定您是否可以將它用於函數指針,但這比第一種解決方案具有更大的靈活性,儘管仍然缺乏類型安全性。
  3. 升級到C++並使用function objects
3

如果我正在閱讀這個權利,我建議的是讓你的函數將一個指向struct的指針作爲參數。然後,當你的結構需要它們時,你的結構可以有「遊戲」和「深度」,當你不需要它們時,將它們設置爲0或Null。

該功能正在發生什麼?你有條件說,

if (depth > -1) //some default 
    { 
    //do something 
    } 

函數總是需要「遊戲」和「深度」?然後,他們應該始終是爭論,這可以進入你的原型。

您是否指示該功能僅有時需要「遊戲」和「深度」?那麼,也許有兩個功能,並在需要時使用每一個功能。

但是,以結構作爲參數可能是最簡單的事情。

6

如果你願意使用一些C++,你可以使用一個「函數對象」:

struct MoveHandler { 
    chess_game *game; 
    int depth; 

    MoveHandler(chess_game *g, int d): game(g), depth(d) {} 

    void operator() (chess_move*) { 
     // now you can use the game and the depth 
    } 
}; 

,將您​​成模板:

template <typename T> 
void foreachMove(T action, chess_game* game); 

,你可以叫它像這樣:

chess_move getNextMove(chess_game* game, int depth){ 
    //for each valid move, determine how good the move is 
    foreachMove(MoveHandler(game, depth), game); 
} 

但它不會中斷您的其他用途MoveHandler

1

我建議使用void *數組,最後一個條目總是void。 說你需要3個參數,你可以這樣做:

void MoveHandler (void** DataArray) 
{ 
    // data1 is always chess_move 
    chess_move data1 = DataArray[0]? (*(chess_move*)DataArray[0]) : NULL; 
    // data2 is always float 
    float data1 = DataArray[1]? (*(float*)DataArray[1]) : NULL; 
    // data3 is always char 
    char data1 = DataArray[2]? (*(char*)DataArray[2]) : NULL; 
    //etc 
} 

void foreachMove(void (*action)(void**), chess_game* game); 

然後

chess_move getNextMove(chess_game* game, int depth){ 
    //for each valid move, determine how good the move is 
    void* data[4]; 
    data[0] = &chess_move; 
    float f1; 
    char c1; 
    data[1] = &f1; 
    data[2] = &c1; 
    data[3] = NULL; 
    foreachMove(moveHandler, game); 
} 

如果所有的參數都是同一類型,那麼你可避免無效*數組,只是發送一個NULL結尾無論你需要什麼類型的數組。

0

+1給Antonio。您需要更改函數指針聲明以接受其他參數。

而且,請不要開始傳遞無效指針或空指針的(尤其是)陣列。這只是要求麻煩。如果你開始傳遞void指針,你還必須傳遞某種消息來指示指針類型(或類型)是什麼。這種技巧很適合很少

如果你的參數都是一樣的,只是將它們添加到你的函數指針參數(或可能它們打包成一個結構,並用其作爲論據,如果有很多的參數)。如果你的參數改變了,那麼考慮爲多個調用場景使用多個函數指針,而不是傳遞void指針。

0

如果你的參數發生變化,我會改變函數指針聲明使用「...」技術來建立一個可變數量的參數。它可以爲您節省可讀性,並且還需要對要傳遞給函數的每個參數進行更改。它比傳遞虛空肯定更安全。

http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html

只是一個供參考,有關鏈接的示例代碼:一些地方,他們有「N ARGS」等是「n_args」用下劃線。他們應該都有下劃線。我認爲語法看起來有點滑稽,直到我意識到他們在某些地方放下了下劃線。

0

另一種選擇是修改chess_move結構而不是函數原型。該結構大概只在一個地方定義。將成員添加到結構中,並在使用它的任何調用之前使用適當的數據填充結構。