I think my Event class does what you want. It uses a few files, so look it over.
Event.hpp:
// OpenIM - a lightweight, object-oriented instant messaging system.// by the OpenIM team//// This program is free software; you can redistribute it and/or// modify it under the terms of the GNU General Public License// as published by the Free Software Foundation; either version 2// of the License, or (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.#ifndef CORE_EVENTS_EVENT_HPP#define CORE_EVENTS_EVENT_HPP#include <memory>#include "../Export.hpp"#include "../EventManager.hpp"#include "../Handler.hpp"namespace OpenIM{namespace Events{ /// Templated base class for Events /// /// Uses Coplien''s Curiously Recurring Template idiom to automatically generate /// stub code to connect client code to the EventManager. Supports both pointer to /// function and pointer to member functions. The necessary code is injected /// into user-level components by virtue of templates. /// /// Member function template support is necessary to use member function connections. I have /// no interest in supporting poor attempts at compilers. (MSVC 6) If you still use /// MSVC6, use free functions that then invoke the appropriate method on the component. template<class DerivedEventT> class Event { public: /// Member function Handler template<typename ClassT> class Handler : public OpenIM::Events::Handler { public: typedef void (ClassT::* MemberFn)(DerivedEventT&); Handler(ClassT* inst, MemberFn fn) : instance(inst), function(fn) { m_isMemberFunction = true; } void operator()(void* arg) { (instance->*function)(*static_cast<DerivedEventT*>(arg)); } bool operator==(const OpenIM::Events::Handler* rhs) { if(rhs->m_isMemberFunction) { const Event<DerivedEventT>::Handler<ClassT>* h = static_cast<const Event<DerivedEventT>::Handler<ClassT>*>(rhs); return this->instance == h->instance && this->function == h->function; } else return false; } private: ClassT* instance; MemberFn function; }; class HandlerFunction : public OpenIM::Events::Handler { public: typedef void (*HandlerFn)(DerivedEventT&); explicit HandlerFunction(HandlerFn fn) : m_fn(fn) { m_isMemberFunction = false; } void operator()(void* arg) { m_fn(*static_cast<DerivedEventT*>(arg)); } bool operator==(const OpenIM::Events::Handler* rhs) { if(! rhs->m_isMemberFunction) { const Event<DerivedEventT>::HandlerFunction* h = static_cast<const Event<DerivedEventT>::HandlerFunction*>(rhs); return this->m_fn == h->m_fn; } else return false; } private: HandlerFn m_fn; }; static void Attach(void (*function)(DerivedEventT&)) { OpenIM::Core::EventManager::Attach(DerivedEventT::GetName(), new Event<DerivedEventT>::HandlerFunction(function)); } static void Detach(void (*function)(DerivedEventT&)) { std::auto_ptr<Event<DerivedEventT>::HandlerFunction> handler ( new Event<DerivedEventT>::HandlerFunction(function)); OpenIM::Core::EventManager::Detach(DerivedEventT::GetName(), handler.get()); } template<class ClassT> static void Attach(ClassT* instance, void(ClassT::* fn)(DerivedEventT&)) { OpenIM::Core::EventManager::Attach(DerivedEventT::GetName(), new Event<DerivedEventT>::Handler<ClassT>(instance, fn)); } template<class ClassT> static void Detach(ClassT* instance, void(ClassT::* fn)(DerivedEventT&)) { std::auto_ptr<Event<DerivedEventT>::Handler<ClassT> > handler( new Event<DerivedEventT>::Handler<ClassT>(instance, fn)); OpenIM::Core::EventManager::Detach(DerivedEventT::GetName(), handler.get()); } void Raise() { OpenIM::Core::EventManager::Raise(DerivedEventT::GetName(), static_cast<void*>(static_cast<DerivedEventT*>(this))); } };}}#endif
EventManager.cpp
// OpenIM - a lightweight, object-oriented instant messaging system.// by the OpenIM team//// This program is free software; you can redistribute it and/or// modify it under the terms of the GNU General Public License// as published by the Free Software Foundation; either version 2// of the License, or (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.#include "EventManager.hpp"#include <cassert>#include <deque>#include <map>#include "Handler.hpp"#include "Log.hpp"#include "String.hpp"#include "Events/AccountEvents.hpp"#include "Events/ConversationEvents.hpp"#include "Events/ContactEvents.hpp"#include "Events/GroupEvents.hpp"#include "Events/MessageEvents.hpp"#include "Events/SystemEvents.hpp"namespace{ typedef std::deque<OpenIM::Events::Handler*> Handlers; typedef std::map<OpenIM::String, Handlers*> HandlerTable; HandlerTable handlers;}namespace OpenIM{namespace Core{ void EventManager::Startup() { Log::Write(L"EventManager: Starting up\n"); } void EventManager::Shutdown() { Log::Write(L"EventManager: Shutting down\n"); for(HandlerTable::iterator i = handlers.begin(); i != handlers.end(); ++i) { for(Handlers::iterator j = i->second->begin(); j != i->second->end(); ++j) delete *j; delete i->second; } handlers.clear(); } void EventManager::Attach(const wchar_t* name, OpenIM::Events::Handler* handler) { String eventName = name; HandlerTable::iterator i = handlers.find(eventName); if(i == handlers.end()) { // TODO: sucky handlers[eventName] = new Handlers(); i = handlers.find(eventName); } i->second->push_back(handler); } void EventManager::Detach(const wchar_t* name, OpenIM::Events::Handler* handler) { String eventName = name; HandlerTable::iterator i = handlers.find(eventName); if(i != handlers.end()) { for(Handlers::iterator j = i->second->begin(); j != i->second->end(); ++j) { if((**j) == handler) { i->second->erase(j); delete *j; break; } } } } void EventManager::Raise(const wchar_t* name, void* data) { assert(name); assert(data); Log::Write(L"%s\n", name); String eventName = name; HandlerTable::iterator i = handlers.find(eventName); if(i != handlers.end()) { for(Handlers::iterator j = i->second->begin(); j != i->second->end(); ++j) (*j)->operator()(data); } }}}
// OpenIM - a lightweight, object-oriented instant messaging system.// by the OpenIM team//// This program is free software; you can redistribute it and/or// modify it under the terms of the GNU General Public License// as published by the Free Software Foundation; either version 2// of the License, or (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.#ifndef CORE_EVENTS_HANDLER_HPP#define CORE_EVENTS_HANDLER_HPP#include "Export.hpp"#include "Heap.h"namespace OpenIM{namespace Events{ class CORE_API Handler { public: virtual ~Handler() { } virtual bool operator==(const Handler*) = 0; virtual void operator()(void*) = 0; static void* operator new(std::size_t size) { return openim_malloc(size); } static void operator delete(void* instance) { openim_free(instance); } bool m_isMemberFunction; };}}#endif
ContactEvents.hpp - shows example events
// OpenIM - a lightweight, object-oriented instant messaging system.// by the OpenIM team//// This program is free software; you can redistribute it and/or// modify it under the terms of the GNU General Public License// as published by the Free Software Foundation; either version 2// of the License, or (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.#ifndef CORE_EVENTS_CONTACTEVENTS_HPP#define CORE_EVENTS_CONTACTEVENTS_HPP#include "Event.hpp"#include "../Contact.hpp"#include "../Group.hpp"namespace OpenIM{namespace Events{ /// A contact has been added to a specific group. The group may be null. struct ContactCreateEvent : public Event<ContactCreateEvent> { static const wchar_t* GetName() { return L"ContactCreateEvent"; } OpenIM::Contact* Contact; OpenIM::Group* Group; int Position; }; /// A contact has been removed from a group. The group may be null. struct ContactDestroyEvent : public Event<ContactDestroyEvent> { static const wchar_t* GetName() { return L"ContactDestroyEvent"; } OpenIM::Contact* Contact; OpenIM::Group* Group; }; /// A contact has updated their details (such as nickname formatting or status). struct ContactUpdateEvent : public Event<ContactUpdateEvent> { static const wchar_t* GetName() { return L"ContactUpdateEvent"; } OpenIM::Contact* Contact; };}}#endif
Attaching to events
ContactCreateEvent::Attach(this, ContactListComponent::OnContactCreate); ContactUpdateEvent::Attach(this, ContactListComponent::OnContactUpdate); ContactDestroyEvent::Attach(this, ContactListComponent::OnContactDestroy);
Invoking events
ContactCreateEvent e; e.Contact = contact; e.Group = g; e.Position = position; e.Raise();
It uses type erasure (like boost::function) to avoid vtable bloat, which, by the way, is very noticeable when you''re doing stuff like this. I had to write my own functors because I wanted to be able to use operator== on functors.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis