Вопросы | c

Передача большего количества параметров в указатели на функции C

Вопрос

andrewrk | 13934 просмотров | рейтинг: 0

Допустим, я создаю шахматную программу. У меня есть функция

 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 является универсальной, и на нее ссылается множество различных мест в коде. Для каждой из этих ссылок не имеет смысла обновлять свою функцию для включения параметров, которые им не нужны. Как я могу передать дополнительные параметры функции, которую я вызываю через указатель?



Ответы

Jesse Beder

+ 8 -
Вам, вероятно, потребуется переопределить указатель на функцию, чтобы получить дополнительные аргументы.
 void foreachMove( void (*action)(chess_move*, int), chess_game* game )
 

 


Antonio Haley

+ 11 -
Ах, если бы только C поддерживал замыкания ... Антонио прав; если вам нужно передать дополнительные параметры, вам нужно переопределить указатель на функцию, чтобы принять дополнительные аргументы. Если вы точно не знаете, какие параметры вам понадобятся, у вас есть как минимум три варианта: Пусть последний аргумент в вашем прототипе будет пустым *. Это дает вам гибкость при передаче всего, что вам нужно, но это определенно не является типобезопасным. Используйте переменные параметры (...). Учитывая мой недостаток опыта работы с переменными параметрами в C, я не уверен, что вы можете использовать это с указателем на функцию, но это дает еще большую гибкость, чем первое решение, хотя все еще с отсутствием безопасности типов. Обновление до C ++ и использование функциональных объектов.  


roo

+ 3 -
Если я читаю это правильно, то я бы предложил, чтобы ваша функция взяла указатель на структуру в качестве аргумента. Тогда ваша структура может иметь игру и глубину, когда она им нужна, и просто оставить для них значение 0 или ноль, когда они вам не нужны. Что происходит в этой функции? У вас есть условие, которое говорит,
 if (depth > -1) //some default
  {
  //do something
  }
 

Функция всегда требует игры и глубины? Тогда они всегда должны быть аргументами, и это может войти в ваши прототипы. Вы указываете, что функция только иногда требует игры и глубины? Ну, может сделать две функции и использовать каждую, когда вам нужно. Но иметь структуру в качестве аргумента, вероятно, проще всего.  


Derek Park

+ 5 -
Если вы хотите использовать 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
    }
};
 

и превратите ваш foreachMove в шаблон:
 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.  


Nathan Fellman

+ 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);
}
 

Если все параметры имеют одинаковый тип, вы можете избежать массива void * и просто отправить массив с нулевым символом в конце любого нужного вам типа.  


Dale Ragan

+ 0 -
+1 к Антонио. Вам нужно изменить объявление указателя функции, чтобы принимать дополнительные параметры. Также, пожалуйста, не начинайте проходить по пустым указателям или (особенно) массивам пустых указателей. Это просто напрашивается на неприятности. Если вы начнете передавать пустые указатели, вам также придется передать какое-то сообщение, чтобы указать, какой тип указателя (или типы). Эта техника редко подходит. Если ваши параметры всегда одинаковы, просто добавьте их в аргументы указателя на функцию (или, возможно, упакуйте их в структуру и используйте в качестве аргумента, если параметров много). Если ваши параметры изменяются, тогда рассмотрите возможность использования нескольких указателей на функции для сценариев с несколькими вызовами вместо передачи пустых указателей.


Jesse Beder

+ 8 -
Если ваши параметры изменятся, я бы изменил объявление указателя функции, чтобы использовать метод ... для установки переменного числа аргументов. Это может спасти вас в удобочитаемости, а также внести изменения для каждого параметра, который вы хотите передать в функцию. Это определенно намного безопаснее, чем пропускать пустоту вокруг. http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html Просто к сведению, пример кода в ссылке: в некоторых местах они имеют «n args», а в других это «n_args» с подчеркиванием. Все они должны иметь подчеркивание. Я думал, что синтаксис выглядит немного забавно, пока не понял, что в некоторых местах они опустили подчеркивание.  


Antonio Haley

+ 11 -
Используйте typedef для указателя на функцию. Смотрите мой ответ на этот вопрос  


roo

+ 3 -
Другой вариант - изменить структуру chess_move вместо прототипа функции. Структура предположительно определена только в одном месте. Добавьте членов в структуру и заполните структуру соответствующими данными перед любым вызовом, который ее использует.


Теги

c | architecture | pointers

Похожие вопросы:

Как вы передаете функцию в качестве параметра в C?