From time to time, I use and/or design callback interfaces in C. Usually, the signature of the callback will be similar to
typedef int (*callback)(int some_argument, void* user_data);
and the function that calls the callback is
int do_something_with_callback(int another_argument, callback f, void* user_data);
This is all well and good (as long as the documentation for do_something_with_callback specifies exactly how if will call f). But consider the following example
#include <stdio.h>
int add(int a, void* user_data)
{
int* b=(int*)user_data;
return a + (*b);
}
typedef int (*callback)(int some_argument, void* user_data);
int apply_arithmetic_function(int initial_value, callback f, void* user_data)
{
return f(initial_value, user_data);
}
int main()
{
int v=1000;
printf("%d\n", apply_arithmetic_function(123, add, &v));
return 0;
}
This works, but as you can see, the add function does not write though its user_data pointer. Therefore, in isolation, it could be marked const, which is ususally a good thing. But is int add(int a, const void* user_data) compatible with the callback interface?
At first glance, it would seem like it, since something that takes a const pointer can be passed a non-const pointer (and gcc 4.3.4 with -Wall -Wextra -Werror -std=c99 -pedantic permitts it). But then again: what if the calling convention is different for const and non-const pointers? In a plain function call, it will work, since the compiler sees the signature of the actual function that is being called, but though the function pointer, it does not.
Looking at the (draft) spec, it is not permitted.
6.7.5.3§15 says:
For two function types to be compatible, both shall specify compatible return types. [127]
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.
127) If both function types are ‘‘old style’’, parameter types are not compared.
So, all the parameters in the signatures have to be "compatible". What does that mean? 6.7.3§9 says:
For two qualified types to be compatible, both shall have the identically qualified version of a compatible type ...
Ergo, the function signatures are not compatible, and 6.5.2.2§9 says:
If the function [that is to be called] is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
Conclusion: be careful with the types of callback functions. Copy-paste verbatim from the declaration, and write a comment at the site of the callback implementations saying that they are supposed to conform to a specific interface and that the parameter declarations may not be modified unless the interface is also changed.