C: Callback Function

typedef  void  (*callbackFun)(int a, int b);
struct exm {
int type;
callbackFun fun;
};

A pointer is a special kind of variable that holds the address of another variable. The same concept applies to function pointers, except that instead of pointing to variables, they point to functions. If you declare an array, say, int a[10]; then the array name awill in most contexts (in an expression or passed as a function parameter) “decay” to a non-modifiable pointer to its first element (even though pointers and arrays are not equivalent while declaring/defining them, or when used as operands of the sizeofoperator). In the same way, for int func();func decays to a non-modifiable pointer to a function. You can think of func as a const pointer for the time being.

But can we declare a non-constant pointer to a function? Yes, we can — just like we declare a non-constant pointer to a variable:

int  (*ptrFunc) ();

Here, ptrFunc is a pointer to a function that takes no arguments and returns an integer. DO NOT forget to put in the parenthesis, otherwise the compiler will assume that ptrFuncis a normal function name, which takes nothing and returns a pointer to an integer.

Let’s try some code. Check out the following simple program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
 
/* function prototype */
int  func( int int );
int  main( void )
{
     int  result;
     /* calling a function named func */
     result = func(10,20);       
     printf ( "result = %d\n" ,result);
     return  0;
}
 
/* func definition goes here */
int  func( int  x,  int  y)             
{
return  x+y;
}

As expected, when we compile it with gcc -g -o example1 example1.c and invoke it with./example1, the output is as follows:

result = 30

The above program calls func() the simple way. Let’s modify the program to call using a pointer to a function. Here’s the changed main() function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<stdio.h>
int  func( int int );
int  main( void )
{
     int  result1,result2;
     /* declaring a pointer to a function which takes
        two int arguments and returns an integer as result */
     int  (*ptrFunc)( int , int );
 
     /* assigning ptrFunc to func's address */                     
     ptrFunc=func;
 
     /* calling func() through explicit dereference */
     result1 = (*ptrFunc)(10,20);
 
     /* calling func() through implicit dereference */         
     result2 = ptrFunc(10,20);               
     printf ( "result1 = %d result2 = %d\n" ,result1,result2);
     return  0;
}
 
int  func( int  x,  int  y)
{
     return  x+y;
}

The output has no surprises:

result1 = 30 result2 = 30

A simple callback function

At this stage, we have enough knowledge to deal with function callbacks. According toWikipedia, “In computer programming, a callback is a reference to executable code, or a piece of executable code, that is passed as an argument to other code. This allows a lower-level software layer to call a subroutine (or function) defined in a higher-level layer.”

Let’s try one simple program to demonstrate this. The complete program has three files:callback.creg_callback.h and reg_callback.c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* callback.c */
#include<stdio.h>
#include"reg_callback.h"
 
/* callback function definition goes here */
void  my_callback( void )
{
     printf ( "inside my_callback\n" );
}
 
int  main( void )
{
     /* initialize function pointer to
     my_callback */
     callback ptr_my_callback=my_callback;                           
     printf ( "This is a program demonstrating function callback\n" );
     /* register our callback function */
     register_callback(ptr_my_callback);                             
     printf ( "back inside main program\n" );
     return  0;
}
1
2
3
/* reg_callback.h */
typedef  void  (*callback)( void );
void  register_callback(callback ptr_reg_callback);
1
2
3
4
5
6
7
8
9
10
11
/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"
 
/* registration goes here */
void  register_callback(callback ptr_reg_callback)
{
     printf ( "inside register_callback\n" );
     /* calling our callback function my_callback */
     (*ptr_reg_callback)();                                  
}

Compile, link and run the program with gcc -Wall -o callback callback.c reg_callback.c and ./callback:

This is a program demonstrating function callback
inside register_callback
inside my_callback
back inside main program

The code needs a little explanation. Assume that we have to call a callback function that does some useful work (error handling, last-minute clean-up before exiting, etc.), after an event occurs in another part of the program. The first step is to register the callback function, which is just passing a function pointer as an argument to some other function (e.g., register_callback) where the callback function needs to be called.

