Этот код компилируется и запускается под 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.
Почему бы не включать ваши ошибки? –