#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 */