We could have written the above code in a single file, but have put the definition of the callback function in a separate file to simulate real-life cases, where the callback function is in the top layer and the function that will invoke it is in a different file layer. So the program flow is like what can be seen in Figure 1.

 

Program flow

Figure 1: Program flow

The higher layer function calls a lower layer function as a normal call and the callback mechanism allows the lower layer function to call the higher layer function through a pointer to a callback function.

This is exactly what the Wikipedia definition states.

Use of callback functions

One use of callback mechanisms can be seen here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/ * This code catches the alarm  signal  generated from the kernel
     Asynchronously */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
 
struct  sigaction act;
 
/* signal handler definition goes here */
void  sig_handler( int  signo, siginfo_t *si,  void  *ucontext)
{
    printf ( "Got alarm signal %d\n" ,signo);
    /* do the required stuff here */
}
 
int  main( void )
{
     act.sa_sigaction = sig_handler;
     act.sa_flags = SA_SIGINFO;
 
     /* register signal handler */
     sigaction(SIGALRM, &act, NULL);  
     /* set the alarm for 10 sec */       
     alarm(10);   
     /* wait for any signal from kernel */                                        
     pause();  
     /* after signal handler execution */                                             
     printf ( "back to main\n" );                     
     return  0;
}

Signals are types of interrupts that are generated from the kernel, and are very useful for handling asynchronous events. A signal-handling function is registered with the kernel, and can be invoked asynchronously from the rest of the program when the signal is delivered to the user process. Figure 2 represents this flow.

 

Kernel callback

Figure 2: Kernel callback

Callback functions can also be used to create a library that will be called from an upper-layer program, and in turn, the library will call user-defined code on the occurrence of some event. The following source code (insertion_main.cinsertion_sort.c andinsertion_sort.h), shows this mechanism used to implement a trivial insertion sort library. The flexibility lets users call any comparison function they want.

1
2
3
4
/* insertion_sort.h */
 
typedef  int  (*callback)( int int );
void  insertion_sort( int  *array,  int  n, callback comparison);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/* insertion_main.c */
 
#include<stdio.h>
#include<stdlib.h>
#include"insertion_sort.h"
 
int  ascending( int  a,  int  b)
{
     return  a > b;
}
 
int  descending( int  a,  int  b)
{
     return  a < b;
}
 
int  even_first( int  a,  int  b)
{
     /* code goes here */
}
 
int  odd_first( int  a,  int  b)
{
     /* code goes here */
}
 
int  main( void )
{
     int  i;
     int  choice;
     int  array[10] = {22,66,55,11,99,33,44,77,88,0};
     printf ( "ascending 1: descending 2: even_first 3: odd_first 4: quit 5\n" );
     printf ( "enter your choice = " );
     scanf ( "%d" ,&choice);
     switch (choice)
     {
         case  1:
             insertion_sort(array,10, ascending);
             break ;
         case  2:
             insertion_sort(array,10, descending);
          case  3:
             insertion_sort(array,10, even_first);
             break ;
         case  4:
             insertion_sort(array,10, odd_first);
             break ;
         case  5:
             exit (0);
         default :
             printf ( "no such option\n" );
     }
 
     printf ( "after insertion_sort\n" );
     for (i=0;i<10;i++)
         printf ( "%d\t" , array[i]);
     printf ( "\n" );
      return  0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* insertion_sort.c */
 
#include"insertion_sort.h"
 
void  insertion_sort( int  *array,  int  n, callback comparison)
{
     int  i, j, key;
     for (j=1; j<=n-1;j++)
     {
         key=array[j];
         i=j-1;
         while (i >=0 && comparison(array[i], key))
         {
             array[i+1]=array[i];
             i=i-1;
         }
         array[i+1]=key;
     }
}

Wrapping it up

The tutorial describes how callbacks are implemented in C. In C++ the same goal can be achieved through template functors. Although callbacks are very hard to trace in large real-world programs, life should be a little easier after going through this introductory article. Just remember, callback is nothing but passing the function pointer to the code from where you want your handler/callback function to be invoked. Happy hacking!

转载于:https://www.cnblogs.com/faeriesoft/p/4200238.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值