游戏开发论坛

 找回密码
 立即注册
搜索
查看: 17108|回复: 10

模版函数指针,C++委托的实现-原创【申请加精】

[复制链接]

119

主题

1367

帖子

1393

积分

金牌会员

Rank: 6Rank: 6

积分
1393
发表于 2008-9-29 16:11:00 | 显示全部楼层 |阅读模式
今天写引擎的时候,很想加入一些回调函数,以前一直没时间整理这块,这次一定要下决心好好整整代码,纯粹用多态,很多类非要加个帽子,类之间的关系也显的很单一,有的情况需要用到委托的回调机制,这是个很好的东西,在C#里面是原生支持的,C++里面本来函数指针是个不错的选择,可到了对象里面,成员函数指针还有那么好用吗?这原本是另C++程序员非常失望和无赖的,难道一说到成员函数指针就真的那么不好用,甚至要沦为被唾弃的命运?并非入此,别忘了,C++里面还有很强的利器,C#和Java后天才具备的特性可是C++先天就具备的哦,那就是范型,C++里面的用的是模版,而且如果把 模版和成员函数指针结合在一起,那就威力无比了,那应该叫做就是“成员模版函数指针”,C++的教科书上有这个名词吗?我查了查,好像是没有,而且网上很多资料竟然说这个不能实现之类的话,我都怀疑那些如此断言的人是否太不负责仁了,误导人啊。

当然指想成员函数的指针,这里面的确有段C++设计的问题,C++的成员函数地址通过对象外去引用不能直接通过“&对象.方法”的方式来引用,这个在C++标准里面是没有的,很多人到这里就绝望了,可间接引用呢?而且用很优雅的方式来引用呢?

好了,我也不绕圈子了,给出我的代码,一个简单的 “成员模版函数指针” 的实现,看看C++是如何优雅的实现委托的,真的非常非常的优雅,由于完全自己摸索出来的,真是感慨万千啊。

#include "stdafx.h"

#include <iostream>

using namespace std;

template<typename T>
class A
{
private:
   typedef int (T::*delegateFun)(int);
   T * _This;
   delegateFun _deleGate;

public:   

//This被代理的对象, delegateFun被代理的方法

A(T * This, int (T::*delegateFun)(int))
{
       _This = This;
       _deleGate = delegateFun;
}

    //c被代理的参数
int execue(int c)
{
      return (_This->*_deleGate)(c);
}

};


class B
{
public:
int FunA(int a) {return a + 10;}
int FunB(int a) {return a - 10;}
B()
{

}
};

int _tmain(int argc, _TCHAR* argv[])
{


B *objB = new B();

A<B>  delegateObj1(objB, (&B::FunA));
A<B>  delegateObj2(objB, (&B::FunB));


cout << delegateObj1.execue(10) <<endl;
cout << delegateObj2.execue(20) <<endl;

return 0;

}







看完了感觉如何?以后想要设计一个callback回调函数是否明朗了许多?
再也不需要强行搞个static约束方法,那么恶心的东西了吧


原文出处
http://www.cppblog.com/tonykee/archive/2008/09/29/63034.html

119

主题

1367

帖子

1393

积分

金牌会员

Rank: 6Rank: 6

积分
1393
 楼主| 发表于 2008-9-30 13:28:00 | 显示全部楼层

Re:模版函数指针,C++委托的实现-原创【申请加精】

在网上搜索了一下,有更好的实现
那就是: FastDelegate

9

主题

249

帖子

260

积分

中级会员

Rank: 3Rank: 3

积分
260
发表于 2008-9-30 16:24:00 | 显示全部楼层

Re:模版函数指针,C++委托的实现-原创【申请加精】

很实用,支持!!!

7

主题

438

帖子

438

积分

中级会员

Rank: 3Rank: 3

积分
438
发表于 2008-10-1 22:33:00 | 显示全部楼层

Re:模版函数指针,C++委托的实现-原创【申请加精】

我一直用boost的function的,虽然笨重了点,不过还行。

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
发表于 2008-10-2 10:10:00 | 显示全部楼层

Re:模版函数指针,C++委托的实现-原创【申请加精】

几年前我就开始更新过N多版本。不仅有delegate,还有non-blocking event。(用队列实现)
以及,处理了引用安全。

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
发表于 2008-10-2 10:12:00 | 显示全部楼层

Re:模版函数指针,C++委托的实现-原创【申请加精】

callback的核心代码是这个:

class __obj{};

#undef INST_THISCALLBACK
#define INST_THISCALLBACK(pthis,pfunc,rettype,params,args)                 ( reinterpret_cast<__obj *>(pthis) ->* ::inst::union_cast<rettype (__obj:: *)params>(pfunc) ) args

