huaxiazhihuo

 

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

    请容许先发一句牢骚,“这万恶的成员函数指针的丑陋语法!”,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& outconst 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;
}

posted on 2012-11-16 10:46 华夏之火 阅读(2923) 评论(4)  编辑 收藏 引用 所属分类: c++技术探讨

评论

# re: 略说成员函数指针及其模板的精简实现 2012-11-16 17:32 Richard Wei

还是直接用C++ 11中的function吧  回复  更多评论   

# re: 略说成员函数指针及其模板的精简实现 2012-11-16 17:55 华夏之火

@Richard Wei
直接用function,会有心理负担,虽然没有必要  回复  更多评论   

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

其实一切源于

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 确实比较特别  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

统计

常用链接

留言簿(6)

随笔分类

随笔档案

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