Here is a very crude example. Note that the only thing I'm trying to demonstrate is the use of callbacks, which should be informative, not a demonstration.
Let's say that we have a library (or any set of functions that revolve around a structure), we will have code similar to this one (of course, I call it foo):
typedef struct foo { int value; char *text; } foo_t;
It is quite simple. Then we (conditionally) provide some means of allocation and release, such as:
foo_t *foo_start(void) { foo_t *ret = NULL; ret = (foo_t *)malloc(sizeof(struct foo)); if (ret == NULL) return NULL; return ret; }
And then:
void foo_stop(foo_t *f) { if (f != NULL) free(f); }
But we need a callback, so we can define a function to be introduced when foo->text something. To do this, we use a typed function pointer:
typedef void (* foo_callback_t)(int level, const char *data);
We also want any of the foo function families to conveniently use this callback. To do this, we need to add it to the structure, which will now look like this:
typedef struct foo { int value; char *text; foo_callback_t callback; } foo_t;
Then we write a function that will actually be introduced (using the same prototype of our callback type):
void my_foo_callback(int val, char *data) { printf("Val is %d, data is %s\n", val, data == NULL ? "NULL" : data); }
Then we need to write a convenient way to say which function it actually points to:
void foo_reg_callback(foo_t *f, void *cbfunc) { f->callback = cbfunc; }
And then our other foo functions can use it, for example:
int foo_bar(foo_t *f, char *data) { if (data == NULL) f->callback(LOG_ERROR, "data was NULL"); }
Please note that in the above example:
f->callback(LOG_ERROR, "data was NULL");
How it's done:
my_foo_callback(LOG_ERROR, "data was NULL"):
In addition, we introduce my_foo_callback() using the function pointer that we previously set, thereby giving us the flexibility to define our own handler on the fly (and even switch handlers if / if necessary).
One of the biggest problems with callbacks (and even the code above) is type safety when using them. Many callbacks will accept a void * pointer, commonly referred to as context , which can be any type of data / memory. This provides more flexibility, but can be problematic if your pointers leave you. For example, you donβt want to accidentally use what is actually struct * like char * (or int , for that matter) by assignment. You can convey much more than simple strings and integers - structures, unions, enumerations, etc. - all this can be conveyed. CCAN enter safe callbacks to help avoid unwanted ghosts (from / void * ) in doing so.
Again, this is a more simplified example, which is designed to give you an overview of one of the possible ways to use callbacks. Please take a look at the psuedo code, which is intended as an example only.