Вот очень грубый пример. Обратите внимание, что единственное, что я пытаюсь продемонстрировать, - это использование обратных вызовов, предназначенных для информационных, а не демонстрационных.
Допустим, что у нас есть библиотека (или любой другой набор функций, которые вращаются вокруг структуры), мы будем иметь код, который выглядит примерно так (конечно, я называть его 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's type safe callbacks поможет вам избежать невольно злых бросков (до/от void *
) при этом.
Опять же, это более упрощенный пример, призванный дать вам обзор одного из возможных способов использования обратных вызовов. Пожалуйста, обратите внимание на код psuedo, который предназначен только в качестве примера.
большой .......... –