Общие указатели на функции в C - PullRequest
12 голосов
/ 16 марта 2010

У меня есть функция, которая принимает блок данных и размер блока и указатель на функцию в качестве аргумента. Затем он перебирает данные и выполняет расчеты для каждого элемента блока данных. Ниже приводится основная схема того, что я делаю:

int myfunction(int* data, int size, int (*functionAsPointer)(int)){
    //walking through the data and calculating something
    for (int n = 0; n < size; n++){
        data[n] = (*function)(data[n]);
    }
}

Функции, которые я передаю в качестве аргументов, выглядят примерно так:

int mycalculation(int input){
    //doing some math with input
    //...
    return input;
} 

Это работает хорошо, но теперь мне нужно передать дополнительную переменную моему указателю на функцию. Нечто подобное

int mynewcalculation(int input, int someVariable){
    //e.g.
    input = input * someVariable;
    //...
    return input;
}

Есть ли элегантный способ достичь этого и в то же время сохранить мою общую дизайнерскую идею?

1 Ответ

14 голосов
/ 16 марта 2010

Обычный способ сделать его полностью универсальным - использовать пустоту *, что, конечно, вызывает все виды проблем безопасности типов:

int map_function(int* data, int size, int (*f_ptr)(int, void*), void *userdata){
    //walking through the data and calculating something
    for (int n = 0; n < size; n++){
        data[n] = (*f_ptr)(data[n], userdata);
    }
}

int simple_calculation(int input, void *userdata) {
    // ignore userdata and do some math with input
    return input;
}

int calculation_with_single_extra_arg(int input, void *userdata) {
    int input2 = * (int*) userdata;
    // do some math with input and input2
    return input;
}

int calculation_with_many_extra_args(int input, void *userdata) {
    my_data_t data = (my_data_t *) userdata;
    return input * (input * data->a + data->b) + data->c;
}

int main(int argc, char **argv) {
    int array[100];
    my_data_t userdata = { 1, 2, 3 };

    populate(array, 100);

    map_function(data, calculation_with_many_extra_args, &userdata);
}

Любая функция, которая должна быть передана, должна иметь этот прототип, но вы можете вставить любые данные, которые хотите, через userdata. Просто нет проверки типов.

Вы также можете использовать va_args, как предполагает Дбингхем; это не сильно отличается, так как прототип вашей функции для отображения должен быть int(int, va_list).

Редактировать: я предпочитаю подход void*. va_list в любом случае не добавляет никакой безопасности типов и увеличивает вероятность ошибки пользователя (в частности, дважды вызывает va_end или не реализует цикл va_arg вправо). Пустота * также не добавляет никаких дополнительных строк кода; он проходит чисто, и пользователь просто разыменовывает его в (надеюсь) правильный тип (который, вероятно, является структурой, в общем случае).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...