关于回调函数
1.什么是回调函数。
在freerdp的项目中看到,几乎所有的绘制工作都是通过回调函数完成的。究竟什么是回调函数呢?要了解回调函数,还得中函数指针说起。那函数指针又是什么呢?它跟指针函数有什么关系呢?请看下面:
一般我们是这样声明一个函数的,
int func(int params, ...);
相信大家对函数最熟悉不过了,这是个返回值为int的函数。稍微变形一下就成了指针函数了,请看:
int* func(int params, ...);
一个函数不仅可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。当一个函数的返回值是一个指针时,该函数就是一个指针函数。那跟指针函数有什么关系呢?先看看指针函数的定义吧。
在程序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。
int func(int x); /* 声明一个函数 */
int (*f) (int x); /* 声明一个函数指针 */
f=func; /* 将func函数的首地址赋给指针f */
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
2.为什么要使用回调函数
因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。
如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell排序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。
回调可用于通知机制,例如,有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,settimer() api使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。
不管怎么说,回调函数是继续自c语言的,因而,在c++中,应只在与c代码建立接口,或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在c++中应使用虚拟方法或函数符(functor),而不是回调函数。
3.回调函数的实现
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <malloc.h>
/*
file: darray.c:回调函数冒泡排序函数实现
author: ecorefeng
created on 2010年8月
*/
/*
*定义一个用于自动测试的宏(关于自动测试请我的博客文章《自动测试的优劣》)
*/
#define return_val_if_fail(p, val)\
if(!(p)){printf("%s:%d"#p" failed", func, line); return val; }
/*
*定义一个回调函数原型,用于回调
*/
typedef int (*compfunc)(void *ctx, void *data);
/*
*功能:实现冒泡排序
*参数:array:要排序的数组 compfunc:回调的用于比较的函数 array_len:数组长度
*返回:
*/
int darray_b_sort(void **array,compfunc compfunc, int array_len)
{
return_val_if_fail(array !=null&&compfunc !=null, 1);
int len = 0;
int max = 0;
int index = 0;
for(len = array_len - 1; len > 0; len--)
{
for(index = 1, max = 0; index < len; index++)
{
if(compfunc(array[index], array[max]) > 0)
{
max = index;
}
}
if(compfunc(array[max], array[len]) > 0)
{
void *data = array[max];
array[max] = array[len];
array[len] = data;
}
//int i = 0;
//for(i = 0; i < 6; i++)
//{
//printf("%d\n",array[i]);
// assert(array[i] >= array[i-1]);
//}
//printf(".......................\n");
}
return 0;
}
/*
*功能:随机生成一个数组
*参数:num:数组长度
*返回:数组指针
*/
static void **int_array_create(int num)
{
int i = 0;
int *array = (int *)malloc(sizeof(int) * num);
for(i = 0; i < num; i++)
{
array[i] = rand()%100;
printf("%d\t",array[i]);
}
printf("***********************\n");
return(void **)array;
}
/*
*功能:实现比较
*参数:compfunc:回调的用于比较的函数 num:数组长度
*返回:数组指针
*/
static void sort_test_asc_or_desc(compfunc compfunc, int num)
{
void **array = int_array_create(num);
darray_b_sort(array, compfunc, num);
int i = 0;
for(i = 0; i < num; i++)
{
printf("%d\t",(int)array[i]);
// assert(array[i] >= array[i-1]);
}
free(array);
}
/*
*功能:实现比较函数(升序)
*参数:指针
*返回:比较结果:大于0、小于0;
*/
int int_sort_compfunc_asc(void *num1, void *num2)
{
return (int)num1 - (int)num2;
}
/*
*功能:实现比较函数(降序)
*参数:指针
*返回:比较结果:大于0、小于0;
*/
int int_sort_compfunc_desc(void *num1, void *num2)
{
return (int)num2 - (int)num1;
}
int main(int argc, char *argv[])
{
sort_test_asc_or_desc(int_sort_compfunc_asc, 20);
printf("................\n");
sort_test_asc_or_desc(int_sort_compfunc_desc, 10);
return 0;
}
回调函数的形式实现的升降序的冒泡排序算法例子。
感谢ecorefeng提供的程序例子。
回头看看freerdp的项目,正是由于回调函数的使用,使得freerdp的移植变得简单多了。例如我是用skia库实现rdp的绘制函数时,不需要了解整个的调用过程,我只需要实现回调函数调用的那些函数,然后注册这些回调函数就可以了。
由于知识有限,难免有错,欢迎大家指正,谢谢。
摘自 hopetribe