C 回调函数的参数哪个更好:va_list 或省略号?
我的图书馆提供了一个回调点,我的图书馆用户可以在其中注册以获取信息。回调的一般形式是一个 int
,后面跟着各种参数,这些参数的类型取决于 int 值。因此,我定义了回调类型和设置它的函数,如下所示。
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 作为参数的回调函数类型是一个好的做法吗?我可以像上面那样定义我的回调函数类型,但是与顶部的定义相比有什么优点和缺点?
My library offers a callback point where my library users can register to get information. The general form of the callback is an int
followed by various parameters whose type depend on the int value. Therefore, I defined the callback type and the function to set it as follows.
typedef void (* callback_func_t)(int type, ...);
void set_callback_func(callback_func_t callback);
Within my library, I am calling this function all throughout, being the user set function, or the default one I am providing. It works.
Now, I'd like to add a level of indirection, to be able to call several registered callbacks. The trouble is that my internal callback function that still takes ellipsis parameters, also have to call the callback function with ellipsis. As a consequence, my internal function has to interpret the type
, unpack the parameters from the va_list
and give them as parameters to the callbacj function.
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);
}
But then, in the user's implementation of the callback, there will also be the same va_list
usage, and interpretation of the arguments, according to the value of type
. The solution is to pass directly the va_list
as an argument to the callback function, making the implementation of the internal callback function obvious.
typedef void (* callback_func_t)(int type, va_list args);
My question is: is it good practice to define a callback function type that takes va_list
as argument? I can define my callback function type as above, but what are the pros and cons compared to the definition at the top?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我假设类型的数量是有限且已知的?如果是这样,为什么不使用枚举和联合来实现某种程度的类型安全,这也会顺便解决您的问题:
根据参数大小的差异,传递指针可能是一个好主意。您还可以提供一些宏来为回调调用提供更好的语法,但如果它们仅从您的库中调用,则可能不值得:
它本身就很短。
I assume there is a finite and known number of types? If so, why not use enumerations and unions for some level of type safety, which would incidentally also solve your problem:
Depending on the difference of argument sizes, it might be a good idea to pass a pointer instead. You could also provide some macros to provide a nicer syntax for callback invocation, but if they are only ever called from within your library, it might not be worth it:
is pretty short on its own.
我会选择
va_list
版本。用户已经在处理处理va_list
的可怕问题,因此您不会通过向他们隐藏来保存任何内容。另外,使用列表而不是尝试重新传递参数将减少堆栈的使用。如果您必须将参数重新传递给其函数,它将创建所有这些参数的新副本,但传递 va_list 类型将仅使用堆栈上已有的这些参数的实例。
最后,您的代码将变得更简单(如果我正确理解问题),并且您将使每个用户不必调用
va_start
和va_end
(这可能不会'对于大多数stdarg
的实现来说,不会改变其函数中的输出代码),但除此之外,它们都必须键入这些调用并且(取决于stdarg
在平台上的实现方式)他们需要确保在返回之前确实调用了va_end
(如果在出现错误的情况下提前返回,很容易错过)。I would go with the
va_list
version. The user is already dealing with the scariness of dealing withva_list
s so you don't save anything by hiding that from them.Additionally using the list rather than trying to repass the arguments will cut down on stack usage. If you had to repass the arguments to their function it would make a new copy of all of those arguments, but passing the
va_list
type will just use the instance of those arguments already on the stack.Finally, your code will both be simpler (if I'm understanding the problem correctly) and you will save each user from having to call
va_start
andva_end
(which probably won't change the output code in their function much for most implementations ofstdarg
) but otherwise they all have to type those calls and (depending on howstdarg
s are implemented on the platform) they will need to make sure they actually do callva_end
before returning (easy to miss if returning early in case of an error).