略说成员函数指针及其模板的精简实现

略说成员函数指针及其模板的精简实现

    请容许先发一句牢骚,“这万恶的成员函数指针的丑陋语法!”,C中的函数指针的语法已经够难看的了,但相比之下,成员函数指针却更加不堪入目,使用上又很不方便,很不人性化,简直是只能行走寸步。只可惜,函数指针的作用实在太大了,忽视不得。
    大家都知道,函数指针(或又叫回调函数)是上层模块和底层模块进行通信的最佳手段,上层通过提供函数指针给底层,以使得底层在适当的时候,可以调用执行上层的代码,C库中的qsort足以说明这种使用,qsort在排序时,不知道如何比较两个数组元素的大小,必须通过上层提供的大小比较函数来进行比较。此外,操作系统提供的API中,有多少地方使用上了回调函数。假如没有函数指针,这简直没法想像,日子没法过了。函数指针是实现模块分层的不二法门,当然接口也可以,但是,用户代码必须继承或者实现底层硬性规定无理取闹的虚函数,本来很轻量级的POD,再也不轻快了,实在颇不方便,不,简直很有点恶心。说来说去,还是函数指针好。
    既然,C中的回调函数这么重要,那么,可想而知,进入C++中,整个世界到处都是CLASS,将回调函数这个概念推广到CLASS上,也即是成员函数指针,将是多么迫切的事情。理论上,可以将成员函数指针视为函数指针的语法糖,只要规定函数指针的第一个参数为void* pThis,然后在函数指针的实现函数中,进行类型转换也能满足使用,在很长的一段时间里,因为这种方式的简单清晰,一直都用这种方式代替成员函数指针。但是,一遍又一遍地被迫编写重复代码,特别是枯燥的类型转换,任何人都无法忍受。因此,决定直面这个问题。
    理论上,成员函数和普通函数一样,在内存中,都有自己的位置,只要有了地址信息,就可以通过指针来获取,保存起来,然后在未来的某个地方,通过这个指针来执行函数中的代码。差别之处,在于,调用成员函数前,要先将this推入ecx中,很久之前,成员函数指针确实和普通函数指针一样简单,只是后来,虚函数和多继承出现之后,简单的指针信息再也承载不了成员函数的重量,从此之后,.......(忽略,请大家自行BAIDU)。总之,C++中,成员函数指针并非指针类型,理所当然,也就不支持指针的大多数操作,比如,赋值NULL,或者类型转换。因此,所有能够让函数指针大放异彩的种种手段,在这里,都用不上了,原本在C中光芒四射的好东西,到了C++中,竟然黯然失色,所有本该让函数指针大显身手的地方,大家都绕道而行。
    逼急了,也有尝试突破,MFC的仅仅作了有限争取的手段(为了这一点点好处,MFC可不知作了多大的努力),居然成为其消息映射的基石。但是,据说,MFC的成员函数指针的设计也非出于自愿,而是因为消息太多,实在没法整成虚函数来处理,每个窗口类背负成千上万个函数的虚函数表,可不是省油的灯。为了努力地支持虚函数和多继承,C++的编译器不惜在成员函数指针的使用上设下种种阻拦,令人又气又恨。而更加令人不解的是,C++横行天下十几年,函数指针似乎长期得不到重视,大师们都在面向对象上探索,很多本该成员函数指针发光发热的地方,几乎都退位给虚函数了,并美其名曰策略模式又或者是其他的什么模式,不过是换了一套更加难看的马甲,却又那么好听的名字,不,不好听,只要听到模式两字,就令人大倒胃口。所有大用特用模式的代码,如果用非模式来实现,其代码量将少掉很多,而且还更具扩展性,这是真的。先透露一下,正在构思一文章,将深度介绍模式,专注于WHY,并且类比现实,兼扯上WINDOWS、COM和MFC对模式的应用,说句良心话,如果只用接口来做设计,模式绝对是好东西。只可惜,接口其实是SB。写底层代码,如果要求用户必须实现某些接口,又或者是继承某些类,改写虚函数,这种侵入式的设计,实在无理取闹之至。
    后来,大伙儿也终于开始重视成员函数指针,特别是C#的委托出现之后,网络上更是充斥着各种成员函数指针的版本代码,都可以很好地完成任务。特别是TR1又或者是BOOST中的function,功能相当的强悍得令人非大吃一惊不可。只可惜,大多数情况下,用户只想填饱肚子而已,但是BOOST或者其他的类库却硬要来一桌满汉全席,这也罢了,但是,它还要用户额外买单,并且还真不低呢,这就很让人受不了啦。其实,一般情况下,我们的要求不会太过分,仅仅想要针对普通的成员函数指针进行回调,它却为此在其内部new一个内部类出来,假如大规模使用,后果将不堪设想,其实也没那么严重,完成是C++迷们的强迫症。
    但是,说真的,实在希望很精简,不要生成不必要的虚函数表,不要模板生成不必要的函数(没办法内联,有函数地址的那一种),只要求它如同对待C中的函数指针一样,参数入栈和一个简单的函数调用的指令,外加将this推入ecx即可,就好像直接调用成员函数那样就好了。好了,贡献上代码了,史上最轻量级,精简无比的成员函数指针,功能也最弱了。对不起,代码并不完整,实际的代码,用上了宏,所谓的宏的图灵完备。在下很懒,一向只介绍想法而已,只于具体的实现细节以及语法考究,窃以为,每个C++迷们应该完全能够胜任。俗话说,高手只要求创意就行了,本文详细介绍算法并给出代码,已经落了下乘。
    这个实现,不考虑多继承,忽略了虚函数,也不支持非成员函数(也可以用上,只是,要多做一点点手脚,以下将给出示例,而且,普通函数指针已经完全可以胜任),只集中火力专注于普通成员函数,毕竟,在下的运用中,它占上了95%以上,所以才能如此的高效。单一职责啊!
    此外,本文参考了《成员函数指针与高性能的C++委托》、《刘未鹏的BOOST源码解析》、《C++设计新思维》,请自行GOOGLE。
