Ответ 1
Этот код компилируется и запускается под 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()
в качестве обратного вызова. Это указатель на функцию, которая передается в общую функцию, которая выполняет то, что общая функция не может сделать для себя.
Другим примером обратного вызова является функция обработчика сигналов. Вы сообщаете системе, чтобы вызвать вашу функцию, когда происходит событие - сигнал. Вы заранее создали механизмы, чтобы, когда системе нужно вызвать функцию, она знает, какую функцию вызывать.
Пример кода пытается обеспечить более сложный механизм - обратный вызов с контекстом. В С++ это, возможно, будет функтором.
Некоторые из кода, с которым я работаю, имеют очень суетливые требования к управлению памятью - при использовании в конкретном контексте. Поэтому для тестирования я использую malloc()
et al, но в процессе производства я должен установить распределители памяти в специализированные распределители. Затем я предоставляю вызов функции в пакете, так что суетливый код может переопределять распределенные распределители памяти по умолчанию со своими суррогатными версиями - и при условии, что суррогаты работают нормально, код будет вести себя как раньше. Это форма обратного вызова - опять-таки форма, которая не нуждается в большом количестве (или что-либо) в способе пользовательских контекстных данных.
Оконные системы имеют обработчики событий (обратные вызовы), которые зарегистрированы, и что цикл событий основного графического интерфейса будет вызываться при возникновении событий. Обычно требуется контекст пользователя, а также информация о событии, предоставляемая системой GUI.