但是在 instEvent.h 中有保存了多个handler的Event对象和实现非阻塞事件的队列

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
发表于 2008-10-2 10:17:00 | 显示全部楼层

Re:模版函数指针,C++委托的实现-原创【申请加精】

奉劝LZ写引擎的时候,不要单纯用delegate----如果你不想毁掉你的引擎的话。

delegate应该放入queue中,用户可以在稍后进行peek.

比如:

MyEvent.PostEvent( this, new CMyEventArgs() );

在类库处理全部完成后,用户才可以调用:

CEvengMgr:eekAll(); // callback所有动态成员函数。

还有,对于属性值不要用 PropXxxChanged 这样的event,而要用Update去刷新。
(因为是Game而不是Soft编程)

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
发表于 2008-10-2 10:21:00 | 显示全部楼层

Re:模版函数指针,C++委托的实现-原创【申请加精】

//instEvent.h
#ifndef INST_EVENT_H
#define INST_EVENT_H


#include <instDefines.h>
#include <instArray.h>
#include <instList.h>


namespace inst
{


template <typename T> val_class _EventMgr // 必须用用SmartPtr保持强引用,而Peek()之后引用就会解除引用
{
public:
        typedef void (__obj:: *EVENTFUNC)(IDynamic *,const void *);
        typedef struct _EVENTCALL
        {
                void *pObjAddr; // thiscallのために用いられる。
                SmartPtr<IDynamic> pObjRoot; // 参照回数??yのために用いられる。Peek()之后引用就会解除
                SmartPtr<IDynamic> pSender; // Peek()之后引用就会解除
                EVENTFUNC pFunc;
                const void *pArgsAddr;
                SmartPtr<const IDynamic> pArgsRoot;

                Bool operator ==(const _EVENTCALL &o)
                {
                        return pObjAddr==o.pObjAddr && pObjRoot==o.pObjRoot && pSender==o.pSender &&
                                        pFunc==o.pFunc && pArgsAddr==o.pArgsAddr && pArgsRoot==o.pArgsRoot;
                }
                Bool operator !=(const _EVENTCALL &o)
                {
                        return pObjAddr!=o.pObjAddr || pObjRoot!=o.pObjRoot || pSender!=o.pSender ||
                                        pFunc!=o.pFunc || pArgsAddr!=o.pArgsAddr || pArgsRoot!=o.pArgsRoot;
                }

        }EVENTCALL;

private:
        static List<EVENTCALL> s_EventCalls;
        static Int32 s_MaxSize;

public:
        static void Post(const EVENTCALL &ec)
        {
                INST_ASSERT(ec.pObjAddr);
                INST_ASSERT(ec.pObjRoot);
                INST_ASSERT(ec.pSender);
                INST_ASSERT(ec.pFunc);
                INST_ASSERT(ec.pArgsAddr);
                INST_ASSERT(ec.pArgsRoot);

                s_EventCalls.Add(ec); // Peek()之后强引用们就会解除
                if( s_MaxSize>0 && s_EventCalls.Count()>s_MaxSize ) s_EventCalls.Remove(0);
        }
public:
        static Bool Peek()
        {
                if(s_EventCalls.Count()==0) return False;
       
                EVENTCALL ec = s_EventCalls.GetRemove(0);

                INST_ASSERT(ec.pObjAddr);
                INST_ASSERT(ec.pObjRoot);
                INST_ASSERT(ec.pSender);
                INST_ASSERT(ec.pFunc);
                INST_ASSERT(ec.pArgsAddr);
                INST_ASSERT(ec.pArgsRoot);

                INST_THISCALLBACK(ec.pObjAddr, ec.pFunc, void, (const IDynamic *,const void *), (ec.pSender,ec.pArgsAddr));
                return True;
        }
        static void PeekAll()
        {
                while(Peek()){};
        }
        static void SetMaxSize(Int32 maxsize){ s_MaxSize = MAX(maxsize,1); } // 最小为1
};

typedef _EventMgr<void> CEventMgr;

#if _MSC_VER<1400

template <class T> List<_EventMgr<T>::EVENTCALL> _EventMgr<T>::s_EventCalls;
template <class T> Int32 _EventMgr<T>::s_MaxSize = 0; // 0 - no limit

#else

List<CEventMgr::EVENTCALL> CEventMgr::s_EventCalls;
Int32 CEventMgr::s_MaxSize = 0; // 0 - no limit

#endif


template <class A> val_class Event
{
private:
        typedef struct _EVENTHANDLER
        {
                void *pObjAddr;
                IDynamic *pObjRoot; // 这里是weak ptr!这里的IDynamic不是用来增加RefCount的,而是等到RaiseEvent的时候传给EventMgr(他要增加RefCount)。 注意,不用了的话,应当RemoveHandler.
                CEventMgr::EVENTFUNC pFunc; // ...某个FormXXX在destructing的时候,只允许RemoveHandler(自己的),因为控件和他的Handler可能正在别处被使用

                Bool operator ==(const _EVENTHANDLER &o)const{ return pObjAddr==o.pObjAddr && pObjRoot==o.pObjRoot && pFunc==o.pFunc; }
                Bool operator !=(const _EVENTHANDLER &o)const{ return pObjAddr!=o.pObjAddr || pObjRoot!=o.pObjRoot || pFunc!=o.pFunc; }

        }EVENTHANDLER;

        Array<EVENTHANDLER> m_EventHandlers;

public:
        Event():m_EventHandlers(){}

#if _MSC_VER < 1400
        template <class O,class F> void AddHandler(O *obj,F func)
#else
        template <class O> void AddHandler(O *obj,void (O:: *func)(IDynamic *,const A *))
#endif
        {
                INST_ASSERT(obj);
                INST_ASSERT(func);

                EVENTHANDLER eh;
                eh.pObjAddr = reinterpret_cast<void *>(obj);
                eh.pObjRoot = static_cast<IDynamic *>(obj);
                eh.pFunc = union_cast<CEventMgr::EVENTFUNC>(func);

                if(m_EventHandlers.Find(eh))return;
                m_EventHandlers.Add(eh);
        }

#if _MSC_VER < 1400
        template <class O,class F> void RemoveHandler(O *obj,F func)
#else
        template <class O> void RemoveHandler(O *obj,void (O:: *func)(IDynamic *,const A *))
#endif
        {
                INST_ASSERT(obj);
                INST_ASSERT(func);

                EVENTHANDLER eh;
                eh.pObjAddr = reinterpret_cast<void *>(obj);
                eh.pObjRoot = obj; // 自动转换为 SmartPtr<IDynamic *> 为安全绝不可以写强制转换!
                eh.pFunc = union_cast<CEventMgr::EVENTFUNC>(func);

                m_EventHandlers.FindRemove(eh);
        }

        void PostEvent(IDynamic *sender,const A *args) // 允许用户直接传入 new Args~~用户没必要AddRef/Release~~
        {
                INST_ASSERT(sender);
                INST_ASSERT(args);

                CEventMgr::EVENTCALL ec;
                ec.pSender = sender; // SmartPtr,但是,这里是Stack,绝对不会发生自引用或循环引用!
                ec.pArgsAddr = reinterpret_cast<const void *>(args);
                ec.pArgsRoot = static_cast<const IDynamic *>(args);
               
                // 必须要对这些强引用AddRef/Release。否则,如果用户是直接new的话,那么永远不会被删除...
                if(m_EventHandlers.Count()==0) return; // ...上面的ec.pArgsRoot是SmartPtr,所以这里可以直接安全return

                EVENTHANDLER eh;
                for(Int32 i=0; i<m_EventHandlers.Count(); i++)
                {
                        eh=m_EventHandlers.Get(i);

                        ec.pObjAddr = eh.pObjAddr;
                        ec.pObjRoot = eh.pObjRoot;
                        ec.pFunc = eh.pFunc;

                        CEventMgr:ost(ec); // RefCount of Args and Obj, will be added here
                }
               
        }
};


class CNullEventArgs:virtual public IDynamic
{
        INST_DYNAMIC1(L"inst.CNullEventArgs");
protected:
        ~CNullEventArgs(){}
};

typedef Event<CNullEventArgs> CSimpleEvent;

class CGlobalEventSender:virtual public IDynamic // 按要求sender不能为null,所以全局Event用这个类的实例
{
        INST_DYNAMIC1(L"inst.CGlobalEventSender");
protected:
        ~CGlobalEventSender(){}
};

// a sample:
//void Form_MouseDown(IDynamic *sender, const CMouseEventArgs *e);


}//end of namespace inst


#endif

1

主题

6

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2008-10-17 12:49:00 | 显示全部楼层

Re: 模版函数指针,C++委托的实现-原创【申请加精】

C++ Prime 第4版 第7章第9节 函数指针, 第16章 模板与范型编程。
类似技巧和语法规则讲的很透撤。
就LZ的方法而言, 可以有以下方向扩展:
定义不同型参类型和数量,使之适合更多的场合。

14

主题

345

帖子

376

积分

中级会员

Rank: 3Rank: 3

积分
376
QQ
发表于 2008-10-20 19:56:00 | 显示全部楼层

Re:模版函数指针,C++委托的实现-原创【申请加精】

FastDelegate用过, 很不错
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-21 02:03

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表