1. 程式人生 > >通用的類成員函式呼叫方法.

通用的類成員函式呼叫方法.

這個方法可以實現按照統一的介面來呼叫類成員函式,或者靜態函式和非類成員函式. 主要原理很簡單, 就是儲存類物件指標和函式指標, 需要呼叫的時候就根據類物件指標是否為空來使用不同的方式呼叫函式.


首先, 我們需要一個把成員函式指標轉化成void *的東西..(強制轉換似乎是不行的), 因為我們需要把成員函式指標儲存起來, 又不想讓使用者寫函式指標型別描述, 那隻能轉換成void*比較方便.
這裡我們使用 聯合地址轉換 的方法.

template <typename T1, typename T2>
struct _T2T{
 union {
  T1  _tv1;
  T2  _tv2;
 };
};

template <typename T1, typename T2>
inline T1 t2t( T2 tv2 )
{
 typedef struct _T2T<T1, T2> * PT2T;
 PT2T pt = (PT2T)&tv2;
 return pt->_tv1;
}

轉換方法就是 t2t<void*>( &ClassName::FuncName );

然後, 我們來構造一個可以用 this 指標和 函式指標來進行函式呼叫的東西.....這裡使用了我之前隨筆裡介紹的多引數定義巨集, 用來生成多引數的函式呼叫.

struct callfunction
{
 class nullclass{};

