Your example can be boiled down to
typedef void * (*VF)(void *);
typedef int * (*IF)(int *);
IF a = 0;
VF b = a; //Warning
In the C standard pointers to functions are less versatile than pointer to objects.
You can convert a pointer to an object to a pointer to void
(and back) without any cast (and warnings) because there is clause in the standard that explicitly allows for it
A pointer to void may be converted to or from a pointer to any object type.
A pointer to
any object type may be converted to a pointer to void and back again;
the result shall
compare equal to the original pointer.
Note here that a function is not an object.
Regarding pointers to functions the only thing that the standard guarantees is:
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer.
If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined
Later the standard clarifies what does it mean for two functions to be compatible:
For two function types to be compatible, both shall specify compatible return types.
Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types.
Now you may think that void*
and struct location_t*
are compatible types, after all you can assign each others.
But the standard is crystal clear:
Two types have compatible type if their types are the same.
It later goes on with extending this relationship for more complex types and qualified types.
It may sound surprising but int*
and void*
are not compatible.
They can be assigned but are not compatible, after all the void*
could point to a float
or an object with different alignment requirements.
When it comes to pointers of different types the standard is concerned mostly about assigning them back and forth.
It doesn't forbid converting between incompatible pointer types, but using them is undefined behavior, hence the warning.
The better approach is to perform the cast inside the function, as suggested in this answer.
All your function should have signature void* (void*)
so that no cast between pointers to functions is needed.
SetElement locationCopy(SetElement element)
{
Location location = (Location)element;
Location new_location = locationCreate(location->name);
return new_location;
}
The casts inside the functions are again subject to undefined behavior if the SetElement
pointers are cast to incompatible pointers but that shouldn't happen if you'll call locationCopy
only with pointers to Location
s (as you indeed expect to do).
As a side note, some programmer may frown upon using a typedef
to hide a pointer type.