No. C does not allow you to do this directly.
In C, the standard method for handling callbacks is the context pointer:
void register_callback(void (*cback)(void *context, int data), void *context);
this means that you will pass a function that will accept void * in addition to the normal parameters that the callback should handle (in the above case, an integer), and you will also pass the void * that you want to go back.
This void * usually points to a struct that will contain all the additional parameters or data that you need in the callback, and using this approach, the library does not depend on what this context is. If the callback does not need any context, you simply pass the NULL pointer as context and ignore the first parameter when calling from the library.
Something like hacking and formally unsafe, but sometimes it is done if the context is simple data that matches the size of void * (for example, an integer), and if your environment does not have problems, you can trick the library by passing a fake void * , which is just an integer, and you convert it back to an integer when called from the library (this saves the caller from highlighting the context and managing its lifetime).
On how to trick the language to avoid this restriction (while still staying in the portable C country), I might think of some hacks:
First, we allocate a pool of two callbacks arguments and contextual data.
void (*cbf[6])(int, int); int ctx[6];
then we write down (or macro-generating) the functions that we want to register, and which will invoke versions with two arguments.
void call_with_0(int x) { cbf[0](ctx[0], x); } void call_with_1(int x) { cbf[1](ctx[1], x); } void call_with_2(int x) { cbf[2](ctx[2], x); } void call_with_3(int x) { cbf[3](ctx[3], x); } void call_with_4(int x) { cbf[4](ctx[4], x); } void call_with_5(int x) { cbf[5](ctx[5], x); }
We also store them in the pool, where they are allocated and freed:
int first_free_cback = 0; int next_free_cback[6] = {1, 2, 3, 4, 5, -1}; void (*cbacks[6])(int) = { call_with_0, call_with_1, call_with_2, call_with_3, call_with_4, call_with_5 };
Then, to bind the first parameter, we can do something like
void (*bind(void (*g)(int, int), int v0))(int) { if (first_free_cback == -1) return NULL; int i = first_free_cback; first_free_cback = next_free_cback[i]; cbf[i] = g; ctx[i] = v0; return cbacks[i]; }
but related functions must also be explicitly freed
int deallocate_bound_cback(void (*f)(int)) { for (int i=0; i<6; i++) { if (f == cbacks[i]) { next_free_cback[i] = first_free_cback; first_free_cback = i; return 1; } } return 0; }