If I try to call a function with a parameter void (*)(char *)
with an actual void (*)(void *)
I get this error:
note: expected ‘void (*)(char *)’ but argument is of type ‘void (*)(void *)’
Isn't it allowed to cast void (*)(void *)
to void (*)(char *)
? If not, why not?
If it is safe, how can the error be suppressed without suppressing all casting errors.
Thanks!
I agree there should be 'assignment compatibility' when passing in an actual parameter of type void (*)(void *)
when void (*)(char *)
is expected.
To some this may seem strange, as void *
is the lesser specific type, so you shouldn't be allowed to assign it to a parameter or variable of type char *
. But in this case it is the other way around. That is sometimes described as 'contravariance'. It is best demonstrated with an example.
#include <stdio.h>
#include <string.h>
char globalData[10];
void PassHello(void (*func)(char *))
{
char *text = "hello";
func(text);
}
void CopyData(void *source)
{
memcpy(globalData, source, sizeof globalData);
}
void CopyHello(void)
{
PassHello(CopyData);
}
int main()
{
CopyHello();
puts(globalData);
}
There should be no problem for func(text)
to make the call to CopyData
. Even though CopyData
is declared to accept void *
as a parameter, it should also accept a more specific type, in this case char *
.
It is counter-intuitive, but true: void (*)(void *)
as a type is more specific than void (*)(char *)
. That is what contravariance is all about.
Unfortunately, the C language does not care about contravariance, allowing implicit pointer conversions for naked void *
only. Compilers could implement this as a language extension, but current major compilers don't.
Consequently, the code above gives a type error.
Note: As pointed out by other posters, the type error is justified by the fact that the C standard does not guarantee different pointer types to be compatible. C compilers can take the liberty to generate different code depending on the type of a pointer. It's theoretically possible to make this work for 'nested' types too (in this case, the type of the parameter(s) of a function pointer), but compiler complexity would probably explode. So it made sense to draw a line somewhere; C is not Haskell.
I think the cleanest solution (i.e. not involving explicit typecasts) is to make a 'wrapper' function around the void (*)(void *)
function pointer. For example:
void CopyCharData(char *source)
{
CopyData(source);
}
And then pass the wrapper instead:
PassHello(CopyCharData);
This will compile and run just fine.
Live demo: https://repl.it/repls/ImmediateWindingOrders