I have a function which takes a block of data and the size of the block and a function pointer as argument. Then it iterates over the data and performes a calculation on each element of the data block. The following is the essential outline of what I am doing:
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]);
}
}
The functions I am passing as arguments look something like this:
int mycalculation(int input){
//doing some math with input
//...
return input;
}
This is working well, but now I need to pass an additional variable to my functionpointer. Something along the lines
int mynewcalculation(int input, int someVariable){
//e.g.
input = input * someVariable;
//...
return input;
}
Is there an elegant way to achieve this and at the same time keeping my overall design idea?
The usual way to make it totally generic is with a void*, which of course causes all kinds of typesafety issues:
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);
}
Any given function to be passed in must have that prototype, but you can shove any data you want in through userdata
. There's just absolutely no typechecking.
You could also use va_args as dbingham suggests; this isn't really much different though, since the prototype for your function to map will have to be int(int, va_list)
.
Edit: I favor the void*
approach. The va_list
doesn't add any typesafety anyway and adds more potential for user error (particularly calling va_end
twice or not implementing the va_arg
loop right). The void* also doesn't add any extra lines of code; it's passed cleanly through, and the user just dereferences it into (hopefully) the right type (which is probably a struct, in the general case).