zoukankan      html  css  js  c++  java
  • c语言 ,回调函数[个人理解]

    回调函数:把需要调用的方法的指针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

  • 相关阅读:
    Qt中的 Size Hints 和 Size Policies
    __declspec,__cdecl,__stdcall区别和作用
    深入理解DLL文件
    TCP/IP TIME_WAIT状态原理
    Linux 网络编程 高级套接字
    OpenCV 图像处理学习笔记(一)
    C++运算符重载的规则
    WinSock异步IO模型之Select
    ASSER、VERIFY、TRACE详解
    VC++ 网络编程总结(二)
  • 原文地址:https://www.cnblogs.com/mylinux/p/4110517.html
Copyright © 2011-2022 走看看