template  < class  OutputClass,  class  InputClass >
union horrible_union{
    OutputClass 
out ;
    InputClass 
in ;
};

template 
< class  OutputClass,  class  InputClass >
inline 
void  union_cast(OutputClass &   out const  InputClass input){
    horrible_union
< OutputClass, InputClass >  u;
    typedef 
int  ERROR_CantUseHorrible_cast[ sizeof (InputClass) == sizeof (u) 
        
&&   sizeof (InputClass) == sizeof (OutputClass)  ?   1  :  - 1 ];
    u.
in   =  input;
    
out   =  u. out ;
}

template
< typename FuncSignature > class  TMemFn;
class  CCallbackObject{};

template
< typename R >
class  TMemFn < R () >
{
public :
    typedef R ReturnType;
    ReturnType 
operator ()()  const { return  (pThis ->* func)();}

    template
< typename _Ty >
    
void  Bind(_Ty *  pObj, R(_Ty:: * proc)())
    {
        union_cast(pThis, pObj);
        union_cast(func, proc);
    }

public :
    typedef ReturnType (CCallbackObject::
* FuncType)();
    FuncType func;
    CCallbackObject
*  pThis;
};

template
< typename R, typename P1 >
class  TMemFn < R (P1) >
{
public :
    typedef R ReturnType;
    typedef P1 Param1Type;

    ReturnType 
operator ()(Param1Type param1)  const { return  (pThis ->* func)(param1);}

    template
< typename _Ty >
    
void  Bind(_Ty *  pObj, ReturnType(_Ty:: * proc)(Param1Type))
    {
        union_cast(pThis, pObj);
        union_cast(func, proc);
    }

public :
    typedef ReturnType (CCallbackObject::
* FuncType)(Param1Type);
    FuncType func;
    CCallbackObject
*  pThis;
};

template
< typename R, typename _Ty >
TMemFn
< R () >  MakeMF(_Ty *  pThis, R(_Ty:: * proc)())
{
    TMemFn
< R () >  res; res.Bind(pThis, proc);
    
return  res;
}

template
< typename R, typename _Ty, typename P1 >
TMemFn
< R (P1) >  MakeMF(_Ty *  pThis, R(_Ty:: * proc)(P1))
{
    TMemFn
< R (P1) >  res;res.Bind(pThis, proc);
    
return  res;
}

int  Test( int  a)
{
    printf(
" Hello World %d\n " , a);
    
return  a;
}

int  _tmain( int  argc, _TCHAR *  argv[])
{
    
class  _CTest
    {
    
public :
        
int  CallTest( int  a)
        {
            
return  Test(a);
        }
    };
    TMemFn
< int  ( int ) >  aTest  =  MakeMF((_CTest * )NULL,  & _CTest::CallTest);
    aTest(
80 );
    
return   0 ;
}



# re: 略说成员函数指针及其模板的精简实现2012-11-22 07:39fzy

其实一切源于

typedef TRET (TClass::*P_METHOD)( TPARAM... );
typedef TRET (__cdecl *P_FUNC_CDECL)( TPARAM... );
typedef TRET (__stdcall * P_FUNC_STDCALL)( TPARAM... );
typedef TRET (__fastcall * P_FUNC_FASTCALL)( TPARAM... );

剩下的就是解决
TRET 和 TPARAM... 类型以及 P_METHOD/P_FUNC_XXXXX 的打包问题。

中间比较繁琐的是 TRET 的void 特例,以及TPARAM...的传参类型。

在某些特定环境下,还需要考虑TPARAM...的存储类型。

不过c++的template有特化来实现这些东西,所以最后都归结到工作量了。

# re: 略说成员函数指针及其模板的精简实现2012-11-22 09:27华夏之火
@fzy
有必要支持那么多吗,只要typedef TRET (TClass::*P_METHOD)( TPARAM... )这一个就行了,void 确实比较特别 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值