這個方法可以實現按照統一的接口來調用類成員函數,或者靜態函數和非類成員函數. 主要原理很簡單, 就是保存類對象指針和函數指針, 需要調用的時候就根據類對象指針是否為空來使用不同的方式調用函數.
首先, 我們需要一個把成員函數指針轉化成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....
}