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?

cuteCAT
  • 2,251
  • 4
  • 25
  • 30
  • 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. – Jonathon Reinhart Jun 11 '15 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? – dlask Jun 11 '15 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. – Lee Daniel Crocker Jun 11 '15 at 15:50
  • http://stackoverflow.com/questions/3520059/does-the-size-of-pointers-vary-in-c – Werner Henze Jun 11 '15 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. – Jonathon Reinhart Jun 11 '15 at 15:53
  • @JonathonReinhart A draw_func1_t function requires a two void* and two doubles on the stack. A draw_func2_t requires a void*, a MyPainter* and two doubles on the stack. If sizeof(void*)!=sizeof(MyPainter*) then you cannot easily cast between draw_func1_t and draw_func2_t and expect it works. – Werner Henze Jun 11 '15 at 15:57
  • Okay I suppose `MyPainter` could itself be a function pointer, but nothing thus far indicated that. Either way, [Bathsheba's answer](http://stackoverflow.com/a/30785523/119527) explains why what the OP is trying to do is wrong. – Jonathon Reinhart Jun 11 '15 at 16:03
  • MyPainter is a C structure, so sizeof(void*)==sizeof(MyPainter*), at least for my question. I am asking if there is better way instead of a raw cast. I can not just use one function prototype throughout, because it is a callback function to our DLL. The user who writes the function has a different view of MyPainter structure. – cuteCAT Jun 11 '15 at 16:15
  • @SherwoodHu `sizeof(void*)` is not guaranteed to equal `sizeof(MyPainter *)`, although it does on modern systems – M.M Jun 11 '15 at 23:22

4 Answers4

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.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 1
    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. – rici Jun 11 '15 at 16:01
  • "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. – Paul Ogilvie Jun 11 '15 at 16:32
  • 1
    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. – Paul Ogilvie Jun 11 '15 at 16:38
  • Annex J is non-normative, although there is normative text in the main body of the standard that covers it. – M.M Jun 11 '15 at 23:19
  • @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. – AnT stands with Russia Jun 12 '15 at 00:22
  • @An, the undefined behaviour is that the programmer must be sure the call and parameters will be correct for the call he/she is performing. It simply means that C does not dictate that an implementation would do some kind of conversion or peform certain checks; it simply means the standard does not define a behaviour (actually it does, namely "perform teh call as is"). It is not so that diferent C implementations could yield yield different results, as would be with a `i= ++i - i--`. Further, _any_ pointer is compatible with `void *` and a a `myPainter *` can be given for a `void *'. – Paul Ogilvie Jun 12 '15 at 10:39
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);
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 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. – cuteCAT Jun 11 '15 at 16:18
  • @SherwoodHu, That's OK. You need to create `func2_wrapper`, not `func2`. – R Sahu Jun 11 '15 at 16:20
  • @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 Jun 11 '15 at 22:10
  • @newacct, thanks for the clarification. For some reason, I misread that. – R Sahu Jun 11 '15 at 22:11
  • @SherwoodHu, now that I understand your comment, would you add more information to your post that explains the flow of execution a little better? – R Sahu Jun 11 '15 at 22:14
  • 1
    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. – M.M Jun 11 '15 at 23:20
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.

newacct
  • 119,665
  • 29
  • 163
  • 224
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);
}
Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41