 template <typename TRet>
 static inline TRet callauto( void * pThis, void * pFunc ){
  if( pThis == NULL )
   return callnothis<TRet>( pFunc );
  return callwiththis<TRet>( pThis, pFunc );
 }

#define DEFINE_CALLFUNC_AUTO_WITH_PARAM( paramcount ) template <typename TRet, DP_STMP_##paramcount(typename, Tp)>\
 static inline TRet callauto( void * pThis, void * pFunc, DP_MTMP_##paramcount(Tp, p ) ){\
  if( pThis == NULL )\
   return callnothis<TRet>( pFunc, LP_SNMP_##paramcount( p ) );\
  else\
   return callwiththis<TRet>( pThis, pFunc, LP_SNMP_##paramcount( p ) );\
 }
DEFINE_CALLFUNC_AUTO_WITH_PARAM(1);
DEFINE_CALLFUNC_AUTO_WITH_PARAM(2);
DEFINE_CALLFUNC_AUTO_WITH_PARAM(3);
DEFINE_CALLFUNC_AUTO_WITH_PARAM(4);
DEFINE_CALLFUNC_AUTO_WITH_PARAM(5);
DEFINE_CALLFUNC_AUTO_WITH_PARAM(6);
DEFINE_CALLFUNC_AUTO_WITH_PARAM(7);
DEFINE_CALLFUNC_AUTO_WITH_PARAM(8);
DEFINE_CALLFUNC_AUTO_WITH_PARAM(9);
DEFINE_CALLFUNC_AUTO_WITH_PARAM(10);

 template <typename TRet>
 static inline TRet callwiththis( void * pThis, void * pFunc ){
  typedef TRet (nullclass::*funcptr)();
  return (((nullclass*)pThis)->*p2t<funcptr>(pFunc))();
 }


#define DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(paramcount) template <typename TRet, DP_STMP_##paramcount(typename, Tp)>\
 static inline TRet callwiththis( void * pThis, void * pFunc, DP_MTMP_##paramcount(Tp, p ) ){\
  typedef TRet (nullclass::*funcptr)(DP_MTMP_##paramcount(Tp, p ));\
  return (((nullclass*)pThis)->*p2t<funcptr>(pFunc))(LP_SNMP_##paramcount( p ));\
 }
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(1);
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(2);
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(3);
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(4);
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(5);
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(6);
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(7);
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(8);
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(9);
DEFINE_CALLFUNC_WITHTHIS_WITH_PARAM(10);

 template <typename TRet>
 static inline TRet callnothis( void * pFunc ){
  typedef TRet (*funcptr)();
  return (*p2t<funcptr>(pFunc))();
 }

#define DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(paramcount) template <typename TRet, DP_STMP_##paramcount(typename, Tp)>\
 static inline TRet callnothis( void * pFunc, DP_MTMP_##paramcount(Tp, p ) ){\
  typedef TRet (*funcptr)(DP_MTMP_##paramcount(Tp, p ));\
  return (*p2t<funcptr>(pFunc))(LP_SNMP_##paramcount( p ));\
 }
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(1);
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(2);
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(3);
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(4);
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(5);
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(6);
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(7);
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(8);
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(9);
DEFINE_CALLFUNC_NOTHIS_WITH_PARAM(10);
};

這裡面提供了三種呼叫方式, callauto 是根據this是否為NULL自動選擇按照類成員函式, 還是普通函式 來呼叫, callwiththis 固定按照類成員函式來呼叫, callnothis 固定按照非成員函式的方式來呼叫.
每種呼叫方式提供了10個帶引數的呼叫和1個不帶引數的呼叫. 最大支援 10個引數的成員函式呼叫, 基本上已經足夠了.

這個struct的使用方法是

callfunction::callwiththis<returntype>( objectptr, memfuncptr, params ... );

比如我們要呼叫 類CAddObject 的物件指標 pObject 的返回型別為int 名字為 add, 並且帶有兩個int型引數的成員函式, 我們只需要這樣呼叫
int result = callfunction::callwiththis<int>( pObject, t2t<void*>( &CAddObject::add ), 20, 20 );
這樣,我們就呼叫了這個函式,並且把結果儲存在result

最後, 我們來封裝一個函式呼叫的物件, 用來更方便的使用這個方法
template <typename TRet>
class CCustomCall
{
 typedef CCustomCall<TRet> TSelf;
 void * lpThis;
 void * lpFunc;
public:
 CCustomCall( const TSelf & sv ):lpThis(sv.lpThis), lpFunc(sv.lpFunc) {}
 template <typename TFunc>
 CCustomCall( void * _this, TFunc _f ):lpThis(_this){
  lpFunc = t2p<TFunc>( _f );
 }
 CCustomCall():lpThis(NULL), lpFunc(NULL){}

 TSelf & operator =( const TSelf & sv )
 {
  lpThis = sv.lpThis;
  lpFunc = sv.lpFunc;
  return (*this);
 }

#define DEFINE_CALL_WITH_PARAM(paramcount) template <DP_STMP_##paramcount( typename, Tp ) >\
 TRet call( DP_MTMP_##paramcount(Tp, p ) ){\
  return callfunction::callauto<TRet>( lpThis, lpFunc, LP_SNMP_##paramcount(p));\
 }
 DEFINE_CALL_WITH_PARAM(1);
 DEFINE_CALL_WITH_PARAM(2);
 DEFINE_CALL_WITH_PARAM(3);
 DEFINE_CALL_WITH_PARAM(4);
 DEFINE_CALL_WITH_PARAM(5);
 DEFINE_CALL_WITH_PARAM(6);
 DEFINE_CALL_WITH_PARAM(7);
 DEFINE_CALL_WITH_PARAM(8);
 DEFINE_CALL_WITH_PARAM(9);
 DEFINE_CALL_WITH_PARAM(10);
 TRet call(){return callfunction::callauto<TRet>( lpThis, lpFunc );}
bool empty(){ return (lpFunc == NULL);}
};

使用這個類, 就可以用簡單的寫法來實現呼叫各種函式...

比如下面演示了使用同一個類來實現成員函式和非成員函式的混合呼叫.

typedef CCustomCall<int> IntCall;

class CTest
{
public:
    int add( int n1, int n2 ){ return (n1+n2);}
    int mul( int n1, int n2 ){ return (n1*n2);}
};

int div( int n1, int n2 ){ return (n1/n2);}

int main(int argc, char * argv[])
{
    CTest test;
    IntCall addcall( &test, &CTest::add );
    IntCall mulcall( &test, &CTest::mul );
    IntCall divcall( NULL, &div );
    int nResult = 0;
    nResult = addcall.call( 20, 20 );
    printf( "addcall result = %d\n", nResult );
    nResult = mulcall.call( 20, 20 );
    printf( "mulcall result = %d\n", nResult );
    nResult = divcall.call( 20, 20 );
    printf( "divcall result = %d\n", nResult );
    return 0;
}
輸出結果是

40
400
1

下面是一個作為事件呼叫的例子

typedef CCustomCall<void> EventCall;
class CButton
{
public:
    EventCall eventOnClick;
};

class CApplication
{
.....
    void OnOkButtonClick( CButton * pButton );
    CButton m_OkButton;
};

............................初始化事件...............
BOOL CApplication::Init(){
.....
m_OkButton.eventOnClick = EventCall( this, &CApplication::OnOkButtonClick );
...
}

............................在BUTTON的滑鼠按下事件中.........................

void CButton::OnMouseDown( int key, int x, int y )
{
    if( key == VK_LBUTTON && !eventOnClick.empty() )
        eventOnClick.call( this );               /// 呼叫了設定的事件函式, 在這個例子裡, 當this = CApplication::m_OkButton的時候, 就會呼叫 CApplication::OnOkButtonClick....
}