7

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;
}

3 Answers 3

5

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;
}
Sign up to request clarification or add additional context in comments.

3 Comments

Indeed, I had thought about that. The problem being that I'm calling a library (which may have a whole stack of problems in itself!). Consequently, I'd need to wrap each function I'm calling separately, which was sort of what I was trying to avoid (though it's still keeps the inner logic simple).
I had not thought of the library-function case. It will be hard to keep both the compiler and the human reader happy. Maybe wrapper functions (or ugly preprocessor-hacks ;-) are the right way.
I agree a wrapper function is the cleanest method. A bit more code, but it has a nice logical separation. It also allows me to change the signature and include code in the wrapper.
2

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.

4 Comments

But the downside of this will be that the compiler won't complain if you try to use a pointer to strcmp() or fprintf() as function pointer.
@wildplasser that's just the same problem as you get when you use void* as the parameter type. You can still pass a int* when it in the body wants a double* and such.
Yes, that is correct. But the compiler at least could check if one pointer and one int are passed as arguments.
Isn't this undefined as per the spec?
1

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.

6 Comments

That just shuts up the compiler (hides the problem under the rug). Not good advice IMO.
Is there an inherent problem with the technique that the compiler shouldn't be shut up?
Yes, you're casting between incompatible function pointer types and calling the cast function without casting it back to its real type. That's undefined behavior according to the standard.
@Mat Updated the answer, pondered removing it but the info you gave was quite useful (and new to at least me)
This is also a nice solution. I have other reasons for not wanting to use macros, but in another situation this would be great.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.