我已经开始回顾回调了.我在SO上找到了这个链接: C中的"回调"是什么?它们是如何实现的? 它有一个很好的回调示例,它与我们在工作中使用的非常类似.但是,我试图让它工作,但我有很多错误.
#include/* 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; }
我知道回调使用函数指针来存储函数的地址.但是我发现有些事情我不明白:
"注册回调"和"事件调度程序"是什么意思?
Jonathan Lef.. 14
此代码使用-Wall在GCC下编译和运行.
#includestruct 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; }
您可能需要检查回调函数是否获取整个结构event_cb - 通常,您只是传递数据,因为,如所示,否则您有两个相同信息的来源(以及指针的备用副本)你所在的职能).可以对此进行大量清理 - 但它确实有效.
评论中的一个问题是:这是一个回调的好例子吗?
简而言之,没有 - 但部分是因为这里没有足够的基础设施.
从某种意义上说,您可以将传递给qsort()
或bsearch()
函数的比较函数视为回调.它是一个指向函数的指针,该函数被传递给泛型函数,该函数执行泛型函数不能为自己做的事情.
回调的另一个例子是信号处理函数.当事件 - 信号发生时,您告诉系统调用您的函数.您提前设置机制,以便在系统需要调用函数时,它知道要调用哪个函数.
示例代码试图提供更复杂的机制 - 带有上下文的回调.在C++中,这可能是一个仿函数.
我使用的一些代码对内存管理有非常繁琐的要求 - 在特定的上下文中使用时.因此,对于测试,我使用malloc()
et al,但在生产中,我必须将内存分配器设置为专用分配器.然后我在包中提供一个函数调用,以便繁琐的代码可以覆盖具有自己的代理版本的默认内存分配器 - 并且如果代理工作正常,代码将像以前一样运行.这些是一种回调形式 - 同样,一种在用户上下文数据方面不需要太多(或任何东西)的形式.
窗口系统具有已注册的事件处理程序(回调)以及GUI主事件循环将在事件发生时调用.那些通常需要用户上下文以及GUI系统提供的特定于事件的信息.
此代码使用-Wall在GCC下编译和运行.
#includestruct 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; }
您可能需要检查回调函数是否获取整个结构event_cb - 通常,您只是传递数据,因为,如所示,否则您有两个相同信息的来源(以及指针的备用副本)你所在的职能).可以对此进行大量清理 - 但它确实有效.
评论中的一个问题是:这是一个回调的好例子吗?
简而言之,没有 - 但部分是因为这里没有足够的基础设施.
从某种意义上说,您可以将传递给qsort()
或bsearch()
函数的比较函数视为回调.它是一个指向函数的指针,该函数被传递给泛型函数,该函数执行泛型函数不能为自己做的事情.
回调的另一个例子是信号处理函数.当事件 - 信号发生时,您告诉系统调用您的函数.您提前设置机制,以便在系统需要调用函数时,它知道要调用哪个函数.
示例代码试图提供更复杂的机制 - 带有上下文的回调.在C++中,这可能是一个仿函数.
我使用的一些代码对内存管理有非常繁琐的要求 - 在特定的上下文中使用时.因此,对于测试,我使用malloc()
et al,但在生产中,我必须将内存分配器设置为专用分配器.然后我在包中提供一个函数调用,以便繁琐的代码可以覆盖具有自己的代理版本的默认内存分配器 - 并且如果代理工作正常,代码将像以前一样运行.这些是一种回调形式 - 同样,一种在用户上下文数据方面不需要太多(或任何东西)的形式.
窗口系统具有已注册的事件处理程序(回调)以及GUI主事件循环将在事件发生时调用.那些通常需要用户上下文以及GUI系统提供的特定于事件的信息.
通过"注册回调"和"事件调度员"满足什么?
"注册回调"是告诉underyling系统调用哪个精确函数,以及(可选地)使用哪些参数,以及可能还应该调用哪个特定类型的事件回调的行为.
"事件调度程序"从O/S(或GUI等)接收事件,并实际调用回调,查看回调列表以查看哪些事件对该事件感兴趣.