Этот код компилируется и запускается под GCC с -Wall.
#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;
}
Возможно, вам нужно проверить, получает ли функция обратного вызова всю структуру event_cb или нет - обычно вы просто передаете данные, потому что, как показано, в противном случае у вас есть два источника одной и той же информации (и запасная копия указатель на функцию, в которой вы находитесь). С этим можно многое сделать, но это работает.
Вопрос в комментариях спрашивает: это хороший пример обратного вызова?
Вкратце, нет, но отчасти потому, что здесь недостаточно инфраструктуры.
В некотором смысле вы можете рассматривать функцию сравнения, переданную в функции qsort()
или bsearch()
, в качестве обратного вызова. Это указатель на функцию, которая передается в универсальную функцию, которая делает то, что универсальная функция не может сделать для себя.
Другим примером обратного вызова является функция обработчика сигнала. Вы говорите системе, чтобы она вызывала вашу функцию, когда происходит событие - сигнал. Вы устанавливаете механизмы заранее, чтобы, когда системе нужно было вызвать функцию, она знала, какую функцию вызывать.
Пример кода пытается предоставить более сложный механизм - обратный вызов с контекстом. В C ++ это может быть функтор.
У некоторого кода, с которым я работаю, есть очень суетные требования к управлению памятью - при использовании в определенном контексте. Итак, для тестирования я использую malloc()
и др., Но на производстве я должен установить распределители памяти для специализированных распределителей. Затем я предоставляю вызов функции в пакете, чтобы суетливый код мог переопределить стандартные распределители памяти своими суррогатными версиями - и при условии, что суррогаты работают нормально, код будет работать как прежде. Это форма обратного вызова - опять же, форма, которая не требует большого (или чего-либо) в отношении данных контекста пользователя.
В оконных системах есть обработчики событий (обратные вызовы), которые зарегистрированы и которые основной цикл событий GUI будет вызывать при возникновении событий. Для них обычно требуется пользовательский контекст, а также информация о событиях, предоставляемая системой графического интерфейса.