12

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?

4
  • 2
    Is there a reason you can't just change the function declaration to include the additional integer parameter? Commented Mar 16, 2010 at 19:42
  • @dbingham: If I am not mistaking, I would need a second "myfunction" then, which would take a function pointer with two "ints". The problem is, that there are going to be many more functions with different types and number of arguments. So that would sort of mess up my design. Commented Mar 16, 2010 at 20:10
  • I'm not sure, I understood the question correctly from the start. I think Jefromi has the bases pretty well covered at this point anyway. Commented Mar 16, 2010 at 20:17
  • yes, that's the problem. @dbingham: You are right that you can do it with va_args - and that will let you avoid making additional structs to handle the possibilities. I just think that the void* approach looks cleaner and is less prone to error. Commented Mar 16, 2010 at 20:19

1 Answer 1

15

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).

Sign up to request clarification or add additional context in comments.

12 Comments

I can't think of a clean way to do this, but the void* method described here isn't too bad. This is the way system functions like ioctl() do it. You can pass any data you need (integer, structure, NULL) through the pointer, but the function and caller both have to be careful since you won't have any type-checking to protect you.
Yep, it's scary, but that's C for you! This is also the way it's done with a bunch of GLib functions (e.g. g_hash_table_foreach).
I tried something similar earlier, but that gave me hundreds of warnings of the type: "passing argument from incompatible pointer type". Can I ignore those?
Pretty sure my now-much-longer example works fine, and you can see g_hash_table_foreach at git.gnome.org/browse/glib/tree/glib/ghash.c and usages of it all over (grel.c, gscanner.c, gcache.c) - those definitely work!
@Jefromi: Thanks a lot! This works, I was actually doing the typecasting wrong and with all the warnings and the void* pointers, it seemed a little suicidal.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.