2008-08-14 5 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. Имейте последний аргумент в своем прототипе - это пустота *. Это дает вам гибкость в передаче всего, что вам нужно, но это определенно не безопасно для типов.
  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 
    } 
}; 

и превратить ваш 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.

1

Я бы предложил использовать массив 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 Антонио. Вам нужно изменить объявление указателя на функцию, чтобы принять дополнительные параметры.

Кроме того, не начинайте прокручивать указатели void или (особенно) массивы указателей пустоты. Это просто просит неприятностей. Если вы начнете передавать указатели void, вам также придется передать какое-то сообщение, чтобы указать, какой тип указателя (или типы). Этот метод редко подходит.

Если ваши параметры всегда одинаковые, просто добавьте их в аргументы указателя на функцию (или, возможно, упакуйте их в структуру и используйте в качестве аргумента, если есть много параметров). Если ваши параметры изменяются, рассмотрите возможность использования нескольких указателей функций для сценариев множественных вызовов вместо передачи указателей void.

0

Если ваши параметры изменяются, я бы изменил объявление указателя функции, чтобы использовать метод «...» для установки переменного количества аргументов. Он может сэкономить вам удобочитаемость, а также внести изменения для каждого параметра, который вы хотите передать функции. Это определенно намного безопаснее, чем прохождение вокруг.

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

Просто FYI, о примере кода в ссылке: некоторые места, которые они имеют «N арг» и другие это «n_args» с подчеркиванием. Все они должны иметь знак подчеркивания. Я думал, что синтаксис выглядел немного смешно, пока я не понял, что они сбросили подчеркивание в некоторых местах.

0

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

Смежные вопросы