Reputation: 2309
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
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
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
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