什麼是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
- #ifdef TOUCHAPI_EXPORTS
- #define TOUCH_API __declspec(dllexport)
- #else
- #define TOUCH_API __declspec(dllimport)
- #endif
- // 定義觸控點的相關資訊
- struct TouchPoint
- {
- int X;
- int Y;
- };
- // 定義callback function的prototype
- // CB_TOUCH_FUNC是一個pointer,指向具有一個TouchPoint類型,且回傳void型的函数
- // 前面有個typedef,所以現在CB_TOUCH_FUNC是這種類型的别名
- typedef void (_stdcall *CB_TOUCH_FUNC) (TouchPoint);
- // 定義了一個RegisterCBTouchFunc的函式,可傳入一個具有CB_TOUCH_FUNC型別的callback function
- TOUCH_API void RegisterCBTouchFunc(CB_TOUCH_FUNC callback);
TouchAPI.cpp
- #include "stdafx.h"
- #include "TouchAPI.h"
- void DetectTouchFromSystem(CB_TOUCH_FUNC callback)
- {
- // 當系統取得touch資料時,
- // 就會呼叫一次pCB_TOUCH_FUNC參數所指到function一次
- TouchPoint tp;
- tp.X = 5;
- tp.Y = 10;
- callback(tp);
- }
- void RegisterCallbackTouchFunc(CB_TOUCH_FUNC callback)
- {
- DetectTouchFromSystem(callback);
- }
TouchPaint.cpp
- #include "stdafx.h"
- #include "conio.h"
- #include "TouchAPI.h"
- // 定義了一個名為GetTouch的函式, 也就是callback函式
- // 它的prototype必須與TouchAPI的CB_TOUCH_FUNC宣告一致
- void _stdcall GetTouch(TouchPoint point)
- {
- printf("x=%d, y=%d", point.X, point.Y);
- }
- int main(void)
- {
- //註冊一個callback function
- RegisterCBTouchFunc(GetTouch);
- getch();
- return 0 ;
- };
Conclusion:
所以Callback function就是等著被呼叫的function
通常會使用在interrupt handler(中斷處理), 或一些event handler(事件處理)
這些callback function一般會在你的主程式向系統註冊它們的address
當某些硬體或軟體中斷發生時, 或某些事件發生時
系統就會去呼叫這些Callback function
所以, callback function並不是直接讓你的程式呼叫的
舉一個C的例子來說:
- #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #define DEFAULT_BLOCK_SIZE (4096)
- // 定義callback function的prototype。
- typedef void (* CALLBACK) (int);
- // 定義了一個名為ShowPercentage的函式。這就是我們的callback函式。
- // 他的prototype必須與前面的CALLBACK宣告一致。
- void ShowPercentage(int percentage)
- {
- fprintf(stderr, "%dn%nn", percentage);
- }
- // 定義了一個CopyFile的函式,這個函式會將參數source所指定檔案複製到
- // target參數所指定的檔案去。而且每複製DEFAULT_BLOCK_SIZE數量的資料
- // 就會呼叫一次callback參數所指到function一次。
- void CopyFile(const char *source, const char *target, CALLBACK callback)
- {
- char buf[DEFAULT_BLOCK_SIZE] ;
- struct stat fs ;
- int fdSrc, fdTrg ;
- int readBytes = 0, totalReadBytes = 0, percentage = 0;
- fdSrc = open(source, O_RDONLY);
- fstat(fdSrc, &fs);
- fdTrg = open(target,O_CREAT|O_TRUNC|O_RDWR);
- // 主要複製資料的迴圈
- while((readBytes=read(fdSrc, buf, DEFAULT_BLOCK_SIZE)) > 0)
- {
- write(fdTrg, buf, readBytes);
- totalReadBytes += readBytes ;
- //複製資料後就呼叫callback函式去做顯示百分比的動作。
- callback( (totalReadBytes*100)/fs.st_size);
- }
- close(fdTrg);
- close(fdSrc);
- }
- int main(void)
- {
- // 這個範例中只是利用callback來顯示目前的進度。
- // 實際上我們可以利用callback來做更多的動作。
- CopyFile("A.TXT", "B.TXT", ShowPercentage);
- return 0 ;
- }
TouchAPI.cpp裡的
回覆刪除void RegisterCallbackTouchFunc(CB_TOUCH_FUNC callback)
{
DetectTouchFromSystem(callback);
}
為甚麼還要多一層函式? 直接用DetectTouchFromSystem(callback)是不是就可以了?