C 中的函数指针和枚举
function pointers and enum in C
我正在寻找 link 函数指针和枚举的奇特方法。
在我的例子中,我有一个消息队列,其中包含一个事件 ID 和一些与该事件相关的数据。
一些简单的伪代码:
event=(eid, data)
switch(eid) {
case eid1:
handler1(data);
break;
case edi2:
handler2(data);
break;
}
现在我喜欢做一些优化。如果事件 ID 具有在 switch case 语句内部调用的函数的值,我可以通过保持代码的良好可读性来保存 switch case 解码。
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
什么是绝对正确的。
有解决该问题的想法吗?
使用函数指针数组,以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);
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.
此实现的优点是 single source of truth;如果决定添加更多 events
,则只需将它们添加到一个位置。缺点是阅读难度大
作为@Barmar 答案的扩展,您可以使用一种称为 X 宏 的技术来保持相应的 (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 };
我正在寻找 link 函数指针和枚举的奇特方法。
在我的例子中,我有一个消息队列,其中包含一个事件 ID 和一些与该事件相关的数据。 一些简单的伪代码:
event=(eid, data)
switch(eid) {
case eid1:
handler1(data);
break;
case edi2:
handler2(data);
break;
}
现在我喜欢做一些优化。如果事件 ID 具有在 switch case 语句内部调用的函数的值,我可以通过保持代码的良好可读性来保存 switch case 解码。
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
什么是绝对正确的。
有解决该问题的想法吗?
使用函数指针数组,以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);
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.
此实现的优点是 single source of truth;如果决定添加更多 events
,则只需将它们添加到一个位置。缺点是阅读难度大
作为@Barmar 答案的扩展,您可以使用一种称为 X 宏 的技术来保持相应的 (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 };