Stefan Jaritz
Stefan Jaritz

Reputation: 2309

function pointers and enum in C

I am looking for a fancy way to link function pointers and enums.

In my case I have a message queue that holds a event id and some data associated with the event. some simple pseudo code:

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

}

Now I like to do some optimization. If the event id has the value of the function called inside of the switch case statement I can save the switch case decode by preserving a nice readability of the code.

event=(eid, data)
eid(data)

Now if I am putting it into an example like:

static void abc(void * p) {

}

static void abc2(void * p) {

}

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

My compiler tells:

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

What is absolutely right.

Any ideas how to solve that problem?

Upvotes: 2

Views: 2176

Answers (3)

Barmar
Barmar

Reputation: 781726

Use an array of function pointers, and use the enum as the index.

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

Upvotes: 4

Lxer Lx
Lxer Lx

Reputation: 422

As an extension to the answer of @Barmar, you can use a technique called X macro, to keep corresponding (eid, handler) pairs in order. Note that you need only to change the definition of LIST_OF_EVENTS macro, adding or deleting pairs as needed.

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

Macro defitions expand to

enum evID { eid1, eid2, eid3 };
void (*handlers[])(void*) = { handler1, handler2, handler3 };

Upvotes: 2

Neil
Neil

Reputation: 1922

enums cannot be linked with other data in C, but the preprocessor can generate code for you in the form of 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;
}

Gives,

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

The advantage of this implementation is it's a single source of truth; if one decided to add more events, they will only need to be added in one spot. The disadvantage is it's hard to read.

Upvotes: 2

Related Questions