Figure 1: The Event and Multicast classes
#if !defined(MULTICAST_H) #define MULTICAST_H #include <windows.h> #include <vector> #include <strstream> #include <set> #include <assert.h> template <unsigned> class NumType {}; typedef NumType<0> Args0; // Use to overload on values // 0, 1, 2, etc. typedef NumType<1> Args1; // typedef'ed for readability and to typedef NumType<2> Args2; // appease VC 5.0 class DefType{}; // Minimal default type // ArgC<T>::Count is 1 for all types except DefType. template <typename T> struct ArgC{enum {Count=1};}; template <> struct ArgC<DefType>{enum {Count=0};}; // The interface to the Event class template <class IObserver> class AbstractEvent { public: typedef AbstractEvent<IObserver> Self; virtual ~AbstractEvent(){} virtual void Invoke(IObserver * p) const = 0; // Compare two events, events are equal if they invoke // the same function. bool operator==(const Self & rhs) const {return Value()== rhs.Value();} protected: typedef void (DefType:: * ValueType)(); virtual ValueType Value() const = 0; }; // Event class holding zero to n parameters, for brevity n = 2; template <class IObserver, typename ObserverFunc, typename P1 = DefType, typename P2 = DefType> class Event : public AbstractEvent<IObserver> { public: explicit Event(ObserverFunc Func, const P1& v1 = P1(), const P2& v2 = P2()) : // Take a copy of the parameters for the event pFunc(Func), p1(v1), p2(v2) {} // Call an observer, by delegating to // the correct implementation virtual void Invoke(IObserver * pObserver) const { const size_t ArgCount = ArgC<P1>::Count + ArgC<P2>::Count; if (pObserver != NULL) InvokeImpl(pObserver, NumType<ArgCount>()); } protected: // Provides a means of comparing events virtual ValueType Value() const { return reinterpret_cast<ValueType>(pFunc); } // These functions will pass the parameters to the // observer. Only one function is instantiated, and only // it is legal. We are effectively overloading on the value // of the ArgCount. template <class T> void InvokeImpl(T * pObserver, Args0) const { (void)(pObserver->*pFunc)(); // Call with 0 parameters } template <class T> void InvokeImpl(T * pObserver, Args1) const { (void)(pObserver->*pFunc)(p1); // Call with 1 parameter } template <class T> void InvokeImpl(T * pObserver, Args2) const { (void)(pObserver->*pFunc)(p1, p2); // Call with 2 params } private: // The member function to call and // the parameters to pass const ObserverFunc pFunc; const P1 p1; const P2 p2; }; // For thread safety, this automatically locks and // unlocks an object. template <class Lockable> class AutoLock { public: AutoLock(Lockable & obj) : m_obj(obj) {m_obj.Lock();} ~AutoLock() {m_obj.Unlock();} private: AutoLock(); // Stop default construction Lockable & m_obj; }; // ** TODO ** Implement CritSec class to provide thread safety struct CritSec {void Lock(){} void Unlock(){}}; typedef AutoLock<CritSec> TSLock; // Thread safe lock template <class IObserver> class Multicast { typedef Multicast<IObserver> Self; typedef std::multiset<IObserver*,std::less<IObserver*> > Observers; typedef std::vector<AbstractEvent<IObserver> *> Events; public: virtual bool OnActive(){return true;} // 1st observer // registered virtual void OnInactive(){} // Last observer deregistered // Called by observers to register bool Advise(IObserver * pObserver) { TSLock write_lock(m_Lock); m_Observers.insert(pObserver); if (m_Observers.size() == 1) // Tell the subject that the if (!OnActive()) { // 1st observer wants to m_Observers.clear(); // register. The subject can return false; // refuse to accept the // registration. } return true; } // Called by observers to deregister void Unadvise(IObserver * pObserver) { TSLock write_lock(m_Lock); m_Observers.erase(pObserver); if (m_Observers.empty()) // Tell the subject that the OnInactive(); // last observer has deregistered } // A window per object is inefficient but it works. Multicast() : m_hWnd(UniqueWindow()) {} // Facilitate copying of derived objects Multicast(const Self &) : m_hWnd(UniqueWindow()) {} Self & operator =(const Self &) {return *this;}; virtual ~Multicast() { for (size_t i = 0; i < m_Events.size(); ++i) delete m_Events[i]; (void) DestroyWindow(m_hWnd); } // Called by the subject to fire events to the observers. // The repetition gives the appearance of two template // functions: // FireEvent(Func, [P1 .. Pn]) // FireSingleEvent(Func, [P1 .. Pn]) // // Func: member function pointer - the observers' function // P1 .. Pn: between 0 & n parameters to pass // to the observers. template <typename FuncType> void FireEvent(FuncType Func) { typedef Event<IObserver, FuncType> EventType; DoEvent(new EventType(Func)); } template <typename FuncType, typename P1> void FireEvent(FuncType Func, const P1& p1) { typedef Event<IObserver, FuncType, P1> EventType; DoEvent(new EventType(Func, p1)); } template <typename FuncType> void FireSingleEvent(FuncType Func) { typedef Event<IObserver, FuncType> EventType; DoSingleEvent(new EventType(Func)); } template <typename FuncType, typename P1> void FireSingleEvent(FuncType Func, const P1& p1) { typedef Event<IObserver, FuncType, P1> EventType; DoSingleEvent(new EventType(Func, p1)); } protected: // Create a new window based on a unique window class. HWND UniqueWindow() const { std::ostrstream Name; Name << "Multicast" << this << '\0'; // Unique class // name WNDCLASSA wc = {0, WndProc, 0, 0, GetModuleHandle(NULL), 0, 0, 0, 0, Name.str()}; (void) RegisterClassA(&wc); HWND hWnd = CreateWindowA(wc.lpszClassName, 0, 0, 0, 0, 0, 0, 0, 0, wc.hInstance,0); assert(hWnd != NULL); return hWnd; } void DoEvent(AbstractEvent<IObserver> * pEvent) { TSLock write_lock(m_Lock); if (m_Events.empty()) PostMessage(m_hWnd, WM_USER+100, 0, (DWORD)this); m_Events.push_back(pEvent); } void DoSingleEvent(AbstractEvent<IObserver> * pEvent) { TSLock write_lock(m_Lock); Events::iterator i; // Search for a pre-existing event for (i = m_Events.begin(); i != m_Events.end(); ++i) if (**i == *pEvent) { // If event is // already scheduled. const AbstractEvent<IObserver> * pOldEvent = *i; *i = pEvent; // Replace old event // with latest event delete pOldEvent; break; } if (i == m_Events.end()) DoEvent(pEvent); } // This function is called by the operating system // asynchronously. static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg != WM_USER+100) return DefWindowProc(hWnd, msg, wParam, lParam); ((Self*)lParam)->AsyncCall(); return 0; } inline void AsyncCall() { TSLock write_lock(m_Lock); // Invoke each event for each observer, then delete it. for (size_t i = 0; i < m_Events.size(); ++i) { const AbstractEvent<IObserver> * pEvent = m_Events[i]; for (Observers::iterator ppObserver = m_Observers.begin(); ppObserver != m_Observers.end(); ++ppObserver) pEvent->Invoke(*ppObserver); delete pEvent; } m_Events.clear(); } private: HWND m_hWnd; // Window handle to enable // an async call CritSec m_Lock; // Thread safe protection for: Events m_Events; // A collection of events to invoke Observers m_Observers; // A collection of registered // observers }; #endif // MULTICAST_H /* End of File */