Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting function pointers with different pointer types as an argument

The following code, I think, describes what I am trying to do. Specifically, I wish to cast a function pointer to a generic function type, with the only difference in signature being different pointer types.

Now, I'm aware that there is a requirement for function pointers to be compatible as discussed in this question, but I'm not sure whether having an argument of different pointer type satisfies that compatibility requirement.

The code compiles and runs, but, as expected, gives warnings about the assignment from incompatible pointer type. Is there some way to satisfy the compiler and achieve what I am after?

#include <stdio.h>

int float_function(float *array, int length)
{
    int i;
    for(i=0; i<length; i++){
        printf("%f\n", array[i]);
    }
}

int double_function(double *array, int length)
{
    int i;
    for(i=0; i<length; i++){
        printf("%f\n", array[i]);
    }
}


int main() 
{
    float a[5] = {0.0, 1.0, 2.0, 3.0, 4.0};    
    double b[5] = {0.0, 1.0, 2.0, 3.0, 4.0};

    int (*generic_function)(void*, int) = NULL;

    generic_function = &float_function;
    generic_function(a, 5);

    generic_function = &double_function;
    generic_function(b, 5);

    return 0;
}
like image 617
Henry Gomersall Avatar asked Jan 28 '12 13:01

Henry Gomersall


People also ask

How can a function pointer be an argument?

Pass-by-pointer means to pass a pointer argument in the calling function to the corresponding formal parameter of the called function. The called function can modify the value of the variable to which the pointer argument points. When you use pass-by-pointer, a copy of the pointer is passed to the function.

Can you cast function pointers?

Yes, it can. This is purpose of casting function pointers, just like usual pointers. We can cast a function pointer to another function pointer type but cannot call a function using casted pointer if the function pointer is not compatible with the function to be called.

How can we pass a pointer as an argument to a function explain with example?

Example 2: Passing Pointers to Functions Here, the value stored at p , *p , is 10 initially. We then passed the pointer p to the addOne() function. The ptr pointer gets this address in the addOne() function. Inside the function, we increased the value stored at ptr by 1 using (*ptr)++; .

Why do we need to use a pointer to pointer as an argument of a function?

You pass a pointer to pointer as argument when you want the function to set the value of the pointer. You typically do this when the function wants to allocate memory (via malloc or new) and set that value in the argument--then it will be the responsibility of the caller to free it.


3 Answers

The cleanest way is IMHO to perform the cast inside the function. This will force all the function signatures to be the same, and keeps the casts out of the caller's code. (this is for instance the way that qsort() wants it)

int double_function(void *p, unsigned size)
{
    double *array = p    
    unsigned uu;

    for(uu=0; uu < size; uu++){
        printf("%f\n", array[uu]);
    }
return 42;
}
like image 100
wildplasser Avatar answered Nov 14 '22 20:11

wildplasser


Yes, declare it without a prototype.

int (*generic_function)() = NULL;

Now you can assign any function that returns int, but also can pass any argument and the compiler is not required to reject incompatible arguments.

like image 22
Johannes Schaub - litb Avatar answered Nov 14 '22 21:11

Johannes Schaub - litb


EDIT: As @Mat points out, to follow the C specification, you'd need to cast the function pointer back to the original type before calling it, which would make this whole excercise quite a bit less useful;

((int(*)(float*,int))generic_function)(a, 5);

Another solution (inspired by the answer from @wildplasser) is to wrap the functions in functions taking void* and performing a cast on that parameter instead. Doing it "by macro" is fairly simple;

#define WRAP(FN, TYPE) int FN##_wrapped(void* p, int len) { return FN((TYPE*)p, len); }
WRAP(float_function, float)
WRAP(double_function, double)

Then you can use the following rather clean lines instead;

generic_function = float_function_wrapped;
generic_function(a, 5);

That said, pointer casting isn't generally a solution I'd advocate, but it has its use cases.

like image 39
Joachim Isaksson Avatar answered Nov 14 '22 21:11

Joachim Isaksson