Designing a generic message handler.

Started by
3 comments, last by antareus 19 years, 7 months ago
Ok, i need some suggestions on writing a generic message handler. What i want to do, is make some sort of registration class, which connects simple integers (message ids) to a method of any class i want. Example: Pseudo code

CRegistration::GetInstance()->Register(ID_NAME_CHANGED, Game.DrawName());
CRegistration::GetInstance()->Register(ID_NAME_CHANGED, Network.SendNameToPlayers());
I want to map multiple methods to the same ID, which i can establish by using a std::multimap. Now i can post a message from anywhere in my application and all the registered functions will be called for the ID which whas posted.

CRegistration::GetInstance()->PostMessage(ID_NAME_CHANGED);
The real problem lies in storing the pointer to member functions. So i've come up with another method. The idea is to create an interface with the function operator:

class CFunction
{
public:
    void operator()() = 0;
};
Now i can derive my classes from the CFunction object. The only real big drawback is that i can only support one function of a class. So i can't do this anymore: Pseudo code

CRegistration::GetInstance()->Register(ID_FIRE, Game.PlayAnimation());
CRegistration::GetInstance()->Register(ID_FIRE, Game.CheckCollision());
It's kind of hard to explain, i hope this makes sense and someone knows of a solution to this rather complicated problem. Maybe there's a desing pattern which helps me with this?
Advertisement
On flipcode, there is a tip of the day called Event Binding to Class Methods that covers pretty much exactly what you want I think, but the comments indicate that the boost library has such functionality somewhere and it'd probably be a better idea to use the tried and true boost version.
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
boost::function and boost::bind may be of use to you.
My message handling almost always had a generic IMessageHandler base.

class IMessageHandler{ public:    int handleMessage( int messageID, MsgParam *param ) = 0;};class SoundSystem : public IMessageHandler{ public:    int handleMessage( int messageID, MsgParam *param )     {       switch(messageID)       {        case SOUNDMSG_PLAY:           // play sound, getting paramters from struct          return 1;        case SOUNDMSG_STOP:           // same         return 1;       }            return 0;     }};



Then you'd have a message system which would allow you to register messages...

// MessageSystem::registerMessageHandler( int messageID, IMessageHandler *handler );// example:Game::init(){  SoundSystem *ss = new SoundSystem;  MessageSystem::registerMessageHandler( SOUNDMSG_PLAY, ss );  MessageSystem::registerMessageHandler( SOUNDMSG_STOP, ss );}


Or better yet, allow the system itself to register its own messages...

  class SoundSystem : public IMessageHandler{ public:    SoundSystem()    {    MessageSystem::registerMessageHandler( SOUNDMSG_PLAY, this );    MessageSystem::registerMessageHandler( SOUNDMSG_STOP, this );    }  // ....};


The MessageSystem simply iterates the message queue, dispatching messages to the registered handlers. For extra functionality you could register the handlers based on priority and whether the message is SHARED or EXCLUSIVE, allowing you to have handlers which 'hog' messages (such as a GUI).

Perhaps my method is different to what you were working on, I based mine upon a flipcode article about Console System Handlers by Gaz Iqbal - but it's an idea to get you started.
Boost::Bind and Boost::Function are overkill and a great way to add ten seconds to your compile time. YMMV of course.

class Function{public: virtual ~Function() { } virtual void operator()() = 0;};template<typename T>class ConcreteFunction{public:explicit ConcreteFunction(T* instance, void (T::* memFunc)()) : _instance(instance), _memFunc(memFunc){ } void operator()() {  (_instance->*_memFunc)(); }private: T* _instance; void (T::* _memFunc)();};template<typename T>ConcreteFunction<T>* MakeFunction(T* instance, void (T::* memFunc)()){ return new ConcreteFunction<T>(instance, memFunc);}// usageFoo* foo = new Foo();Function* fn = MakeFunction(foo, &Foo::Bar);(*fn)(); // calls foo's Bar() member function;delete fn;fn = MakeFunction(foo, &Foo::Stuff);(*fn)(); // calls the foo instance's Stuff() member function

Remember to delete the pointers when you're done! Or make a wrapper class to do that for you.

Disclaimers:
Off the top of my head, probably won't compile, etc, etc
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis

This topic is closed to new replies.

Advertisement