2010-10-27 27 views
2

我的图书馆提供了一个回调点,我的图书馆用户可以注册以获取信息。回调的一般形式是int,后面跟着各种类型取决于int值的参数。因此,我定义了回调类型和函数来设置它如下。什么是C回调函数的参数更好:va_list或省略号?

typedef void (* callback_func_t)(int type, ...); 
void set_callback_func(callback_func_t callback); 

在我的库中,我一直在调用这个函数,作为用户设置函数,或者我提供的默认函数。有用。

现在,我想添加一个间接级别,以便能够调用多个注册的回调。麻烦的是我的内部回调函数仍然需要省略号参数,也必须用省略号调用回调函数。因此,我的内部函数必须解释type,从va_list中解压参数并将它们作为callbacj函数的参数。

void internal_callback(int type, ...) { 
    va_list args; 
    va_start(args, type); 
    switch (type) { 
    case 0: call_callback(type, va_arg(args, int)); break; 
    // ... 
    } 
    va_end(args); 
} 

但随后,在用户执行回调,也会有同样的va_list使用以及的参数解释,根据type值。解决方案是直接将va_list作为参数传递给回调函数,使内部回调函数的实现变得明显。

typedef void (* callback_func_t)(int type, va_list args); 

我的问题是:这是很好的做法,定义一个回调函数类型采用va_list作为参数?我可以像上面那样定义我的回调函数类型,但是与顶部定义相比有什么优点和缺点?

回答

3

我假设有一个有限的已知数量的类型?如果是这样,为什么不使用枚举和工会类型安全的一定程度上,这会顺带还解决您的问题:

enum callback_arg_type { INT_ARG, DOUBLE_ARG, VECTOR_ARG }; 

union callback_arg 
{ 
    int as_int; 
    double as_double; 
    struct { double x, y, z; } as_vector; 
}; 

typedef void (*callback_func_t)(
    enum callback_arg_type type, union callback_arg arg); 

根据参数大小的差异,这可能是一个好主意,通过一个指针代替。你也可以提供一些宏来提供回调调用一个更好的语法,但如果他们只会被调用从资料库中,它可能不值得:

union callback_arg arg = { .as_int = 42 }; 
callback_fn(INT_ARG, arg); 

是对自己很短。

2

我会去与va_list版本。用户已经在处理va_list的处理问题了,所以你不要通过隐藏它来保存任何内容。

此外使用列表而不是尝试重新参数将减少堆栈使用。如果必须将参数重新提交到它们的函数中,它将创建所有这些参数的新副本,但传递va_list类型将只使用已经在堆栈上的那些参数的实例。最后,你的代码会更简单(如果我正确地理解了这个问题),并且你将保存每个用户不必调用va_startva_end(这可能不会改变其功能中的输出代码大多数实现方式为stdarg),但除此之外,他们都必须键入这些呼叫,并且(取决于如何在平台上实施stdarg),他们需要确保在返回之前确实实际拨打va_end(如果返回时很容易错过一个错误)。

相关问题