Funcpointer and Callback

This article mainly talk about the using of the function pointer and the call back function in c/c++.

fuction pointer

what is the type of the func?

1
int(*)(char a)

first part “int” means the return value of the function is the type of int, the second part “*” means this is a function pointer, the third part “char a” means that the input parameter of the function is a char character.

function pointer declaration:

1
returntype (* functionptrname)(arguments...)

function pointer 指向函数的首地址,也就是指向一段可以执行的代码的首地址,但是并非仅仅如此,因为还要和输入参数以及返回参数相关联。

下面的例子是一个基本的function pointer的定义以及使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "stdio.h"
int func(char a)
{
printf("%d\n", a);
return 0;
}
int main()
{
int (*funcptr)(char a);
funcptr = func;
funcptr('a');
return 0;
}

可以看到从函数名称调用和从指针调用效果是一致的。这里的函数指针是可以指向函数所在地址的指针,但并不是任何函数都可以,必须要是形参为char a 并且返回值为int的函数才行。如果将上面的指针指向一个其他类型的指针就会有如下的错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include "stdio.h"
int funca(char a)
{
printf("%d\n", a);
return 0;
}
int funcb(int a)
{
printf("test charb\n");
return 0;
}
int main()
{
int (*funcptr)(char a);
funcptr = funca;
funcptr('a');
funcptr = funcb;
return 0;
}
./funcp.c:22:13: warning: incompatible pointer types assigning to 'int (*)(char)' from 'int (int)'
[-Wincompatible-pointer-types]
funcptr = funcb;
^ ~~~~~
1 warning generated.

那么这样做和直接调用函数相比的好处在什么地方?和直接通过函数名称调用excutable code segmentation相比的好处在哪里。

callback

在基于event的编程模式中,call back会被经常使用到。总的来说分为两部分,函数注册+函数调用。在programming的时候先把函数的地址注册进某个framework中,之后framework在运行的时候,当某些事件被触发了之后就调用这个对应的注册进来的函数。本质上来讲,callback就是先将函数的地址传递进某个地方,再等到未来合适的某个时机调用这个函数。

下面这个例子是callback的一个使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef int (*JobCompleteCallBack)(char a);
int func(char a)
{
printf("job complete, input parameter %c\n", a);
return 0;
}
void LongJob(char job, JobCompleteCallBack jobfunc)
{
printf("do the long running job %c\n", job);
jobfunc('b');
}
int main()
{
LongJob('a', func);
return 0;
}

可以看到,这里首先使用typedef重新定义了函数指针从而增强了代码的可读性,在LongJob函数的时候,其中的一个形参数是函数指针,在某些逻辑执行完成之后会调用这个callback函数。需要注意的是对于callback的代码一定要按照callback的场景来理解,问自己,这个背后的逻辑是想说”在某个条件触发的时候就执行某个函数”,这个时候一定要跳出来整体看,要是盯着代码一行一行地往下走就容易把自己弄晕。

在实际的case中,常常通过function table的形式只用callback。function table的key值是具体的event类型,value值是具体的预先注册好的function table,两者对应起来就可以通过某些具体的event来触发对应的函数了。这个时候再调用函数就叫callback。这种通过事件来触发函数的机制可以理解为event-driven trigger。

下面的例子中使用array数组来模拟一个简单的call back function table,实际上使用map会有更高的效率。下面的例子就是通过标准输入来触发不同的函数,这就是大部分event-driven programming paradigm的原型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include "stdio.h"
typedef void (*eventFunc)();
struct MYSTRUCT
{
int eventTypeLast;
eventFunc funcTable[3];
} myGlobalStruct;
void func0()
{
printf("triggured by event 0\n");
}
void func1()
{
printf("triggured by event 1\n");
}
void func2()
{
printf("triggured by event 2\n");
}
void Init()
{
myGlobalStruct.funcTable[0] = func0;
myGlobalStruct.funcTable[1] = func1;
myGlobalStruct.funcTable[2] = func2;
}
int main()
{
Init();
int eventNow;
while (1)
{
scanf("%d", &eventNow);
if (eventNow >= 0 && eventNow < 3)
{
myGlobalStruct.eventTypeLast = eventNow;
myGlobalStruct.funcTable[eventNow]();
}else{
break;
}
}
return 0;
}

reference

https://www.youtube.com/watch?v=47m7yhaKhgM