13

我已经开始审查回调。我在 SO 上找到了这个链接: 什么是 C 中的“回调”以及它们是如何实现的? 它有一个很好的回调示例,与我们在工作中使用的非常相似。但是,我试图让它工作,但我有很多错误。

#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;
}

我知道回调使用函数指针来存储函数的地址。但是有几件事我觉得我不明白:

  • “注册回调”和“事件调度程序”是什么意思?
4

6 回答 6

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()orbsearch()函数的比较函数视为回调。它是一个指向传递给通用函数的函数的指针,该函数执行通用函数不能为自己做的事情。

回调的另一个示例是信号处理函数。当事件(信号)发生时,您告诉系统调用您的函数。您提前设置机制,以便当系统需要调用函数时,它知道要调用哪个函数。

示例代码试图提供一种更精细的机制——带有上下文的回调。在 C++ 中,这可能是一个仿函数。

我使用的一些代码对内存管理有非常挑剔的要求——当在特定上下文中使用时。因此,为了测试,我使用malloc()et al,但在生产中,我必须将内存分配器设置为专门的分配器。然后我在包中提供了一个函数调用,以便繁琐的代码可以使用自己的代理版本覆盖默认内存分配器 - 并且如果代理工作正常,代码将像以前一样运行。这些是回调的一种形式——同样,一种不需要太多(或任何东西)用户上下文数据的形式。

窗口系统具有已注册的事件处理程序(回调),并且 GUI 主事件循环将在事件发生时调用。这些通常需要用户上下文以及 GUI 系统提供的特定于事件的信息。

于 2009-03-10T17:12:28.537 回答
7

“注册回调”和“事件调度程序”是什么意思?

“注册回调”是告诉底层系统要调用哪个精确函数,以及(可选地)使用哪些参数,以及可能还为哪个特定类的事件调用回调的行为。

“事件调度程序”从操作系统(或 GUI 等)接收事件,并通过查看已注册回调列表以查看哪些对该事件感兴趣,从而实际调用回调。

于 2009-03-10T17:17:19.023 回答
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);

您不能将类型(“事件”)的名称传递给函数,您必须传递该类型的实例。

于 2009-03-10T17:09:02.320 回答
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;

或一些这样的东西。

于 2009-03-10T17:13:06.460 回答
1

注册回调意味着您指定在感兴趣的事件发生时应该调用哪个函数。基本上,您在注册回调时设置函数指针。

于 2009-03-10T17:11:33.030 回答
1

您创建了您声明的结构的指针,但它不指向任何东西:

struct event_cb *callback;

您应该只创建一种结构类型:

struct event_cb callback;

然后将其地址传递给函数。

于 2010-05-27T20:18:32.273 回答