这个方法可以实现按照统一的接口来调用类成员函数,或者静态函数和非类成员函数. 主要原理很简单, 就是保存类对象指针和函数指针, 需要调用的时候就根据类对象指针是否为空来使用不同的方式调用函数.
首先, 我们需要一个把成员函数指针转化成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....
}