views:

2491

answers:

6

Hello,

I have started to review callbacks. I found this link: http://stackoverflow.com/questions/142789/what-is-a-callback-in-c-and-how-are-they-implemented

which has a good example of callback which is very similar to what we use at work. However, I have tried to get it to work, but I have many errors.

#include <stdio.h>

/* Is the actual function pointer? */
typedef void (*event_cb_t)(const struct event *evt, void *user_data);

struct event_cb
{
event_cb_t cb;
void *data;
};

int event_cb_register(event_ct_t cb, void *user_data);

static void my_event_cb(const struct event *evt, void *data)
{
/* do some stuff */
}

int main(void)
{
event_cb_register(my_event_cb, &my_custom_data);

struct event_cb *callback;

callback->cb(event, callback->data);

return 0;
}

I know that callback use function pointers to store an address of a function.

But there is a few things that I find I don't understand. That is what is meet by "registering the callback" and "event dispatcher"?

Many thanks for any advice,

+2  A: 

Without the compiler output it's hard, but I can see a few problems;

int event_cb_register(event_ct_t cb, void *user_data);

Should be

int event_cb_register(event_cb_t cb, void *user_data);

The my_custom_data variable does not exist when it's used here;

event_cb_register(my_event_cb, &my_custom_data);

This pointer is never initialized;

struct event_cb *callback;

And in;

callback->cb(event, callback->data);

You cannot pass the name of a type ('event') to a function, you must pass an instance of that type.

Andrew Grant
The first param is event_ct_t but should be event_cb_t
Andrew Grant
+1  A: 

Registering a callback means that you are specifying which function should be called when the event of interest occurs. Basically you are setting the function pointer when registering a callback.

Trent
+1  A: 

This code compiles and runs under GCC with -Wall.

#include <stdio.h>

struct event_cb;

typedef void (*event_cb_t)(const struct event_cb *evt, void *user_data);

struct event_cb
{
    event_cb_t cb;
    void *data;
};

static struct event_cb saved = { 0, 0 };

void event_cb_register(event_cb_t cb, void *user_data)
{
    saved.cb = cb;
    saved.data = user_data;
}

static void my_event_cb(const struct event_cb *evt, void *data)
{
    printf("in %s\n", __func__);
    printf("data1: %s\n", (const char *)data);
    printf("data2: %s\n", (const char *)evt->data);
}

int main(void)
{
    char my_custom_data[40] = "Hello!";
    event_cb_register(my_event_cb, my_custom_data);

    saved.cb(&saved, saved.data);

    return 0;
}

You probably need to review whether the call back function gets the whole struct event_cb or not - usually, you'd just pass the data because, as demonstrated, otherwise you have two sources of the same information (and a spare copy of the pointer to the function that you're in). There is a lot of cleanup that can be done on this - but it does work.


A question in the comments asks: Is this a good example of a callback?

Succinctly, no - but in part because there isn't sufficient infrastructure here.

In a sense, you can think of the comparison function passed to the qsort() or bsearch() functions as a callback. It is a pointer to a function that is passed into the generic function that does what the generic function cannot do for itself.

Another example of a callback is a signal handler function. You tell the system to call your function when the event - a signal - occurs. You set up the mechanisms ahead of time so that when the system needs to call a function, it knows which function to call.

The example code is attempting to provide a more elaborate mechanism - a callback with a context. In C++, this would perhaps be a functor.

Some of the code I work with has very fussy requirements about memory management - when used in a particular context. So, for testing, I use malloc() et al, but in production, I have to set the memory allocators to the specialized allocators. Then I provide a function call in the package so that the fussy code can override the default memory allocators with its own surrogate versions - and provided the surrogates work OK, the code will behave as before. Those are a form of callback - again, a form that does not need much (or anything) in the way of user context data.

Windowing systems have event handlers (callbacks) that are registered and that the GUI main event loop will call when events occur. Those usually need user context as well as the event-specific information provided by the GUI system.

Jonathan Leffler
And the code crashes horribly if you don't register the function.
Jonathan Leffler
Hello, Thanks for your effort. I am just getting into function pointers, as I will have to use them at work for the first time. Is this a good example of a callback. The example I gave you seemed close to what we use. If you know anything better?
robUK
+1  A: 
int event_cb_register(event_ct_t cb, void *user_data);

What is that type event_ct_t? Do you mean event_cb_t?

struct event_cb *callback;

Creates an uninitialized pointer to a structure event_cb. Note mostly this points to garbage.

callback->cb(event, callback->data);

You are trying to call garbage. You need initialization:

struct event_cb callback;
callback.cb = my_event_cb;
callback.data = 42;

or some such stuff.

dirkgently
+1  A: 

What is meet by "registering the callback" and "event dispatcher"?

"registering the callback" is the act of telling the underyling system which precise function to call, and (optionally) with which parameters, and possibly also for which particular class of events that callback should be invoked.

The "event dispatcher" receives events from the O/S (or GUI, etc), and actually invokes the callbacks, by looking in a list of callbacks to see which are interested in that event.

Alnitak
hello,saved.cb( Would this be the event depatcher that invokes the calback?So registering the callback would be assigning the address of the callback function to the function pointer, that will call the callback when the function pointer is invoked?
robUK
yes, that's the dispatcher, albeit the most trivial one possible.
Alnitak
+1  A: 

you created a pointer of the struct you declared, but it does not point to anything :

struct event_cb *callback;

you should just create a type of your struct

struct event_cb callback;

I hope this helps

jramirez