This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  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.

 

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.