|
Event Binding to Class Methods
Submitted by |
There are times, during event sequencing that u may need to "bind"
events to functions ..
eg. ON_MISSILE_HIT_TARGET needs to be bound to sprite object function
OnCollision( ...)
Generally, we us a simple interface, eg "ISpriteNotify" that will
support a "OnNotify(DWORD dwEventID, LPVOID lpData)" method. All events
will be published to relevant objects via this function.
eg.
int CCarSprite::OnNotify(DWORD dwEventId, LPVOID lpData)
{
if (dwEventId == ON_COLLIDE)
{
//Sanity check lpdata ...
COLLISION_STRUCT* pCollideData = (COLLISION_STRUCT*)lpData;
//Act on data ...
}
} |
The design/code that I submit, will "bind" an event to an object, ie
,when event EVENT_X is fired, function OnEventX is automatically
invoked. The great thing about is, is that you dont need to process and
act on events, the CEventBinder class does this for u. What's also
great, is that a function (FUNCTIONX) can be bound to multiple events
(Event1, Event2 etc). Now the interesting bit here, is that the
functions
I speak of, are class methods and not stand alone functions.
eg.
//On StartUp - CCarSprite::BindEvents gets called from CEventBinder ..
HRESULT CCarSprite::BindEvents(CEventBinderInterface<CCarSprite*
pEventBinder)
{
pEventBinder-Bind(ON_COLLIDE, this-OnHit);
return S_OK;
}
...
//In Sprite Manager ..
TestEvent(ON_COLLIDE, (LPVOID)&tCollideData);
...
Goes thru EventBinderManager and gets published to subscribed objects
...
...
bool CCarSprite::OnHit(LPVOID lpData)
{
if (lpData == NULL)
{
return FALSE;
}
COLLISION_STRUCT* pCollideData = (COLLISION_STRUCT*)lpData;
// ... process
} |
Consider this :
// Some Class ...
class Bob
{
public: int FooBar(int AnInt);
}
//Our class function pointer ..
typedef int (CBob::* FPTR)(int SomeInt);
To utilise the function pointer,
...
Bob* p = new Bob;
FPTR fptr = Bob::FooBar;
(p-*fptr)(10);
will invoke "FooBar" in class Bob.Simple !! |
Heres the code for the event binding class:
//This is the main event binding class
template <class T
class CEventBinder
{
public:
CEventBinder();
public:
~CEventBinder();
private: typedef BOOL (T::* FPTR)(LPVOID lpParam=NULL);
public:
virtual BOOL Invoke(DWORD dwEventID, LPVOID lpParam = NULL);
public:
BOOL Bind(DWORD dwEventID, FPTR lpfnpMethod);
private:
struct EVENT_MANAGED_METHODS
{
DWORD dwEventID;
FPTR lpfnBoundMethod;
};
private:
CDynamicArray<EVENT_MANAGED_METHODS* m_pEventMethodList;
T* _class;
};
/////////////////////////////////////////////////////////////
//This class will be passed arround to the contained objects
template <class X
class CEventBinderInterface
{
public:
CEventBinderInterface(CEventBinder<X* pEventBinder);
private: typedef BOOL (X::* FPTR)(LPVOID lpParam=NULL);
public :
BOOL Bind(DWORD dwEventID, FPTR lpfnMethod);
private:
CEventBinder<X* m_pEventBinder;
};
///////////////////////////////////////////////////////////////////
//The Interface that the objects registering for events will support
template <class F
interface IEventBoundObject
{
virtual HRESULT BindEvents(CEventBinderInterface<F* pEventBinder) =
0;
};
#include "CEventBinder.hpp"
#endif
// - HPP
//-- BEGIN CEventBinderInterface --//
template <class X
CEventBinderInterface<X::CEventBinderInterface(CEventBinder<X*
pEventBinder)
{
m_pEventBinder = pEventBinder;
}
template <class X
BOOL CEventBinderInterface<X::Bind(DWORD dwEventID, FPTR
lpfnBoundMethod)
{
return m_pEventBinder-Bind(dwEventID, lpfnBoundMethod);
}
//-- END CEventBinderInterface --//
//-- BEGIN CEventBinder --//
template <class T
CEventBinder<T::CEventBinder()
{
m_pEventMethodList = NULL;
_class = new T;
CEventBinderInterface<T* pEBI = new CEventBinderInterface<T(this);
HRESULT hr = _class-BindEvents(pEBI);
delete pEBI;
return hr;
}
template <class T
CEventBinder<T::~CEventBinder()
{
if (_class)
{
delete _class;
}
if (m_pEventMethodList)
{
m_pEventMethodList-Kill();
m_pEventMethodList = NULL;
}
_class = NULL;
}
// - Used to bind events to objects
template <class T
BOOL CEventBinder<T::Bind(DWORD dwEventID, FPTR lpfnMethod)
{
if (m_pEventMethodList == NULL)
{
m_pEventMethodList = new CDynamicArray<EVENT_MANAGED_METHODS;
}
EVENT_MANAGED_METHODS _new;
_new.dwEventID = dwEventID;
_new.lpfnBoundMethod = lpfnMethod;
return m_pEventMethodList-Add(_new) != NULL;
}
// - Used to Invoke Events
template <class T
BOOL CEventBinder<T::Invoke(DWORD dwEventID, LPVOID lpParam)
{
BOOL fResult = FALSE;
for (int i=1; i<=m_pEventMethodList-GetSize(); i++)
{
EVENT_MANAGED_METHODS* tMethods = m_pEventMethodList-GetAtPtr(i);
if (tMethods-dwEventID == dwEventID)
{
FPTR x = tMethods-lpfnBoundMethod;
fResult |= (_class-*x)(lpParam);
}
}
return i0 && fResult;
} |
USAGE :
class CBob : public IEventBoundObject<CBob) ...
.. //Somebody creating CBob
CEventBinder<CBob* a = new CEventBinder<CBob;
.. //Invoking Events.
a-Invoke(MY_EVENT, (LPVOID)&tEventData); |
NOTE 1 :Because all objects will now be "CEventBinder" objects, they
can be kept in a template list . All u do then
is the ffg.
..
myList-Add(a);
//Invoking Events
for (int i=0; i<myList-Size(); i++)
{
myList-Invoke(MY_EVENT, (LPVOID)&tEventData);
} |
NOTE 2:
The CEventBinder class can be modified to be be a central container
class that keeps all sprite objects. The invoke would then
be :
m_pSprites-Invoke(MY_EVENT, (LPVOID)&tEventData); |
where the CEventBinder class handles the looping publish.
NB : These are just code snippets, they may not compile
Regards
Mark Simon.
|
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
|