Указатели на функции и обратные вызовы в C - PullRequest
13 голосов
/ 10 марта 2009

Я начал просматривать обратные вызовы. Я нашел эту ссылку на SO: Что такое "обратный вызов" в 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;
}

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

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

Ответы [ 6 ]

14 голосов
/ 10 марта 2009

Этот код компилируется и запускается под 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 будет вызывать при возникновении событий. Для них обычно требуется пользовательский контекст, а также информация о событиях, предоставляемая системой графического интерфейса.

7 голосов
/ 10 марта 2009

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

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

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

2 голосов
/ 10 марта 2009
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;

или что-то в этом роде.

2 голосов
/ 10 марта 2009

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

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);

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

1 голос
/ 28 мая 2010

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

struct event_cb *callback;

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

struct event_cb callback;

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

1 голос
/ 10 марта 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...