2008-08-14 23 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结构而不是函数原型。该结构大概只在一个地方定义。将成员添加到结构中,并在使用它的任何调用之前使用适当的数据填充结构。