Definition and writing of callback function in C language

1 Definition and usage occasions

The callback function means that the user defines a function by himself, implements the program content of this function, and then passes this function (entry address) as a parameter into the function of others (or the system), and the function of the other person (or system) is executed at runtime. function to call. The function is implemented by you, but it is called by other people's (or system) functions by passing parameters at runtime, which is the so-called callback function. To put it simply, it is to call back the function you implemented during the running of other people's functions.

This design allows low-level code to call subroutines defined at the high-level (as shown in Figure 1-1). In the C language, the callback function is mainly implemented by means of function pointers.

 

Figure 1-1 The call result of the callback function in the software system

 

Callbacks can be used in a variety of ways: [1]

For example, suppose there is a function that reads a configuration file and sets the corresponding options from the contents of the file. If the options are identified by a hash function, having the function accept a callback allows for more programming flexibility: the caller of the function can use the desired hashing algorithm, which consists of a method that converts the option name to A callback function implementation of the hash value; thus, the callback allows the caller of the function to adjust the behavior of the original function at runtime .

Another use of callbacks is in handling semaphores. For example, a POSIX program may not want to terminate immediately when it receives a SIGTERM signal; in order to ensure that everything runs smoothly, the program can register a cleanup function as a callback corresponding to the SIGTERM signal.

Callbacks can also be used to control whether a function acts or not: Xlib allows custom predicates (NSPredicate) to be used to determine whether a program wishes to handle a particular event.

copy code
#include <iostream> 
#include <string> using
 namespace std; 
typedef void (*FP)( char * s);     // struct means function pointer void f1( char * s){cout<< s;}
 void f2( char * s){cout<< s;}
 void f3( char * s){cout<< s;} int main( int argc, char * argv[]) 
{ int funcselector= 0 ;            // Define an integer for Control the function void to be executed 




    
    * a[]={f1,f2,f3};    // A pointer array is defined, here a is an ordinary pointer 
    a[ 0 ]( " Hello World!\n " ); // Compile error, pointer array cannot be used The standard way to call the function 

    FP f[] ={f1,f2,f3};       // Define an array of function pointers, where f is a function pointer 
    
    /* Handle of funselector */        // Here is used to handle funselector, Control the function to be executed 
    f[funselector]( " Hello World!\n " ); // Correct, the subscript operation of the array of function pointers can make indirect call of the function 
    
    return  0 ; 
}     
copy code

The above example shows part of the function of the callback function. Here f1, f2, and f3 represent three functions with different functions (for example: f1 realizes the maximum value output, f2 realizes the average value output, and f3 realizes the minimum value output). To summarize some advantages of callback functions:

Using funcselector as a marker, selecting the function to be executed is very convenient to control the flow and process of the function.

f1,f2,f3三个特定函数模块化明显,便于设计者去维护、修改。如图1-1所示,很多系统中software library会完全封装,这样开发者只能通过回调函数去修改函数功能。

分析函数思路更加清晰,在lwip中大量使用回调函数,开发者可以根据回调函数的调用流程分析系统结构。

 

2 结构解析

回调函数主要结构有三部分组成:主函数、调用函数和被调函数(如图1-1所示)。C语言中,被调函数通常以函数指针(指向对应函数的入口地址)的形式出现。 

这里给出一个最简单的回调函数结构,并解析相关数据结构。

copy code
//定义回调函数
void PrintfText() 
{
    printf("Hello World!\n");
}

//定义实现回调函数的"调用函数"
void CallPrintfText(void (*callfuct)())
{
    callfuct();
}

//实现函数回调
int main(int argc,char* argv[])
{
    CallPrintfText(PrintfText);
    return 0;
}
copy code

调用函数向其函数中传递 void (*callfuct)(void) 这是一个 void callfuct(void) 函数的入口地址,即PC指针可以通过移动到该地址执行void callfuct(void) 函数,可以通过类比数组来理解。

实现函数调用中,函数调用了“调用函数”,再在其中进一步调用被“调用函数”。相比于主函数直接调用“被调函数”,这种方法为使用者,而不是开发者提供了灵活的接口。另外,函数入口可以像变量一样设定同样为开发者提供了灵活性。

 

3 实例分析

 这里分析一个lwip中较为复杂的回调函数使用范例:

copy code
void httpd_init(void)
{
    struct tcp_pcb * pcb;
    pcb = tcp_new();
    tcp_bind(pcb,IP_ADDR_ANY,80);
    pcb = tcp_listen(pcb);
    tcp_accept(pcb, http_accept);        
}

void
tcp_accept(struct tcp_pcb * pcb, err_t(* accept)(void *arg, struct tcp_pcb *newpcb, err_t err))

static err_t http_accept(void *arg, struct tcp_pcb * pcb, err_t err)
{
    /* set the prio of callback function, important */
    tcp_setprio(pcb, TCP_PRIO_MIN);
    tcp_recv(pcb, http_recv);
    return ERR_OK;    
}    

void 
tcp_recv(struct tcp_pcb * pcb, err_t (* recv)(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err))

static err_t http_recv(void *arg, struct tcp_pcb * pcb, struct pbuf *p, err_t err)
{
    /* html handler by user's definition */
    /* use tcp_write(pcb, message, sizeof message, 0) to send message */
}
copy code

Guess you like

Origin blog.csdn.net/qq_27568125/article/details/55094616