2009-03-10 3 views
11

Я начал просматривать обратные вызовы. Я нашел эту ссылку на SO: What is a "callback" in C and how are they implemented? У этого есть хороший пример обратного вызова, который очень похож на то, что мы используем на работе. Однако я попытался заставить его работать, но у меня много ошибок.Указатели функций и обратные вызовы в C

#include <stdio.h> 

/* Is the actual function pointer? */ 
typedef void (*event_cb_t)(const struct event *evt, void *user_data); 

struct event_cb 
{ 
    event_cb_t cb; 
    void *data; 
}; 

int event_cb_register(event_ct_t cb, void *user_data); 

static void my_event_cb(const struct event *evt, void *data) 
{ 
    /* do some stuff */ 
} 

int main(void) 
{ 
    event_cb_register(my_event_cb, &my_custom_data); 

    struct event_cb *callback; 

    callback->cb(event, callback->data); 

    return 0; 
} 

Я знаю, что обратные вызовы используют указатели функций для хранения адреса функции. Но есть несколько вещей, которые я нахожу, я не понимаю:

  • Что подразумевается под «регистрацией обратного вызова» и «диспетчером событий»?
+0

Почему бы не включать ваши ошибки? –

ответ

12

Этот код компилируется и запускается под 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.

+0

И код ужасно сработает, если вы не зарегистрируете функцию. –

+0

Здравствуйте, Спасибо за ваши усилия. Я просто нахожусь в указателях функций, так как мне придется использовать их на работе в первый раз. Это хороший пример обратного вызова. Пример, который я вам дал, казался близким к тому, что мы используем. Если вы знаете что-нибудь лучше? – ant2009

+0

Непонятно. Кто «спасен»? где он определен в вашем коде? – Curnelious

2

Без выхода компилятора это сложно, но я вижу несколько проблем;

int event_cb_register(event_ct_t cb, void *user_data); 

Должно быть

int event_cb_register(event_cb_t cb, void *user_data); 

Переменная my_custom_data не существует, когда он используется здесь;

event_cb_register(my_event_cb, &my_custom_data); 

Этот указатель никогда не инициализируется;

struct event_cb *callback; 

И в;

callback->cb(event, callback->data); 

Вы не можете передать имя типа («событие») к функции, вы должны передать экземпляр этого типа.

+0

Первым параметром является event_ct_t, но должно быть event_cb_t –

1

Регистрация обратного вызова означает, что вы указываете, какую функцию следует вызывать при возникновении события интереса. В основном вы устанавливаете указатель функции при регистрации обратного вызова.

2
int event_cb_register(event_ct_t cb, void *user_data); 

Что такое event_ct_t? Вы имеете в виду event_cb_t?

struct event_cb *callback; 

Создает неинициализированный указатель на структуру event_cb. Обратите внимание, что это указывает на мусор.

callback->cb(event, callback->data); 

Вы пытаетесь вызвать мусор. Вам необходима инициализация:

struct event_cb callback; 
callback.cb = my_event_cb; 
callback.data = 42; 

или некоторые подобные вещи.

6

Что встречает «регистрация обратного вызова» и «диспетчер событий»?

«регистрация обратного вызова» является актом говоря систему underyling, какие именно функции для вызова, и (необязательно), с помощью которых параметров, а также, возможно, для которых конкретного класс событий, обратный вызов должен быть вызван.

«Диспетчер событий» принимает события от O/S (или GUI и т. Д.) И фактически вызывает обратные вызовы, просматривая список обратных вызовов, чтобы узнать, кто заинтересован в этом событии.

+1

hello, saved.cb (& saved, saved.data); Будет ли это депаттером события, который вызывает калбек? Таким образом, для регистрации обратного вызова будет назначаться адрес функции обратного вызова указателю функции, который вызовет обратный вызов при вызове указателя функции? – ant2009

+0

Да, это диспетчер, хотя и самый тривиальный. – Alnitak

1

Вы создали указатель на структуры вы объявлены, но это не указывает ни на что:

struct event_cb *callback; 

Вы должны просто создать тип вашей структуры:

struct event_cb callback; 

, а затем передать его обратитесь к функциям.

Смежные вопросы