2014年4月17日 星期四

簡單實作 boost::function 的主要功能

trace goost::function 真的像在走迷宮,它使用大量的 macro 層層相扣,但也只提供最多 10 個參數可以使用,若參數個數能無限制還情有可原,只為 10 個參數搞得這麼誇張,不如使用暴力法還來得簡單明瞭。

goost::function 其實也沒什麼,主要的關鍵在要怎麼把函數的 type 分離出來,比如 goost::function<char(float,int)> 中的 char(float,int) 是一個 type,要怎麼分離出 char、float 和 int 這三個 type?goost::function 是借用特製化 class 自動匹配 type 的技巧,以下用例子說明:

#include <iostream>

using namespace std;

// 本尊,用來限定 template 參數只能有一個
// K 是為了指示函數型別用的,比如 int(int)
template<typename K>
class A
{
public:
  A(){}
  ~A(){}
};

// 特製化類別
// template 參數的型別會依使用端的指定而自動匹配
template<typename K, typename T>
class A<K(T)>
{
  T m_i;
public:
  A(){}
  ~A(){}

  K operator()(T i)
  {
    m_i = i;
    cout << "K(T)" << endl;
    return m_i;
  }
};

// 特製化類別
template<typename K, typename T, typename S>
class A<K(T,S)>
{
  float m_i;
public:
  A(){}
  ~A(){}

  K operator()(T i, S s)
  {
    m_i = i;
    cout << "K(T,S)" << endl;
    return m_i;
  }
};

int main()
{
  A<int(int)> a2;
  A<int(long,char)> a3;

  a2(2);
  a3(2,2);

  cout << "Hello world!" << endl;
  return 0;
}


有了以上的了解,再來就直接來看如何用暴力法來實作和 goost::function 相同的功能

/*---------------------------------------------

DELEGATE.HPP v1.0

Copyright 楊志賢 CxxlMan, 2014
All Rights Reserved

可代理一般函數指標或成員函數指標,參數最多 10 個
----------------------------------------------*/

#if !defined(__DELEGATE_HPP_CxxlMan)
#define __DELEGATE_HPP_CxxlMan

namespace CxxlMan
{

// 本尊
template<typename T>
class delegate
{
public:
  // Constructor
  delegate(){}
  // Destructor
  ~delegate(){}
};

}   /* namespace CxxlMan */

// 產生 11 個 delegate 特製化類別
#define FUNCTION_ARGS 0
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 1
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 2
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 3
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 4
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 5
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 6
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 7
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 8
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 9
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#define FUNCTION_ARGS 10
#include "DELEGATE_TEMPLATE.HPP"
#undef FUNCTION_ARGS
#endif

/*------------------------------------------------------------------------

DELEGATE_TEMPLATE.HPP v1.0

Copyright 楊志賢 CxxlMan, 2014
All Rights Reserved

給 DELEGATE.HPP 多次引用,以建立 11 個 delegate 的特製化類別
------------------------------------------------------------------------*/

namespace CxxlMan
{

#if FUNCTION_ARGS == 0
#  define FUNCTION_TEMPLATE_PARMS typename T0
#  define FUNCTION_PARTIAL_SPEC T0()
#  define FUNCTION_PARMS
#  define FUNCTION_ARGS_VALUE
#elif FUNCTION_ARGS == 1
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1
#  define FUNCTION_PARTIAL_SPEC T0(T1)
#  define FUNCTION_PARMS T1 a1
#  define FUNCTION_ARGS_VALUE a1
#elif FUNCTION_ARGS == 2
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1,typename T2
#  define FUNCTION_PARTIAL_SPEC T0(T1,T2)
#  define FUNCTION_PARMS T1 a1,T2 a2
#  define FUNCTION_ARGS_VALUE a1,a2
#elif FUNCTION_ARGS == 3
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1,typename T2,typename T3
#  define FUNCTION_PARTIAL_SPEC T0(T1,T2,T3)
#  define FUNCTION_PARMS T1 a1,T2 a2,T3 a3
#  define FUNCTION_ARGS_VALUE a1,a2,a3
#elif FUNCTION_ARGS == 4
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1,typename T2,typename T3,typename T4
#  define FUNCTION_PARTIAL_SPEC T0(T1,T2,T3,T4)
#  define FUNCTION_PARMS T1 a1,T2 a2,T3 a3,T4 a4
#  define FUNCTION_ARGS_VALUE a1,a2,a3,a4
#elif FUNCTION_ARGS == 5
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1,typename T2,typename T3,typename T4,typename T5
#  define FUNCTION_PARTIAL_SPEC T0(T1,T2,T3,T4,T5)
#  define FUNCTION_PARMS T1 a1,T2 a2,T3 a3,T4 a4,T5 a5
#  define FUNCTION_ARGS_VALUE a1,a2,a3,a4,a5
#elif FUNCTION_ARGS == 6
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6
#  define FUNCTION_PARTIAL_SPEC T0(T1,T2,T3,T4,T5,T6)
#  define FUNCTION_PARMS T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6
#  define FUNCTION_ARGS_VALUE a1,a2,a3,a4,a5,a6
#elif FUNCTION_ARGS == 7
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7
#  define FUNCTION_PARTIAL_SPEC T0(T1,T2,T3,T4,T5,T6,T7)
#  define FUNCTION_PARMS T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7
#  define FUNCTION_ARGS_VALUE a1,a2,a3,a4,a5,a6,a7
#elif FUNCTION_ARGS == 8
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7,typename T8
#  define FUNCTION_PARTIAL_SPEC T0(T1,T2,T3,T4,T5,T6,T7,T8)
#  define FUNCTION_PARMS T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8
#  define FUNCTION_ARGS_VALUE a1,a2,a3,a4,a5,a6,a7,a8
#elif FUNCTION_ARGS == 9
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7,typename T8,typename T9
#  define FUNCTION_PARTIAL_SPEC T0(T1,T2,T3,T4,T5,T6,T7,T8,T9)
#  define FUNCTION_PARMS T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9
#  define FUNCTION_ARGS_VALUE a1,a2,a3,a4,a5,a6,a7,a8,a9
#elif FUNCTION_ARGS == 10
#  define FUNCTION_TEMPLATE_PARMS typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7,typename T8,typename T9,typename T10
#  define FUNCTION_PARTIAL_SPEC T0(T1,T2,T3,T4,T5,T6,T7,T8,T9,T10)
#  define FUNCTION_PARMS T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9,T10 a10
#  define FUNCTION_ARGS_VALUE a1,a2,a3,a4,a5,a6,a7,a8,a9,a10
#endif

// 特製化
template<FUNCTION_TEMPLATE_PARMS>
class delegate<FUNCTION_PARTIAL_SPEC>
{
  // 函數指標的版本
  class HostBase
  {
    T0 (*m_pFunc_Ptr)(FUNCTION_PARMS);
  public:
    // Constructor
    HostBase(){}

