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

  • 相关阅读:
    Pascal's Triangle II
    Pascal's Triangle
    Best Time to Buy and Sell Stock II
    Best Time to Buy and Sell Stock
    Populating Next Right Pointers in Each Node
    path sum II
    Path Sum
    [转载]小波时频图
    [转载]小波时频图
    [转载]Hilbert变换及谱分析
  • 原文地址:https://www.cnblogs.com/mylinux/p/4110517.html
Copyright © 2011-2022 走看看