Callback是這樣的一類對象(在這裡不能簡單的理解為"回調函數"了):你注冊一個函數,以及調用它時的參數,希望在滿足某個條件時,以這些注冊的函數調用這個回調,完成指定的操作.
很多地方會使用到這個概念.比如,UI程序中,注冊一個函數,當某個鼠標事件發生的時候自動調用;比如,創建一個線程,線程開始運行時,執行注冊的函數操作.
Callback的出現,本質上是因為很多操作都有異步化的需要---你不知道它什麼時候會執行,只需要告訴它,在執行的時候,調用我告訴你的操作即可.
盡管使用的地方不盡相同,但是從程序的角度上看,做的事情都是差不多的.
要實現一個Callback,最大的難點在於,變化的參數和需要統一的對外接口之間的矛盾.也就是說,回調函數執行時參數的數量是你無法預知的.而你需要對外提供一個統一的接口,調用該接口的不需要關注到注冊進去的到底是什麼,有幾個參數,具體的執行留到回調真正執行的時候再去處理.
簡單介紹一下目前我所知道的幾種方法,有C++的,也有C的.
1) 使用模板
將不同參數的類型,作為模板的參數.比如:

#include <stdio.h>

class Closure



{

public:


virtual ~Closure()

{}


virtual void Run()

{}

protected:


Closure()

{}

};

template<class T>

class Callback0

: public Closure



{

public:

typedef void (T::*Done)();

public:

Callback0(T *obj, Done run)

: object_(obj)

, run_(run)


{

}

virtual void Run()


{

(object_->*run_)();

}

private:

T *object_;

Done run_;

};

template<class T, class T1>

class Callback1

: public Closure



{

public:

typedef void (T::*Done)(T1);

public:

Callback1(T *obj, Done run, T1 arg)

: object_(obj)

, run_(run)

, arg0_(arg)


{

}

virtual void Run()


{

(object_->*run_)(arg0_);

}

private:

T *object_;

Done run_;

T1 arg0_;

};

class Test



{

public:

void Run0()


{

printf("in Test::Run0\n");

}

void Run1(int i)


{

printf("in Test::Run1\n");

}

};

template<class T>

Closure*

NewCallback(T *obj, void (T::*member)())



{

return new Callback0<T>(obj, member);

}

template<class T, class T1>

Closure*

NewCallback(T *obj, void (T::*member)(T1), T1 P)



{

return new Callback1<T, T1>(obj, member, P);

}

int main()



{

Test test;

Closure *callback0 = NewCallback(&test, &Test::Run0);

callback0->Run();

delete callback0;

Closure *callback1 = NewCallback(&test, &Test::Run1, 1);

callback1->Run();

delete callback1;

return 0;

}
在這裡,定義了一個虛擬基類Closure,它對外暴露一個接口Run,也就是,使用它的時候只需要使用Closure指針->Run即可以執行注冊的操作.需要注意的是,Closure的構造函數聲明為protected,也就是僅可以被子類調用.
接下來,定義的Closure'子類都是模板類,其中的模板都是參數,我分別實現了兩種子類,分別是不帶參數的和帶一個參數的.將回調函數需要的參數,保存在具體的子類對象中.
最後,對外構造一個Closure指針時,最好也提供一致的接口,這裡分別為兩種子類實現了NewCallback函數.
剩下的,理解起來應該不難.
這種實現方法,看明白的就知道,其實難點不多.它將回調函數和傳遞給回調函數的參數放在了一個類中,當外部調用Run接口的時候,再根據內部的實現來具體進行操作.
但是,我本人很不喜歡模板滿天飛的代碼,所以應該還有些別的方法來實現吧?
2) 不使用模板,將參數和回調分離,分別對參數和回調進行抽象
CEGUI是一款開源的游戲UI項目,早幾年我還在做著3D引擎程序員夢的時候,曾經看過一些,對它的一些代碼還有些印象.
裡面對UI事件的處理,也使用了類似Callback的機制(這種使用場景最開始的時候曾經說過,所以應該不會感到意外).
在CEGUI中,一個事件由一個虛擬基類Event定義,處理事件的時候調用的是它的純虛函數fireEvent,而這個函數的參數之一是EventArgs--這又是一個虛擬基類.
所以,熟悉面向對象的人,應該可以很快的反應過來了:在Event的子類中實現fireEvent,而不同的函數參數,可以從EventArgs虛擬基類中派生出來.
於是,具體回調的時候,僅僅需要調用 Event類指針->fireEvent(EventArgs類指針)就可以了.
(我在這裡對CEGUI的講解,省略了很多細節,僅僅關注到最關注的點,感興趣的可以自己去看看代碼)
對比1)和2)兩種解決方法,顯然對我這樣不喜歡模板的人來說,更喜歡2).除了模板的代碼讀起來比較頭大,以及模板會讓代碼量增大之外.喜歡2)的原因還在於,C對"類模板"機制的支持實在是欠缺,至今除了使用宏之外,似乎找不到很好的辦法能夠實現類C++的模板機制.但是,如果采用2)的繼承接口的方式,C就可以很清楚的實現出來.所以就有了下面C的實現:
3) C的實現.
有了2)的准備,使用C來實現一個類似的功能,應該很容易了,下面貼代碼,應該很清楚的:

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

typedef struct event



{

void (*fireEvent)(void *arg);

void *arg;

}event_t;

typedef struct event_arg1



{

int value;

}event_arg1_t;

void fireEvent_arg1(void *arg)



{

event_arg1_t *arg1 = (event_arg1_t*)arg;

printf("arg 1 = %d\n", arg1->value);

}

#define NewEvent(event, eventtype, callback) \


do

{ \

*(event) = (event_t*)malloc(sizeof(event_t)); \

assert(*(event)); \

(*(event))->arg = (eventtype*)malloc(sizeof(char) * sizeof(eventtype)); \

assert((*(event))->arg); \

(*(event))->fireEvent = callback; \

} while (0)

#define DestroyEvent(event) \


do

{ \

free((*(event))->arg); \

free(*(event)); \

} while(0)

int main()



{

event_t *event;

NewEvent(&event, event_arg1_t, fireEvent_arg1);

((event_arg1_t*)(event->arg))->value = 100;

event->fireEvent(event->arg);

DestroyEvent(&event);

return 0;

}