fredag den 28. januar 2011

simple event system

Hey all

I'm sick and what a time for updating some code. I've updatd my generic event handler to be completely self-contained. This means that we can deliver a specific payload from a specific function to any amount of listeners - without doing external type registration. I also found this nice tool for escaping text for use in html environments here.

I found this super simple Type2Int function that runtime maps types to ints. With that tool it's pretty simply (compile errors aside) to rewrite my old enum-based manager.

The code still has the limitation of not supporting member function listeners, but supporting that should be as simple as either converting to your favorite function wrapper or overloading these methods with member pointer syntax.

The example at the bottom should get my point across.


#include <iostream>
#include <vector>
#include <assert.h>
#include <map>


// ---------------------------------------------------------------------------
// Framework

inline size_t NextUnique()
{
static size_t id = 0;
size_t result = id;
++id;
return result;
}

/** Returns a small value which identifies the type.
Multiple calls with the same type return the same value. */
template <typename T>
size_t Type2Int()
{
const static size_t id = NextUnique();
return id;
}

// Manager class never needs to be updated.
class ZManager
{
public:
template<typename T>
void RegisterListener( void(*fpListener)(const T*) )
{
const size_t nType = Type2Int<T>();
// convert callback to void* for storage.
void(*fpStorage)(void*)  = (void(*)(void*))fpListener;
m_Listeners[nType].push_back( fpStorage );
}

template<typename T>
void RecieveEvent( const T* Args ) const
{
const size_t nType = Type2Int<T>();
TypeCallbackMap::const_iterator itCallbackList = m_Listeners.find( nType );
if( itCallbackList == m_Listeners.end() )
{
// unregistered argument type
return;
}
const CallbackList& aListeners = itCallbackList->second;
for( size_t i = 0; i < aListeners.size(); ++i )
{
// convert callback argument type from void* to T*;
void(*fpListener)(const T*) = (void(*)(const T*)) aListeners[i];
fpListener( Args );
}
}

private:
// array of listeners
//typedef void (*CBType)(void*); // void* arguments to casted at registration.
typedef std::vector<void(*)(void*)> CallbackList;
typedef std::map<size_t, CallbackList> TypeCallbackMap;
TypeCallbackMap m_Listeners;
};


// ---------------------------------------------------------------------------
// Example

struct  ZFruitEventArgs
{
// real args go here
float m_fTastines;
int m_nHowManyMore;
};

void TastyPrinter( const ZFruitEventArgs* Args )
{
std::cout << "mmm, tasty: " << Args->m_fTastines << std::endl;
}

void ManyPrinter( const ZFruitEventArgs* Args )
{
std::cout << "more: " << Args->m_nHowManyMore << std::endl;
}

struct A{};

int main()
{
ZManager TheMan;
TheMan.RegisterListener<ZFruitEventArgs>( TastyPrinter );
TheMan.RegisterListener<ZFruitEventArgs>( ManyPrinter );
//TheMan.RegisterListener<A>( ManyPrinter ); // Does not compile due to mismatching signatures :)

ZFruitEventArgs Fruity;
Fruity.m_fTastines = 1.2f;
Fruity.m_nHowManyMore = 6;

TheMan.RecieveEvent( &Fruity );

A a;
TheMan.RecieveEvent( &a ); // Does not do anything since A is not registered to a reciever.
}