我已经开始审核回调了。我在SO上找到了这个链接: What is a "callback" in C and how are they implemented?它有一个很好的回调示例,它与我们在工作中使用的非常类似。但是,我试图让它工作,但我有很多错误。
#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;
}
我知道回调使用函数指针来存储函数的地址。但是我发现有些事情我不明白:
答案 0 :(得分:14)
此代码使用-Wall编译并在GCC下运行。
#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;
}
您可能需要检查回调函数是否获取整个struct event_cb - 通常,您只是传递数据,因为,如所示,否则您有两个相同信息的来源(以及备用副本)指向您所在函数的指针。可以对此进行大量清理 - 但它确实有效。
评论中的一个问题是:这是一个回调的好例子吗?
简洁,不 - 但部分是因为这里没有足够的基础设施。
从某种意义上说,您可以将传递给qsort()
或bsearch()
函数的比较函数视为回调函数。它是一个指向函数的指针,该函数被传递给泛型函数,该函数执行泛型函数不能为自己做的事情。
回调的另一个例子是信号处理函数。当事件 - 信号发生时,您告诉系统调用您的函数。您提前设置机制,以便在系统需要调用函数时,它知道要调用哪个函数。
示例代码试图提供更复杂的机制 - 带有上下文的回调。在C ++中,这可能是一个算子。
我使用的一些代码对内存管理有非常繁琐的要求 - 在特定上下文中使用时。因此,为了测试,我使用malloc()
等,但在生产中,我必须将内存分配器设置为专用分配器。然后我在包中提供一个函数调用,以便繁琐的代码可以覆盖具有自己的代理版本的默认内存分配器 - 并且如果代理工作正常,代码将像以前一样运行。这是一种回调形式 - 同样,这种形式在用户上下文数据方面不需要太多(或任何东西)。
窗口系统具有已注册的事件处理程序(回调),并且当事件发生时GUI主事件循环将调用。这些通常需要用户上下文以及GUI系统提供的特定于事件的信息。
答案 1 :(得分:7)
“注册回调”和“事件调度程序”是什么意思?
“注册回调”是告诉底层系统调用哪个精确函数,以及(可选地)使用哪些参数,以及可能还应该调用哪些特定类型的事件回调的行为。
“事件调度程序”从O / S(或GUI等)接收事件,并实际调用回调,查看已注册的回调列表,以查看哪些事件对该事件感兴趣。
答案 2 :(得分:2)
没有编译器输出很难,但我可以看到一些问题;
int event_cb_register(event_ct_t cb, void *user_data);
应该是
int event_cb_register(event_cb_t cb, void *user_data);
my_custom_data
变量在此处使用时不存在;
event_cb_register(my_event_cb, &my_custom_data);
永远不会初始化此指针;
struct event_cb *callback;
并且在;
callback->cb(event, callback->data);
您无法将类型名称('event')传递给函数,您必须传递该类型的实例。
答案 3 :(得分:2)
int event_cb_register(event_ct_t cb, void *user_data);
那种类型event_ct_t
是什么?你的意思是event_cb_t
?
struct event_cb *callback;
创建一个指向结构event_cb
的未初始化指针。请注意,这主要指向垃圾。
callback->cb(event, callback->data);
你正试图打电话给垃圾。你需要初始化:
struct event_cb callback;
callback.cb = my_event_cb;
callback.data = 42;
或其他一些东西。
答案 4 :(得分:1)
注册回调意味着您要指定在感兴趣的事件发生时应调用哪个函数。基本上,您在注册回调时设置了函数指针。
答案 5 :(得分:1)
您创建了一个您声明的结构的指针,但它没有指向任何内容:
struct event_cb *callback;
您应该只创建一种结构类型:
struct event_cb callback;
然后将其地址传递给函数。