C宏用于类型安全回调

时间:2017-12-17 19:06:17

标签: c macros

我试图在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);
}

3 个答案:

答案 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宏一样,请确保完整记录预期的争论,以便消费者知道要提供什么以及期望什么。