указатели функций и перечисление в C - PullRequest
2 голосов
/ 09 января 2020

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

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

event=(eid, data)
switch(eid) {
    case eid1:
        handler1(data);
        break;
    case edi2:
        handler2(data);
        break;

}

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

event=(eid, data)
eid(data)

Теперь, если я помещаю его в Например, как:

static void abc(void * p) {

}

static void abc2(void * p) {

}

enum eventId {
    eid1 = abc,
    eid2 = abc2
} xyz;

Мой компилятор говорит:

error: enumerator value for 'eid1' is not an integer constant eid1 = abc

Что абсолютно верно.

Есть идеи, как решить эту проблему?

Ответы [ 3 ]

4 голосов
/ 09 января 2020

Используйте массив указателей на функции и используйте enum в качестве индекса.

typedef void (*handler_func)(void *);
handler_func event_handlers[] = { abc, abc2 };
enum eventId {
    eid1 = 0,
    eid2 = 1,
    eid_max
}

if (eid < eid_max) event_handlers[eid](data);
2 голосов
/ 09 января 2020

enums нельзя связать с другими данными в C, но препроцессор может сгенерировать для вас код в виде X-Macros .

#include <stdio.h>

typedef void (*handler_func)(void *);

static void handler1(void *const param) {
    printf("Event 1: %p.\n", param);
}

static void handler2(void *const param) {
    printf("Event 2: %p.\n", param);
}

#define EVENT(X) \
    X(EID1, &handler1), \
    X(EID2, &handler2)

#define PARAMA(A, B) A
#define PARAMB(A, B) B
#define STRINGISEA(A, B) #A

enum Event { EVENT(PARAMA) };
static const handler_func event_handlers[] = { EVENT(PARAMB) };
static const char *const event_strings[] = { EVENT(STRINGISEA) };
/* Everything will be the same size, pick one. */
static const size_t event_size = sizeof event_strings / sizeof *event_strings;

int main(void) {
    size_t i;
    void *const param = (void *)0x100;
    for(i = 0; i < event_size; i++) {
        printf("Calling %s.\n", event_strings[i]);
        event_handlers[i](param);
    }
    return 0;
}

Дает,

Calling EID1.
Event 1: 0x100.
Calling EID2.
Event 2: 0x100.

Преимущество этой реализации в том, что это единственный источник правды ; если кто-то решил добавить больше events, их нужно будет добавить только в одном месте. Недостаток в том, что его трудно читать.

1 голос
/ 10 января 2020

В качестве дополнения к ответу @Barmar вы можете использовать технику, называемую X macro , чтобы поддерживать в порядке соответствующие пары (eid, handler). Обратите внимание, что вам нужно только изменить определение макроса LIST_OF_EVENTS, добавляя или удаляя пары по мере необходимости.

void handler1(void*);
void handler2(void*);
void handler3(void*);

#define LIST_OF_EVENTS X(eid1, handler1), X(eid2, handler2), X(eid3, handler3)

#define X(id, x) id
enum evID { LIST_OF_EVENTS };
#undef X

#define X(x, handler) handler
void (*handlers[])(void*) = { LIST_OF_EVENTS };
#undef X

int get_event(void**);

void event_loop(void)
{
    for (;;) {
        void *data;
        int eid = get_event(&data);
        handlers[eid](data);
    }
}

Определения макросов расширяются до

enum evID { eid1, eid2, eid3 };
void (*handlers[])(void*) = { handler1, handler2, handler3 };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...