標籤

2012年4月15日 星期日

Callback Function


什麼是Callback function?
如果在程式中使用某個function, 我們可稱作”call” function,這種行為是主動的,  而callback function是被動的, 也就是當特定事件發生時(例如觸控發生, 或某手勢被偵測),callback function才會被呼叫.

而Callback在C/C++實現的方式, 就是利用function pointer(函式指標), 例如A程式想要B程式發生某些事件時, 能通知A程式. 利用Callback機制的話, A程式必須傳一個function pointer給B程式,讓B程式可以使用這個function pointer來通知A程式.

*其實這個有點像OBJECTIVE C的DELEGATE:
http://seruziu.blogspot.com/
不過OBJECTIVE C的CALLBACK 通常是用SELECTOR

function pointer(函式指標)宣告的方式如下:


返回類型 (* 函式指標名稱) (參數列表);


example:
int (* GetPointCallback)(int x, int y);


Callback function的使用案例
例如: 我們在設計一個觸控繪圖的應用程式時, 通常不會讓程式不斷地向系統要觸控點的資料, 而是希望當系統偵測到觸控發生時, 才會丟回觸控點的資料, 而這個丟回觸控點的行為, 就可以設計成callback function. 其示意圖如下所示:


Callback function 範例程式
TouchAPI.h
  1. #ifdef  TOUCHAPI_EXPORTS  
  2.  #define TOUCH_API __declspec(dllexport)  
  3. #else  
  4.  #define TOUCH_API __declspec(dllimport)  
  5. #endif  
  6.   
  7. // 定義觸控點的相關資訊  
  8. struct TouchPoint  
  9. {  
  10.  int X;   
  11.  int Y;   
  12. };  
  13.   
  14. // 定義callback function的prototype  
  15. // CB_TOUCH_FUNC是一個pointer,指向具有一個TouchPoint類型,且回傳void型的函数  
  16. // 前面有個typedef,所以現在CB_TOUCH_FUNC是這種類型的别名  
  17. typedef void (_stdcall *CB_TOUCH_FUNC) (TouchPoint);  
  18.   
  19. // 定義了一個RegisterCBTouchFunc的函式,可傳入一個具有CB_TOUCH_FUNC型別的callback function  
  20. TOUCH_API void RegisterCBTouchFunc(CB_TOUCH_FUNC callback);  

TouchAPI.cpp
  1. #include "stdafx.h"  
  2. #include "TouchAPI.h"  
  3.   
  4. void DetectTouchFromSystem(CB_TOUCH_FUNC callback)  
  5. {  
  6.     // 當系統取得touch資料時,  
  7.     // 就會呼叫一次pCB_TOUCH_FUNC參數所指到function一次  
  8.     TouchPoint tp;  
  9.     tp.X = 5;  
  10.     tp.Y = 10;  
  11.     callback(tp);  
  12. }  
  13.   
  14. void RegisterCallbackTouchFunc(CB_TOUCH_FUNC callback)  
  15. {  
  16.  DetectTouchFromSystem(callback);  
  17. }  
TouchPaint.cpp
  1. #include "stdafx.h"  
  2. #include "conio.h"  
  3. #include "TouchAPI.h"  
  4.    
  5. // 定義了一個名為GetTouch的函式, 也就是callback函式  
  6. // 它的prototype必須與TouchAPI的CB_TOUCH_FUNC宣告一致  
  7. void _stdcall GetTouch(TouchPoint point)  
  8. {  
  9.  printf("x=%d, y=%d", point.X, point.Y);  
  10. }  
  11.    
  12. int main(void)  
  13. {  
  14.     //註冊一個callback function  
  15.     RegisterCBTouchFunc(GetTouch);  
  16.     getch();  
  17.     return 0 ;  
  18. };  


Conclusion:
所以Callback function就是等著被呼叫的function
通常會使用在interrupt handler(中斷處理), 或一些event handler(事件處理)
這些callback function一般會在你的主程式向系統註冊它們的address
當某些硬體或軟體中斷發生時, 或某些事件發生時
系統就會去呼叫這些Callback function
所以, callback function並不是直接讓你的程式呼叫的
舉一個C的例子來說:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <fcntl.h>  
  4. #include <sys/types.h>  
  5. #include <sys/stat.h>  
  6. #include <unistd.h>  
  7. #define DEFAULT_BLOCK_SIZE (4096)  
  8.    
  9. // 定義callback function的prototype。  
  10. typedef void (* CALLBACK) (int);  
  11.    
  12. // 定義了一個名為ShowPercentage的函式。這就是我們的callback函式。  
  13. // 他的prototype必須與前面的CALLBACK宣告一致。  
  14. void ShowPercentage(int percentage)  
  15. {  
  16.     fprintf(stderr, "%dn%nn", percentage);  
  17. }  
  18.    
  19. // 定義了一個CopyFile的函式,這個函式會將參數source所指定檔案複製到  
  20. // target參數所指定的檔案去。而且每複製DEFAULT_BLOCK_SIZE數量的資料  
  21. // 就會呼叫一次callback參數所指到function一次。  
  22. void CopyFile(const char *source, const char *target, CALLBACK callback)  
  23. {  
  24.     char buf[DEFAULT_BLOCK_SIZE] ;  
  25.     struct stat fs ;  
  26.     int fdSrc, fdTrg ;  
  27.     int readBytes = 0, totalReadBytes = 0, percentage = 0;  
  28.     fdSrc = open(source, O_RDONLY);  
  29.     fstat(fdSrc, &fs);  
  30.     fdTrg = open(target,O_CREAT|O_TRUNC|O_RDWR);  
  31.     // 主要複製資料的迴圈  
  32.     while((readBytes=read(fdSrc, buf, DEFAULT_BLOCK_SIZE)) > 0)  
  33. {  
  34.         write(fdTrg, buf, readBytes);  
  35.         totalReadBytes += readBytes ;  
  36.         //複製資料後就呼叫callback函式去做顯示百分比的動作。  
  37.         callback( (totalReadBytes*100)/fs.st_size);  
  38. }  
  39.     close(fdTrg);  
  40.     close(fdSrc);  
  41. }  
  42.    
  43. int main(void)  
  44. {  
  45.     // 這個範例中只是利用callback來顯示目前的進度。  
  46.     // 實際上我們可以利用callback來做更多的動作。  
  47.     CopyFile("A.TXT""B.TXT", ShowPercentage);  
  48.     return 0 ;  
  49. }  

1 則留言:

  1. TouchAPI.cpp裡的

    void RegisterCallbackTouchFunc(CB_TOUCH_FUNC callback)
    {
    DetectTouchFromSystem(callback);
    }

    為甚麼還要多一層函式? 直接用DetectTouchFromSystem(callback)是不是就可以了?

    回覆刪除