如何在 C 中实现回调函数?

ant*_*009 5 c

海湾合作委员会 4.4.3 c89

我正在创建一个客户端服务器应用程序,我需要实现一些回调函数。

但是,我在回调方面不太有经验。我想知道是否有人知道设计回调时可以遵循的一些好的参考材料。是否有用于 c 的设计模式?我确实看过一些模式,但那里都是 C++。

非常感谢您的任何建议,

小智 4

这是一个非常粗略的例子。请注意,我唯一想要演示的是回调的使用,其设计目的是提供信息,而不是演示。

假设我们有一个库(或任何围绕结构的函数集),我们将拥有与此类似的代码(当然,我将其命名为 foo):

typedef struct foo {
     int value;
     char *text;
} foo_t;
Run Code Online (Sandbox Code Playgroud)

这很简单。然后,我们(按照惯例)提供一些分配和释放它的方法,例如:

foo_t *foo_start(void)
{
    foo_t *ret = NULL;

    ret = (foo_t *)malloc(sizeof(struct foo));
    if (ret == NULL)
        return NULL;

    return ret;
}
Run Code Online (Sandbox Code Playgroud)

进而:

void foo_stop(foo_t *f)
{
   if (f != NULL)
     free(f);
}
Run Code Online (Sandbox Code Playgroud)

但我们想要一个回调,所以我们可以定义一个函数,当有东西要报告时将进入该函数foo->text。为此,我们使用类型化函数指针:

typedef void (* foo_callback_t)(int level, const char *data);
Run Code Online (Sandbox Code Playgroud)

我们还希望该foo函数系列中的任何一个都能够方便地进入此回调。为此,我们需要将其添加到结构中,现在看起来像这样:

typedef struct foo {
     int value;
     char *text;
     foo_callback_t callback;
} foo_t;
Run Code Online (Sandbox Code Playgroud)

然后我们编写实际要输入的函数(使用与我们的回调类型相同的原型):

void my_foo_callback(int val, char *data)
{
    printf("Val is %d, data is %s\n", val, data == NULL ? "NULL" : data);
}
Run Code Online (Sandbox Code Playgroud)

然后我们需要编写一些方便的方法来说明它实际指向的函数:

void foo_reg_callback(foo_t *f, void *cbfunc)
{
    f->callback = cbfunc;
}
Run Code Online (Sandbox Code Playgroud)

然后我们的其他 foo 函数就可以使用它,例如:

int foo_bar(foo_t *f, char *data)
{
    if (data == NULL)
       f->callback(LOG_ERROR, "data was NULL");
}
Run Code Online (Sandbox Code Playgroud)

请注意上面的内容:

f->callback(LOG_ERROR, "data was NULL");
Run Code Online (Sandbox Code Playgroud)

就像这样做:

my_foo_callback(LOG_ERROR, "data was NULL"):
Run Code Online (Sandbox Code Playgroud)

除此之外,我们my_foo_callback()通过之前设置的函数指针进入,从而使我们能够灵活地动态定义自己的处理程序(甚至根据需要切换处理程序)。

回调(甚至上面的代码)的最大问题之一是使用它们时的类型安全。许多回调将采用一个void *指针,通常命名为类似的东西context,可以是任何类型的数据/内存。这提供了很大的灵活性,但如果您的指针离开您,可能会出现问题。例如,您不想通过赋值意外地转换实际上是 a struct *char *int就此而言)的内容。您可以传递的不仅仅是简单的字符串和整数 - 结构、联合、枚举等都可以传递。CCAN 的类型安全回调可帮助您避免void *在执行此操作时无意中进行邪恶的强制转换(to / from )。

同样,这是一个过于简化的示例,旨在让您概述使用回调的一种可能方法。请考虑它只是作为示例的伪代码。