我试图在c中创建一个非常简单的事件系统。我的界面如下所示:
typedef struct EventEmitter EventEmitter;
EventEmitter* emitter_create();
void emitter_subscribe(EventEmitter* emitter, void (*cb)(void*));
void emitter_publish(EventEmitter* emitter, void *payload);
一切正常,但为了注册一个事件监听器,我需要提供一个带有void *的函数指针。
static void registerOnKeyDown(void (*cb)(void*)) {
emitter_subscribe(keyDownEmitter, cb);
}
static void registerOnKeyUp(void (*cb)(void*)) {
emitter_subscribe(keyUpEmitter, cb);
}
有没有办法,使用宏或其他方式,允许EventEmitters的用户提供类型回调?类似的东西:
void onKey(int keyCode) {
printf("%d", keyCode);
}
而不是:
void onKey(void *keyCode) {
int code = (int)keyCode;
printf("%d", code);
}
答案 0 :(得分:1)
我最后通过在包装函数中根据需要简单地转换为void(* cb)(void *)来解决这个问题:
http://
答案 1 :(得分:1)
如果您知道类型是什么,可以使用C11泛型选择来查找参数的类型,并将其作为枚举值提供。
#include <stdio.h>
typedef struct EventEmitter EventEmitter;
typedef void (*INT_CALLBACK)(int);
typedef void (*VOIDP_CALLBACK)(void *);
enum cbtype {
_INT_CB,
_VOIDP_CB
};
void _safe_emitter_subscribe(EventEmitter *emitter,
void (*callback)(),
enum cbtype type)
{
printf("Registering a callback of type %d\n", type);
}
#define safe_emitter_subscribe(emitter, callback) \
_safe_emitter_subscribe( \
emitter, \
(void (*)())callback, \
_Generic(callback, \
INT_CALLBACK: _INT_CB, \
VOIDP_CALLBACK: _VOIDP_CB))
void func1(int a) {
}
void func2(void *a) {
}
int main(void) {
safe_emitter_subscribe(NULL, func1);
safe_emitter_subscribe(NULL, func2);
}
然后根据枚举值,您将知道如何再次强制转换函数:如果它是_INT_CB
,则必须在调用之前将其重新转换为INT_CALLBACK
; _VOIDP_CB
VOIDP_CALLBACK
等等。
答案 2 :(得分:0)
在answer上查看此Software Engineering SE。
鉴于您的API:
typedef struct EventEmitter EventEmitter;
EventEmitter* emitter_create();
void emitter_subscribe(EventEmitter* emitter, void (*cb)(void*));
void emitter_publish(EventEmitter* emitter, void *payload);
如果您修改它以在该API上定义订阅宏而不是推迟客户端代码,如下所示:
typedef struct EventEmitter EventEmitter;
EventEmitter* emitter_create();
void emitter_subscribe_impl(EventEmitter* emitter, void (*cb)(void*));
#define emitter_subscribe(emitter, xFunc) emitter_subscribe_impl((emitter), (void(*)(void*))(xFunc))
void emitter_publish_impl(EventEmitter* emitter, void *payload);
#define emitter_publish(emitter, xFunc) emitter_publish_impl((emitter), (void(*)(void*)(xFunc))
然后订阅者可以使用他们手头的类型来调用它。与所有API宏一样,请确保完整记录预期的争论,以便消费者知道要提供什么以及期望什么。