C++實現Delegate

來源:互聯網
上載者:User
大家都知道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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.