【发布时间】:2010-05-08 14:59:11
【问题描述】:
gcc 4.4.3 c89
我正在创建一个客户端服务器应用程序,我需要实现一些回调函数。
但是,我在回调方面没有太多经验。我想知道在设计回调时是否有人知道一些好的参考资料。是否有任何用于 c 的设计模式。我确实看过一些模式,但那里都是 c++。
非常感谢您的任何建议,
【问题讨论】:
标签: c
gcc 4.4.3 c89
我正在创建一个客户端服务器应用程序,我需要实现一些回调函数。
但是,我在回调方面没有太多经验。我想知道在设计回调时是否有人知道一些好的参考资料。是否有任何用于 c 的设计模式。我确实看过一些模式,但那里都是 c++。
非常感谢您的任何建议,
【问题讨论】:
标签: c
这是一个非常粗略的例子。请注意,我要演示的唯一内容是回调的使用,它旨在提供信息,而不是演示。
假设我们有一个库(或任何围绕结构旋转的函数集),我们将拥有与此类似的代码(当然,我将其命名为 foo):
typedef struct foo {
int value;
char *text;
} foo_t;
这很简单。然后(通常)我们会提供一些分配和释放它的方法,例如:
foo_t *foo_start(void)
{
foo_t *ret = NULL;
ret = (foo_t *)malloc(sizeof(struct foo));
if (ret == NULL)
return NULL;
return ret;
}
然后:
void foo_stop(foo_t *f)
{
if (f != NULL)
free(f);
}
但是我们想要一个回调,所以我们可以定义一个函数,当foo->text 有事情要报告时将被输入。为此,我们使用类型化函数指针:
typedef void (* foo_callback_t)(int level, const char *data);
我们还希望任何foo 系列函数能够方便地输入此回调。为此,我们需要将其添加到结构中,现在看起来像这样:
typedef struct foo {
int value;
char *text;
foo_callback_t callback;
} foo_t;
然后我们编写将实际进入的函数(使用我们回调类型的相同原型):
void my_foo_callback(int val, char *data)
{
printf("Val is %d, data is %s\n", val, data == NULL ? "NULL" : data);
}
然后我们需要写一些方便的方式来说明它实际指向的函数:
void foo_reg_callback(foo_t *f, void *cbfunc)
{
f->callback = cbfunc;
}
然后我们的其他 foo 函数可以使用它,例如:
int foo_bar(foo_t *f, char *data)
{
if (data == NULL)
f->callback(LOG_ERROR, "data was NULL");
}
请注意,在上面:
f->callback(LOG_ERROR, "data was NULL");
就是这样:
my_foo_callback(LOG_ERROR, "data was NULL"):
除此之外,我们通过之前设置的 函数指针 输入my_foo_callback(),从而使我们能够灵活地动态定义自己的处理程序(甚至根据需要切换处理程序) .
回调(甚至上面的代码)的最大问题之一是使用它们时的类型安全。许多回调将采用void * 指针,通常命名为context 之类的名称,可以是任何类型的数据/内存。这提供了很大的灵活性,但如果您的指针远离您,可能会出现问题。例如,您不想通过赋值意外地将实际上是 struct * 的内容转换为 char *(或 int)。您可以传递的不仅仅是简单的字符串和整数——结构、联合、枚举等都可以传递。 CCAN 的type safe callbacks 可帮助您在这样做时避免不经意间的邪恶演员(到/来自void *)。
同样,这是一个过于简化的示例,旨在让您大致了解一种使用回调的可能方式。请考虑它只是作为示例的伪代码。
【讨论】:
在 C 中,回调是通过函数指针完成的。
您绝对想要的一个功能是用户定义的上下文。您的代码采用 void * 指针并使其可用于回调函数:
void callback(..., void *ctx);
void call_service_which_invokes_callback(...,
void (*cb)(..., void *ctx),
void *ctx);
这样,回调可以访问任何必要的状态,而无需使用全局变量。
【讨论】:
【讨论】: