In my C program, I need to pass a callback function to a 3rd party library. The library calls this callback with a few arguments. However, I need to expose one more variable to this callback. This variable is accessible in the scope of the function from within I'm setting the callback.
In JS, I'd easily solve this with .bind()
, more or less like this (pseudocode):
func my_callback(int a, int b) {
printf("A: %d\n", a);
printf("B: %d\n", b);
}
func new_instance() {
int a = 1;
// setup_callback expects (void(*)(int b)) as an argument
setup_callback(my_callback.bind(null, a));
}
There are two main restrictions:
- Variable
a
cannot be global - it is declared and initialized inside thenew_instance
procedure. - It has to be a C solution (ideally ANSI), not C .
After playing with it (function pointers) for a while, I don't seem to be any closer to a workable solution...
CodePudding user response:
You are asking for a closure, and closures are not a feature of the C language. Since you're not in control of the callback-using API, there is no workaround to allow you to get an additional local variable through to the callback.
If you were in control of the API, then it would be possible to make the callback interface accept an additional parameter along with the function, to be stored alongside it and be passed to it as an argument when the function is called. Typically a parameter of type void *
is used for such a purpose, as you can convey data of any type that way.
Even then, however, you need to be careful. You could provide the value of a local variable that way, but if you provided a pointer to a local variable then that would only be usable during the lifetime of that variable, which is not longer than the execution of the function containing it (and would be shorter under some circumstances).
CodePudding user response:
C does not support that, period. What can be done instead is to store the "bound" variable in a scope accessible to the callback and expose it to the code that would create the closure were it's available.
It has the drawbacks of storing state, thus it might get messy when sharing data e.g. in multithreaded contexts.
Should it suffice? Depends on context, but bear in mind this is no function object by any means, it is merely a stored state.
Can it be improved? Depends, but if the b
parameter passed by the API using the callback can utilized to somehow identify its caller, then maybe some container of such callback states can be created to separate them from each other.
The dead-simple solution would be sth like this. Again, I cannot tell it this suffices or not.
/* callback.c */
#include "callback.h"
static int first_arg;
/* May or may not be static.
I chose static for simplicity...
*/
static void my_callback(int a, int b)
{
/* whatever */
}
void api_compliant_callback(int b)
{
my_callback(first_arg, b);
}
void set_first_arg(int val)
{
first_arg = val;
}
/* callback.h */
#ifndef CALLBACK_H
#define CALLBACK_H
void set_first_arg(int);
void api_compliant_callback(int);
#endif /* CALLBACK_H */
/* file where the callbacks are registered */
#include "callback.h"
void fkn()
{
/* whatever */
set_first_arg(42);
setup_callback(api_compliant_callback);
/* anything that goes after */
}