typesafe_cb
callback 回调函数
什么是callback function
如图(来自维基百科),回调函数提供了一种服务,可以由用户决定使用怎么样的服务(登记回调函数)。回调函数机制,提供了很大的灵活性,可以将库函数视为中间函数,借用回调函数完成个性化的服务。本文下面的内容会使用到ccan
中的一个安全回调函数库。
typesafe_cb.h
版权:
License: CC0 (Public domain)
Author: Rusty Russell <rusty@rustcorp.com.au>
该头文件是为实现安全的回调函数所编写的一系列的宏,包括
作者在_info
文件中写道:
If an expression exactly matches a given type, it is cast to the target type, otherwise it is left alone.
其根本目的在于,避免强制使用void*
指针破坏类型检查。
typesafe_cb_cast
/*
* 当表达式匹配给定类型时,进行类型转换
* dest_t:目标类型
* allow_t : 允许的类型
* expr : 需要转换类型的表达式
*/
#define typesafe_cb_cast(dest_t, allow_t, expr) \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(0?(expr):(expr)), \
allow_t), \
(dest_t)(expr), (expr))
此处使用了两个GNU扩展
int __builtin_types_compatible_p(type_a,type_b)
如果类型相同返回1
否则返回0
type __builtin_choose_expr(const_expr,expr_a,expr_b)
如果常量表达式值非0,则生成expr_a
,否则生成expr_b
。 这都是编译期行为。
这两个扩展的结合,可以一定程度实现泛型编程。
因此这个宏typesafe_cb_cast
的功能便很显然了。
由此产生的相关宏如下:
/*
* 三种允许的类型,typesafe_cb_cast 的三重嵌套
*/
#define typesafe_cb_cast3(dest_t,allow_t1,allow_t2,allow_t3,expr) \
typesafe_cb_cast(dest_t,allow_t1, \
typesafe_cb_cast(dest_t,allow_t2, \
typesafe_cast(dest_t,allow_t3,(expr))))
typesafe_cb
/*
* 如果回调函数参数匹配就进行类型转换
* @ret_t: 回调函数返回值类型
* @exp_t: 回调函数期待的(指针)类型
* @func: 需要类型转换的回调函数
* @arg: 传给回调函数的参数
*/
#define typesafe_cb(ret_t,exp_t,func,arg) \
typesafe_cb_cast(ret_t(*)(exp_t), \
ret_t(*)(__typeof__(arg)), \
(func))
此处调用的 typesafe_cb_cast
中的类型为函数指针
ret_t(*)(exp_t) :指向返回值为ret_t类型,参数为exp_t类型的函数的函数指针
这个宏在函数只有一个参数传入时能很好的工作。
if typeof(func)==ret_t(*)(__tyoeof__(arg))
then typeof(func)--->ret_t(*)(exp_t)
typesafe_cb_preargs
/*
* 对于有多个参数的函数,针对的是在arg之前有多个参数
* 比如 void(*fn)(int,void*arg)
*/
#define typesafe_cb_preargs(rtype, atype, fn, arg, ...) \
typesafe_cb_cast(rtype (*)(__VA_ARGS__, atype), \
rtype (*)(__VA_ARGS__, __typeof__(arg)), \
(fn))
仅仅是用了变参宏从而扩展了多参数回调函数的情况,举个例子
void _register_callback(void (*fn)(int, void *arg), void *arg);
#define register_callback(fn, arg) \
_register_callback(typesafe_cb_preargs( \
void, void *,(fn), (arg), int), \
(arg))
在这个例子中:
ret_t :void
allow_t: void*
fn: void (*fn)(int, void *arg)
arg: (arg)
...: int
那么对于多的参数在arg
之后也就有相应的情况了
#define typesafe_cb_postargs(rtype, atype, fn, arg, ...) \
typesafe_cb_cast(rtype (*)(atype, __VA_ARGS__), \
rtype (*)(__typeof__(arg), __VA_ARGS__), \
(fn))
应用
在ccan
的_info
中,作者给出了这样一个例子
#include "typesafe_cb.h"
#include <stdio.h>
#include <stdlib.h>
/*callback函数链*/
struct callback{
struct callback *next;
int value;
int(*callback)(int value,void *arg);
void *arg;
};
static struct callback *callbacks;
/*给定值与参数注册回调函数*/
static void _register_callback(int value,int(*cb)(int,void*),void *arg)
{
struct callback *new=malloc(sizeof(*new));
new->next=callbacks;
new->value=value;
new->callback=cb;
new->arg=arg;
callbacks=new;
}
/*
* exp_t:int(*)(__VA_ARGS__,void*)
* allow_t:int(*)(__VA_ARGS__,__typeof__(arg))
*/
#define register_callback(value,cb,arg)\
_register_callback(value,\
typesafe_cb_preargs(int ,void*,(cb),(arg),int),(arg))
/*通过value,查找callback函数*/
static struct callback *find_callback(int value)
{
struct callback * i;
for(i=callbacks;i;i=i->next){
if(i->value==value){
return i;
}
}
return NULL;
}
/* 定义回调函数的宏,注意此处并没有用void*指针
* 回调函数的类型
* int(*cb)(int,int*arg)
* */
#define def_callback(name,op)\
static int name(int val,int *arg)\
{\
printf("%s",#op);\
return val op *arg;\
}
def_callback(multiply,*);
def_callback(add, +);
def_callback(divide, /);
def_callback(sub, -);
def_callback(or,|);
def_callback(and,&);
def_callback(xor, ^);
def_callback(assign, =);
int main(int argc,char*argv[])
{
int i,run=1,num= argc > 1 ? atoi(argv[1]) : 0 ;
for (i = 1; i < 1024;) {
/*
* replacement: _register_callback(i++, (int (*)(int, void *)) (((add))), (&run))
* exp_t: int (*)(int, void *)
* allow_t: int (*)(int,__typeof__(arg))--->int (*)(int,int*)
* cb_t: int (*)(int,int*)
*/
register_callback(i++, add, &run);
register_callback(i++, divide, &run);
register_callback(i++, sub, &run);
register_callback(i++, multiply, &run);
register_callback(i++, or, &run);
register_callback(i++, and, &run);
register_callback(i++, xor, &run);
register_callback(i++, assign, &run);
}
printf("%i ", num);
while (run < 56) {
struct callback *cb = find_callback(num % i);
if (!cb) {
printf("-> STOP\n");
return 1;
}
num = cb->callback(num, cb->arg);
printf("->%i ", num);
run++;
}
printf("-> Winner!\n");
return 0;
}