    // Constructor
    HostBase(T0 (*pFunc_Ptr)(FUNCTION_PARMS))
    {
      m_pFunc_Ptr = pFunc_Ptr;
    }
    // Destructor
    virtual ~HostBase() {}

    virtual T0 Function(FUNCTION_PARMS)
    {
      return m_pFunc_Ptr(FUNCTION_ARGS_VALUE);
    }
  };

  // 成員函數指標的版本
  template<typename K>
  class HostDerived:public HostBase
  {
    K &m_Obj;
    T0 (K::*m_pmFunc_Ptr)(FUNCTION_PARMS);
  public:
    // Constructor
    HostDerived(K &Obj, T0 (K::*pmFunc_Ptr)(FUNCTION_PARMS))
      :m_Obj(Obj)
    {
      m_pmFunc_Ptr = pmFunc_Ptr;
    }
    // Destructor
    virtual ~HostDerived() {}

    T0 Function(FUNCTION_PARMS)
    {
      return (m_Obj.*m_pmFunc_Ptr)(FUNCTION_ARGS_VALUE);
    }
  };

  HostBase *m_pHostBase;

public:
  // Constructor
  delegate()
  {
    m_pHostBase = NULL;
  }
  // Destructor
  ~delegate()
  {
    if(m_pHostBase)
      delete m_pHostBase;
  }

  void bind(T0 (*pFunc_Ptr)(FUNCTION_PARMS))
  {
    if(m_pHostBase)
      delete m_pHostBase;

    m_pHostBase = new HostBase(pFunc_Ptr);
  }

  template<typename K>
  void bind(K &Obj, T0 (K::*pmFunc_Ptr)(FUNCTION_PARMS))
  {
    if(m_pHostBase)
      delete m_pHostBase;

    m_pHostBase = new HostDerived<K>(Obj,pmFunc_Ptr);
  }

  T0 operator()(FUNCTION_PARMS)
  {
    if(m_pHostBase == NULL)
      throw "須先使用 delegate::bind()"; // 我是很不喜歡用 throw,可是也想不出其他辦法

    return m_pHostBase->Function(FUNCTION_ARGS_VALUE);
  }

};

#undef FUNCTION_TEMPLATE_PARMS
#undef FUNCTION_PARTIAL_SPEC
#undef FUNCTION_PARMS
#undef FUNCTION_ARGS_VALUE

}   /* namespace CxxlMan */

以下是測試程式

#include <iostream>
#include <C:\delegate\DELEGATE.HPP>

using namespace std;
using namespace CxxlMan;

int SubFunc(int lhs, int rhs)
{
  return lhs - rhs;
}

struct tagAdd
{
  int AddFunc(int lhs, int rhs)
  {
    return lhs + rhs;
  }

}AddObj;

int main()
{
  delegate<int(int,int)> Sub;
  delegate<int(int,int)> Add;
  Sub.bind(SubFunc);
  Add.bind(AddObj,&tagAdd::AddFunc);

  cout << "5-3 = " << Sub(5,3) << endl;
  cout << "5+3 = " << Add(5,3) << endl;

  return 0;
}


delegate 原碼下載:
https://www.dropbox.com/s/u8lfos941jz3hm5/delegate%20v1.0.7z
http://pan.baidu.com/s/1o6FKHUM




沒有留言:

張貼留言