回調機制

摘選.詳情見:http://www.ibm.com/developerworks/cn/linux/l-callback/php

另見 Callback Functions Tutorial : http://www.codeguru.com/cpp/cpp/cpp_mfc/callbacks/article.php/c10557/Callback-Functions-Tutorial.htmlinux

1 什麼是回調 架構

軟件模塊之間老是存在着必定的接口,從調用方式上,能夠把他們分爲三類:同步調用、回調和異步調用。同步調用是一種阻塞式調用,調用方要等待對方執行完畢才返回,它是一種單向調用;回調是一種雙向調用模式,也就是說,被調用方在接口被調用時也會調用對方的接口;異步調用是一種相似消息或事件的機制,不過它的調用方向恰好相反,接口的服務在收到某種訊息或發生某種事件時,會主動通知客戶方(即調用客戶方的接口)。回調和異步調用的關係很是緊密,一般咱們使用回調來實現異步消息的註冊,經過異步調用來實現消息的通知。同步調用是三者當中最簡單的,而回調又經常是異步調用的基礎,所以,下面咱們着重討論回調機制在不一樣軟件架構中的實現。 異步

對於不一樣類型的語言(如結構化語言和對象語言)、平臺(Win3二、JDK)或構架(CORBA、DCOM、WebService),客戶和服務的交互除了同步方式之外,都須要具有必定的異步通知機制,讓服務方(或接口提供方)在某些狀況下可以主動通知客戶,而回調是實現異步的一個最簡捷的途徑。 分佈式

對於通常的結構化語言,能夠經過回調函數來實現回調。回調函數也是一個函數或過程,不過它是一個由調用方本身實現,供被調用方使用的特殊函數。 函數

在面向對象的語言中,回調則是經過接口或抽象類來實現的,咱們把實現這種接口的類成爲回調類,回調類的對象成爲回調對象。對於象C++或Object Pascal這些兼容了過程特性的對象語言,不只提供了回調對象、回調方法等特性,也能兼容過程語言的回調函數機制。 ui

Windows平臺的消息機制也能夠看做是回調的一種應用,咱們經過系統提供的接口註冊消息處理函數(即回調函數),從而實現接收、處理消息的目的。因爲Windows平臺的API是用C語言來構建的,咱們能夠認爲它也是回調函數的一個特例。 代理

對於分佈式組件代理體系CORBA,異步處理有多種方式,如回調、事件服務、通知服務等。事件服務和通知服務是CORBA用來處理異步消息的標準服務,他們主要負責消息的處理、派發、維護等工做。對一些簡單的異步處理過程,咱們能夠經過回調機制來實現。 指針

下面咱們集中比較具備表明性的語言(C、Object Pascal)和架構(CORBA)來分析回調的實現方式、具體做用等。 code

 

20131122150103

 

2 過程語言中的回調(C)

2.1 函數指針

回調在C語言中是經過函數指針來實現的,經過將回調函數的地址傳給被調函數從而實現回調。所以,要實現回調,必須首先定義函數指針,請看下面的例子:

void Func(char *s);// 函數原型
void (*pFunc) (char *);//函數指針
 

能夠看出,函數的定義和函數指針的定義很是相似。

通常的化,爲了簡化函數指針類型的變量定義,提升程序的可讀性,咱們須要把函數指針類型自定義一下。

typedef void(*pcb)(char *);
 

回調函數能夠象普通函數同樣被程序調用,可是隻有它被看成參數傳遞給被調函數時才能稱做回調函數。

被調函數的例子:

void GetCallBack(pcb callback)
{
/*do something*/
}
用戶在調用上面的函數時,須要本身實現一個pcb類型的回調函數:
void fCallback(char *s) 
{
/* do something */
} 
而後,就能夠直接把fCallback看成一個變量傳遞給GetCallBack,
GetCallBack(fCallback);

若是賦了不一樣的值給該參數,那麼調用者將調用不一樣地址的函數。賦值能夠發生在運行時,這樣使你能實現動態綁定。

 

2.2 參數傳遞規則

到目前爲止,咱們只討論了函數指針及回調而沒有去注意ANSI C/C++的編譯器規範。許多編譯器有幾種調用規範。如在Visual C++中,能夠在函數類型前加_cdecl,_stdcall或者_pascal來表示其調用規範(默認爲_cdecl)。C++ Builder也支持_fastcall調用規範。調用規範影響編譯器產生的給定函數名,參數傳遞的順序(從右到左或從左到右),堆棧清理責任(調用者或者被調用者)以及參數傳遞機制(堆棧,CPU寄存器等)。

將調用規範當作是函數類型的一部分是很重要的;不能用不兼容的調用規範將地址賦值給函數指針。例如:

// 被調用函數是以int爲參數,以int爲返回值
__stdcall int callee(int); 
// 調用函數以函數指針爲參數
void caller( __cdecl int(*ptr)(int)); 
// 在p中企圖存儲被調用函數地址的非法操做
__cdecl int(*p)(int) = callee; // 出錯

指針p和callee()的類型不兼容,由於它們有不一樣的調用規範。所以不能將被調用者的地址賦值給指針p,儘管二者有相同的返回值和參數列

 

2.3 應用舉例

C語言的標準庫函數中不少地方就採用了回調函數來讓用戶定製處理過程。如經常使用的快速排序函數、二分搜索函數等。

快速排序函數原型:

void qsort(void *base, size_t nelem, size_t width, 
  int (_USERENTRY *fcmp)(const void *, const void *));
二分搜索函數原型:
void *bsearch(const void *key, const void *base, size_t nelem,
	size_t width, int (_USERENTRY *fcmp)(const void *, const void *));

其中fcmp就是一個回調函數的變量。

 

下面給出一個具體的例子:

#include <stdio.h>
#include <stdlib.h>
int sort_function( const void *a, const void *b);
int list[5] = { 54, 21, 11, 67, 22 };
int main(void)
{
   int  x;
   qsort((void *)list, 5, sizeof(list[0]), sort_function);
   for (x = 0; x < 5; x++)
      printf("%i\n", list[x]);
   return 0;
}
int sort_function( const void *a, const void *b)
{
   return *(int*)a-*(int*)b;
}
相關文章
相關標籤/搜索