2

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?

9
  • 2
    What are you actually trying to accomplish? I have never seen a need to cast between function pointer types. It's generally unsafe to do so. Commented Jun 11, 2015 at 15:40
  • You wrote I need to write a function that converts draw_func1_t to draw_func2_t but your example makes the opposite conversion. What's actually required? Commented Jun 11, 2015 at 15:45
  • 1
    The most portable option is to use only draw_func1_t for both functions. You can still legally pass a MyPainter * into the void * argument, you just won't get as much type checking as you might like. Maybe you can add a magic number in the MyPainter structure and check that at runtime. Commented Jun 11, 2015 at 15:50
  • stackoverflow.com/questions/3520059/… Commented Jun 11, 2015 at 15:52
  • 1
    @WernerHenze He's asking about casting one function pointer to another (which will be the same size). I'm not sure what your point is in linking to that question. Commented Jun 11, 2015 at 15:53

4 Answers 4

5

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.

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

6 Comments

The cast is valid. Using the cast pointer is not. That is, you can safely cast a pointer to a function to a pointer to a different function type; if you then cast it back to the original type, you will preserve the value. That's the equivalent of round-trip casting a pointer to an object type to void* and back. But you cannot call the function using the cast pointer, as in your quote.
"then the behaviour on its calling is undefined" is not correct. It is perfectly defined, namely, it will do as I tell it (that is why we have casts). Only when the calling conventions of these functions are different, will the behaviour be undefined.
If a void *painter is actually an abstraction hiding of the MyPainter data structure, then the two are internally compatible. No C langage construct forbids the cast.
Annex J is non-normative, although there is normative text in the main body of the standard that covers it.
@Paul Ogilvie: As it has been stated above, the cast itself is not forbidden. But using the result of the cast to perform a function call leads to undefined behavior. C language states that quite explicitly. The function types in this case are not compatible, as this answer correctly states.
|
1

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

6 Comments

This is elegant however it requires func2 to exist. Unfortunately func2 is supplied by the user of the library and I have to use function pointer.
@SherwoodHu, That's OK. You need to create func2_wrapper, not func2.
@RSahu: The OP said "func2 is supplied by the user of the library", which I take to mean func2 is not known to your code at compile time.
@newacct, thanks for the clarification. For some reason, I misread that.
This would be better without the redundant cast. void * can be implicitly converted to other object pointer types. In fact you could go func2(data, painter, x, y); and get rid of realPainter entirely.
|
0

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.

Comments

0

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

Comments

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.