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;
}
Community
  • 1
  • 1
Henry Gomersall
  • 8,434
  • 3
  • 31
  • 54

3 Answers3

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;
}
wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • 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). – Henry Gomersall Jan 28 '12 at 13:32
  • 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. – wildplasser Jan 28 '12 at 13:39
  • 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. – Henry Gomersall Jan 28 '12 at 16:58
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.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
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.

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • 1
    That just shuts up the compiler (hides the problem under the rug). Not good advice IMO. – Mat Jan 28 '12 at 13:28
  • Is there an inherent problem with the technique that the compiler shouldn't be shut up? – Henry Gomersall Jan 28 '12 at 13:29
  • 1
    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 Jan 28 '12 at 13:31
  • @Mat Updated the answer, pondered removing it but the info you gave was quite useful (and new to at least me) – Joachim Isaksson Jan 28 '12 at 13:50
  • This is also a nice solution. I have other reasons for not wanting to use macros, but in another situation this would be great. – Henry Gomersall Jan 29 '12 at 22:46
  • Where does it say this in the spec? Because from what I read it sounded like you can cast and use function pointers with arguments of compatible types. – Matthew Mitchell Oct 27 '13 at 18:36