您的位置:首页 > 编程语言 > C语言/C++

c语言 ,回调函数[个人理解]

2014-11-20 14:17 253 查看
回调函数:把需要调用的方法的指针pCallBackFuncX作为参数传递给一个函数UsrFunction,以便该UsrFunction函数在处理相似事件的时候可以灵活的使用不同的方法。

以在flash中存储有序的交易记录为例:

交易记录列表内容如下所示我们要对其排序然后存储到flash当中去。这个排序函数写作: int SortReclist(RECLIST_ST * reclist);

typedef struct {
char cityCode[2] ;            //城市代码 + 应用序列号
char usrName[10];
unsigned char tradeTime[7];      //交易时间【bcd】 年 月 日 时 分 秒
unsigned char TerminalCode[6];   //终端硬件序列号
unsigned char reserve[1];
} RECS_ST ;//交易记录

typedef struct {
unsigned char count[2] ;
unsigned char max[2] ;
unsigned char data[N_MAX_RECORD][SIZE_OF_RECS];// sizeof(RECS_ST); //要对这里的数据进行排序、去重。
unsigned char check ;
} RECLIST_ST;//交易记录列表


我们使用冒泡排序的方法对其中的数据进行排序,我们得到一组无序数据,我们不知道这组数据有多少个数据项、一个数据项多大、怎么比较他们的大小、如何交换数据项的位置。

我们可以设计如下,我们需要传入的参数不仅是数据p_data_in、n_elements、element_size,还有方法cmp_func、mem_swap

bubble_sort(void* p_data_in ,                  //pointer to data to sort  待排列的数据
int n_elements ,                   //number of elements        待排列的数据个数
int element_size,                  //size of each element == sizeof(RECS_ST) 数据宽度
(CALLBACK_AFC_DATA_CMP) cmp_func , //cmp_func: pointer to comparison function  判定其排列次序的方法,这个是根据需要变换的。
(CALLBACK_MEM_SWAP) mem_swap) ;    //mem_swap: pointer to swap function or NULL 内存交换的方法

    typedef void (*CALLBACK_MEM_SWAP)(void *, void *, int n_bytes)  ;
    typedef int (*CALLBACK_AFC_DATA_CMP)(const void *, const void *)  ;



bubble_sort的实现如下:


//ret==1: error
//ret==0: ok
int bubble_sort(void *base ,           //pointer to data to sort
int n_elements ,   //number of elements
int size_element ,      //size of each element
CALLBACK_AFC_DATA_CMP cmp_func ,//cmp_func: pointer to comparison function
CALLBACK_MEM_SWAP mem_swap )//mem_swap: pointer to swap function or NULL
{
int i, j, flag , cmp ;
if( base==0 || cmp_func==0 || mem_swap==0 ){
return 1 ;
}

for (i=0; i<n_elements-1; i++)
{
flag = 0;
for (j=0; j<n_elements-1-i; j++)
{
cmp = cmp_func( ((char *)base+size_element*j ) ,  ((char *)base+size_element*(j+1)) )  ;
if ( cmp > 0 )
{
mem_swap( ((char *)base+j*size_element) ,  ((char *)base+(j+1)*size_element)  , size_element );
flag = 1;
}
}
if ( !flag ) {
break ;
}
}
return 0;
}


int SortReclist(RECLIST_ST * reclist)
{
int ret;
unsigned char count[2];

do{
memcpy(count , reclist->count , sizeof( count )); //这里是解决mdk对外部全局变量不能正确强转的解决办法,无视

bubble_sort(reclist->data ,  //pointer to data to sort
*(signed short*)count ,   //number of elements
sizeof( RECS_ST ) ,  //size of each element
(CALLBACK_AFC_DATA_CMP) reclistcmp , //cmp_func: pointer to comparison function
(CALLBACK_MEM_SWAP)mem_swap) ; //mem_swap: pointer to swap function or NULL
//暂且无视排序中去重的需求
}while(ret!=0) ;

return 0;
}


到底采用何种方式来判断数据的大小,就根据实际需要了。下面是一个实现,只比较城市代码:


int reclistcmp(char *before ,char *next )
{
int ret ;
ret = memcmp(before ,next , 2);  //citycode[2] 简单判定
return ret ;
}


好处是,以回调函数的方式,在写主体框架时,可以不用知道某种功能的具体实现,而只是提出这个需求。

【写框架的人并不是完全不关心具体实现,不合理的框架设计导致具体功能无法按照给定的接口来实现。】

这样在处理具体业务时有更高的灵活性,这个功能的具体实现甚至可以交给其他人去完成。

//-----------------------------------------------------------------------------------------------------------------------------------------------//

另外,linux设备驱动函数注册也是回调的思想。linux内核并不知道我们具体设备的操作方式,它是这样对设备进行操作的,先规定操作函数的形式:

struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);//指定函数接口形式

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*dir_notify)(struct file *filp, unsigned long arg);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
};


然后驱动设计者按照规定的形式实现ioctl。

/* 应用程序对设备文件/dev/leds执行ioctl(...)时,
* 就会调用s3c24xx_leds_ioctl函数
*/
static int s3c24xx_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,//ON / OFF
unsigned long arg)//n_led
{
if (arg > 4) {
return -EINVAL;
}

switch(cmd) {
case IOCTL_LED_ON:
// 设置指定引脚的输出电平为0
s3c2410_gpio_setpin(led_table[arg], 0);
return 0;

case IOCTL_LED_OFF:
// 设置指定引脚的输出电平为1
s3c2410_gpio_setpin(led_table[arg], 1);
return 0;

default:
return -EINVAL;
}
}


驱动设计者再注册操作集,包括了ioctl。

static struct file_operations s3c24xx_leds_fops = {
.owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open   =   s3c24xx_leds_open,
.ioctl  =   s3c24xx_leds_ioctl,
};

//in moudle_init:
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);


这样,在打开操作设备时,系统使用到了驱动设计者的设备操作方法。

扩展阅读:

1.回调机制

  http://blog.sina.cn/dpool/blog/s/blog_77c632410101cjty.html

2.简单的c++回调函数设计方法(一)

  回调实现分层设计。

  http://blog.chinaunix.net/uid-21222282-id-1829257.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: