大家都知道C#的Delegate非常的好用,特別是應用在Subject-Observer模式的時候,具有很好的靈活性,但是C++只有function pointers,前面有一篇blog講到了它的問題,以及難以理解的地方。不過也提到boost使用模板實現了boost::function基本具備了delegate的特點,我稍加改造,去除了boost的依賴關係,實現了一個小巧的Delegate,不過鑒於VC6.0的編譯器還不支援“偏特化”,因此使用上還有點不爽的地方。class Subject
{
public:
void notify()
{
myHandler();
} mylib::delegate0<int> myHandler;
std::string myString;
};class Observer
{
public:
int OnNotify()
{
std::cout << "Observer::OnNotify" << std::endl;
return 0;
}
}; int main(int argc, char* argv[])
{
Subject subject;
Observer observer; subject.myHandler += mylib::mem_fun0(&Observer::OnNotify, &observer);
subject.myString = "changed";
subject.notify();}這樣的用法看起來和C#的已經很接近了!下面我們來一步步的解瀆boost::function()的原理吧,鑒於很多人都和我一樣對於模板和宏技巧都是入門級的,我主要介紹幾個要點吧(卡了我超過一個小時的點):1、【技巧】使用VC工程編譯參數加 /E可以把預先處理的結果輸出到output,/P輸出到中間檔案*.i;2、【置疑】下面是兩個宏定義,結果完全不同的:#define PARAM_COUNT 3#define _DELEGATE_FUNC_P(i) delegate_func ##i
#define _DELEGATE_FUNC(count) _DELEGATE_FUNC_P(count) _DELEGATE_FUNC(PARAM_COUNT) #define PARAM_COUNT 3#define _DELEGATE_FUNC_P(i) delegate_func ##i
_DELEGATE_FUNC_P(PARAM_COUNT) 3、【基礎】函數模板,以前從來沒有使用過函數模板,不過看到這個例子就會發現模板的好處了:template<class _R, class _Ty, class _A>
class mem_fun1_t{
public:
explicit mem_fun1_t(_R (_Ty::*_Pm)(_A))
: _Ptr(_Pm){}
private:
_R (_Ty::*_Ptr)(_A);
};
// TEMPLATE FUNCTION mem_fun1
template<class _R, class _Ty, class _A> inline
mem_fun1_t<_R, _Ty, _A> mem_fun1(_R (_Ty::*_Pm)(_A))
{return (mem_fun1_t<_R, _Ty, _A>(_Pm)); }
4、【技巧】現在遇到的問題是,雖然通過函數模板,在函數內可以提取出類的名字,產生對象,但是該對象如果需要儲存下來,下次調用的時候又無法知道開始的時候是什麼類了。舉個例子來說明問題所在:在delegate類中儲存了CTest的指標強制轉換成了void*後面需要調用CTest的函數,需要把void*指標重新轉換成CTest的指標,可是delegate類並不知道CTest類,這怎麼辦呢?方法是使用了一個類和靜態函數的方法,靜態函數的形式是固定的,這樣就可以儲存下CTest類的類型了,下面是樣本:
template<typename FuncPtr, typename _R, typename T1>
struct delegate_h{
static _R invoker(void* p, T1 i)
{
FuncPtr* mp= (FuncPtr*)p;
return (*mp)(i);
}
};
template<class _R, class _A1>
class delegate{
public:
typedef _R(*FuncPtr)(_A1);
template<typename Mem>
delegate(Mem& f)
{
m_mem = new Mem(f);
invoker = delegate_h<Mem,_R, _A1>::invoker;
}
_R operator() (_A1 a)
{
return invoker(m_mem, a);
}
private:
void* m_mem;
typedef _R (*invoke_type)(void*, _A1);
invoke_type invoker;
};
5、【技巧】宏定義進行重複,這個是為了降低工作量的,因為對於不同的參數個數,需要定義不同的類,因此需要重複的代碼,這個非常不好,我們可以使用宏定義來進行重複。比如需要產生3個參數的模板就需要template<typename R, typename A1, typename A2, typename A3>,如何通過簡單的代碼產生任意長度的列表呢?
#define BOOST_PP_REPEAT(n, m, p) BOOST_PP_REPEAT ## n(m, p)
#define BOOST_PP_REPEAT0(m, p)
#define BOOST_PP_REPEAT1(m, p) m(0, p)
#define BOOST_PP_REPEAT2(m, p) m(0, p) m(1, p)
#define BOOST_PP_REPEAT3(m, p) BOOST_PP_REPEAT2(m, p) m(2, p)
#define BOOST_PP_REPEAT4(m, p) BOOST_PP_REPEAT3(m, p) m(3, p)
#define BOOST_PP_REPEAT5(m, p) BOOST_PP_REPEAT4(m, p) m(4, p)
....
//typename A1, typename A2, typename A3
#define DEFINE_TYPENAME(i, k) MY_COMMA_I(BOOST_PP_BOOL_I(i)) typename A##i
#define REPEAT_TYPENAME(count) REPEAT_TYPENAME_I(count)
#define REPEAT_TYPENAME_I(count) BOOST_PP_REPEAT(count, DEFINE_TYPENAME, count)
6、【技巧】上面的例子裡面使用了BOOST_PP_BOOL_I(i),這個是一個條件陳述式,表示第一個參數前面不要加逗號,這個是因為這個重複出來的字串,可能作為operator()的參數,那麼參數列表的第一個參數前面不能有逗號,而重複出來的代碼肯定有,所以需要通過條件宏來解決,第一個不加逗號。下面是條件宏的實現:
//BOOST BOOL
#define BOOST_PP_BOOL_I(x) BOOST_PP_BOOL_ ## x
#define BOOST_PP_BOOL_0 0
#define BOOST_PP_BOOL_1 1
#define BOOST_PP_BOOL_2 1
#define BOOST_PP_BOOL_3 1
#define BOOST_PP_BOOL_4 1
#define BOOST_PP_BOOL_5 1
......
#define MY_COMMA(bit) MY_COMMA_##bit
#define MY_COMMA_I(count) MY_COMMA(count)
#define MY_COMMA_0
#define MY_COMMA_1 ,
7、【基礎】使用模板函數之後如果希望對特定的類型做不同的處理,那麼這個特定的函數一定需要在模板函數後面定義,否則就會出現二意性。
8、【技巧】重複引用同一個檔案,給定不同的宏定義,這樣可以產生很多個類,用來產生delegate0, delegate1, delegate2....下面是一個例子:
#include "funcbase.h"
#include <stdio.h>
#include <vector>
#define PARAM_COUNT 0
#include "function_temp.h"
#undef PARAM_COUNT
#define PARAM_COUNT 1
#include "function_temp.h"
#undef PARAM_COUNT
9、【問題】最後的問題是傳回值必須不能是void,因為對於void類型的返回return後面不能跟任何的東西,解決的方法是定義result_type把void進行封裝,返回一個不需要的類型,但是這樣需要增加不少代碼,故我沒有採用這個方法。
好了,現在已經完整的講了boost::function實現過程中會碰到的問題,也溫習了一些知識點,相信這個對於boost的描述和很多其它的資料很不同:)。
PS:講個自己的故事,記得以前還在上大學的時候,勤工儉學需要對一批WORD文檔進行同樣的編輯處理,我當時僅會最簡單的WORD編輯,因此我用我自己的知識加上勤奮的雙手奮鬥了一個通宵完成了任務。第二天我們班一個精通電腦的傢伙看我很辛苦,就幫我研究了一下WORD,然後讓我使用一些自動的工具,大幅度的提高了效率!這個給我很深的印象,我們很多時候都只是在使用最簡單的技術搭建巨大的應用,卻不願意多花10分鐘學習一下,以提高效率,這是可悲的。
PS2:這裡不能加附件,如果需要原始碼的可以和聯絡,代碼比較簡單。另外有志同道合的也希望多多交流加個連結哦~ oeichenwei@gmail.com