C function pointer cast to another function pointer

197 views Asked by At

I need help in function pointers.

I have two function pointer types:

typedef void (*draw_func1_t)(void* data, void* painter, double x, double y);
typedef void (*draw_func2_t)(void* data, MyPainter* painter, double x, double y);

The two types are almost the same, except the second parameter. Now I need to write a function that convert a draw_func1_t to draw_func2_t:

draw_func2_t convert_func_p(draw_func1_t func) { ... }

How can I write it? Can I just force a cast like

return (draw_func2_t)func;

because the two function prototypes are binary compatible?

4

There are 4 answers

0
newacct On

Theoretically, if you cast it to a different signature and call it, it is undefined behavior, as Bathsheba's answer cites, because the calling convention for calling different types of functions can be different.

However, practically, it will work on pretty much any real-world system, because pretty much all calling conventions treat different types of (non-function) pointers identically. And since that's the only difference (everything else, including the number of parameters and return type are the same), the calling conventions will almost certainly be the same. You can check the function-calling ABI for your specific system to make sure.

0
Paul Ogilvie On

The following compiles without warnings (VC2008) and shows that the two function types are compatible. Unexpectedly, a void * is accepted where a MyPainter * is requied.

typedef struct {
    int x;
    int y;
} MyPainter;

typedef void (*draw_func1_t)(void* data, void* painter, double x, double y);
typedef void (*draw_func2_t)(void* data, MyPainter* painter, double x, double y);

void f1(void* data, void* painter, double x, double y);
void f2(void* data, MyPainter* painter, double x, double y);

void f1(void* data, void* painter, double x, double y)
{
    f2(data,painter,x,y);   // no compiler warning is unexpected
}
void f2(void* data, MyPainter* painter, double x, double y)
{
    f1(data,painter,x,y);   // no compiler warning is expected
}
void pTest(void)
{
    MyPainter p = {0,0};
    draw_func1_t pf1;
    draw_func2_t pf2;

    pf1= f1;
    pf2= f1;

    pf1= f2;
    pf2= f2;

    pf1(0,&p,0.0,0.0);
    pf2(0,&p,0.0,0.0);
}
6
Bathsheba On

If you cast a function pointer to a different type, then the behaviour on its calling is undefined. See Annex J.2 of the C standard:

The behaviour is undefined in the following circumstances: A pointer is used to call a function whose type is not compatible with the pointed-to type (6.3.2.3).

Compatibility is dealt with in 6.7.5.1, paragraph 2:

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

A MyPainter* is not compatible with a void*. So your function pointer cast cannot be used to call the function.

6
R Sahu On

Since use of:

draw_func1_t convert_func_p(draw_func2_t func)
{
   return (draw_func1_t)func;
}

leads to undefined behavior, you might want to change your strategy.

Say you have:

void func2(void* data, MyPainter* painter, double x, double y)
{
   printf("In func2, working with MyPainter\n");
}

and you would like to be able use that function indirectly through a function pointer.

One option is to use a wrapper function.

void func2_wrapper(void* data, void* painter, double x, double y)
{
   // In this function, if you are sure that painter points to
   // a valid MyPainter object, you can do this:
   MyPainter* realPainter = (MyPainter*)painter;

   // Then call the core function.
   func2(data, realPainter, x, y);
}

Register func2_wrapper as a callback.

You can also make func2_wrapper simpler by removing the explicit cast to MyPainter*.

void func2_wrapper(void* data, void* painter, double x, double y)
{
   func2(data, painter, x, y);
}