838 lines
28 KiB
C
838 lines
28 KiB
C
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
//
|
||
|
// Copyright (C) Microsoft Corporation, 1999 - 1999
|
||
|
//
|
||
|
// File: events.h
|
||
|
//
|
||
|
// History: 09-24-1999 VivekJ created
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
/************************************************************************
|
||
|
* This file contains classes that provide support for the Observer pattern,
|
||
|
* or publish-subscribe.
|
||
|
*
|
||
|
* Event sources send notifications to event observers. The parameterized type
|
||
|
* is the event observer class, or (C++) interface.
|
||
|
*
|
||
|
* To use this mechanism, derive your event source class from CEventSource
|
||
|
* parameterized by the observer class(es) or interface(es). Add a specific observer object
|
||
|
* to the list of observers by calling the AddObserver function. The observer object class
|
||
|
* should derive from CObserverBase.
|
||
|
*
|
||
|
* When the source wishes to call a particular notification method in the
|
||
|
* observer class, use ScFireEvent template method.
|
||
|
* specify observer's method as a 1st parameter. for instance: CMyObserver::MyEvent
|
||
|
* [optionaly] specify parameters to event as 2nd and following parameters to ScFireEvent
|
||
|
*
|
||
|
* All event observer notification methods MUST return SC's.
|
||
|
*
|
||
|
* You can not have other method with the same name as event on observer - it won't compile.
|
||
|
*
|
||
|
* When sources/observers are deleted, the connection between them is broken automatically.
|
||
|
* There is no support as yet to manually break a connection, although such a method can
|
||
|
* be easily added to either the source or observer (make sure both sides of the connection
|
||
|
* are broken.
|
||
|
*
|
||
|
* See the test code at the end of the file as an example.
|
||
|
************************************************************************/
|
||
|
|
||
|
// a #define to make it easy to implement only some observer methods.
|
||
|
#define DEFAULT_OBSERVER_METHOD SC sc; return sc;
|
||
|
|
||
|
#include "cpputil.h"
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* class CObserverBase
|
||
|
*
|
||
|
*
|
||
|
* PURPOSE:
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
class CObserverBase;
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* class CEventSourceBase
|
||
|
*
|
||
|
*
|
||
|
* PURPOSE: provides general Event Source interface
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
class CEventSourceBase
|
||
|
{
|
||
|
public:
|
||
|
virtual ~CEventSourceBase() {}
|
||
|
virtual void UnadviseSource(CObserverBase &observer) =0;
|
||
|
|
||
|
};
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* class _CEventSource
|
||
|
*
|
||
|
*
|
||
|
* PURPOSE: implements connection between event source and observer
|
||
|
*
|
||
|
* NOTE: This class IS NOT intended for external use - use CEventSource instead
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
template<class CObserver>
|
||
|
class _CEventSource : public CEventSourceBase
|
||
|
{
|
||
|
private:
|
||
|
typedef CObserver * POBSERVER;
|
||
|
typedef _CEventSource<CObserver> ThisClass;
|
||
|
|
||
|
// no std::set, or std::map please
|
||
|
// those cannot be shared by several DLL's
|
||
|
// see KB article Q172/3/96
|
||
|
|
||
|
// since some items may be gone and some new added during the operation,
|
||
|
// std::list is the best choice, since it does not reallocate on insert
|
||
|
// but that is not enough - see CListIntegrityProtector class below
|
||
|
struct ObserverData
|
||
|
{
|
||
|
POBSERVER pObject; // pointer to the object
|
||
|
bool bDeleted; // true means entry remove pending, pObject not valid
|
||
|
};
|
||
|
|
||
|
typedef std::list< ObserverData > CObserverList;
|
||
|
|
||
|
CObserverList m_observerList;
|
||
|
|
||
|
// count of active locks.
|
||
|
// when value is 0, it is safe to remove deleted items from the list.
|
||
|
int m_nStackDepth;
|
||
|
|
||
|
// removes deleted items from the list
|
||
|
void CleanupDeleted();
|
||
|
|
||
|
protected:
|
||
|
typedef CObserverList::iterator iterator;
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* CLASS: CListIntegrityProtector
|
||
|
*
|
||
|
* PURPOSE: locks the list in the owning class from item deletion.
|
||
|
* thus assures the list is safe to iterate, the client just
|
||
|
* needs to skip deleted entries.
|
||
|
* Destructor will release the lock, an in case this is the last
|
||
|
* lock - will perforn the cleanup.
|
||
|
*
|
||
|
* this technique is needed, since objects get deleted and created
|
||
|
* during the events, so the observer list is changed, which
|
||
|
* invalidated the iterators. Using the std::list and postponing
|
||
|
* cleanup allowed to deal with 'live' list safelly.
|
||
|
*
|
||
|
* USAGE: Create the object of this class in scope where you need to
|
||
|
* make sure the items does not get deleted from the list.
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
friend class CListIntegrityProtector;
|
||
|
class CListIntegrityProtector
|
||
|
{
|
||
|
DECLARE_NOT_COPIABLE(CListIntegrityProtector)
|
||
|
DECLARE_NOT_ASSIGNABLE(CListIntegrityProtector)
|
||
|
|
||
|
ThisClass *m_pOwner;
|
||
|
public:
|
||
|
// constructor (increments iterator count)
|
||
|
CListIntegrityProtector(ThisClass *pOwner) : m_pOwner(pOwner)
|
||
|
{
|
||
|
ASSERT(pOwner);
|
||
|
if (pOwner)
|
||
|
++(pOwner->m_nStackDepth);
|
||
|
}
|
||
|
|
||
|
// destructor (decrements iterator count - cleans up if not iterators left)
|
||
|
~CListIntegrityProtector()
|
||
|
{
|
||
|
ASSERT(m_pOwner);
|
||
|
if ( m_pOwner && ( 0 == --(m_pOwner->m_nStackDepth) ) )
|
||
|
m_pOwner->CleanupDeleted();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
protected:
|
||
|
CObserverList & GetObserverList() { return m_observerList;}
|
||
|
|
||
|
public:
|
||
|
_CEventSource() : m_nStackDepth(0) {};
|
||
|
virtual ~_CEventSource(); // disconnects all listeners
|
||
|
|
||
|
void _AddObserver(CObserver &observer);
|
||
|
virtual void UnadviseSource(CObserverBase &observer); // to disconnect a particular observer. (only disconnects this side)
|
||
|
};
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* CLASSES: _CEventSource1 - _CEventSource5
|
||
|
*
|
||
|
* PURPOSE: these classes do not add anything to _CEventSource
|
||
|
* defining them is just required for using them as base classes
|
||
|
* of CEventSource_ [ same class cannot appear twice in base class list]
|
||
|
*
|
||
|
* NOTE: These classes ARE NOT intended for external use
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<class CObserver> class _CEventSource1 : public _CEventSource<CObserver> {};
|
||
|
template<class CObserver> class _CEventSource2 : public _CEventSource<CObserver> {};
|
||
|
template<class CObserver> class _CEventSource3 : public _CEventSource<CObserver> {};
|
||
|
template<class CObserver> class _CEventSource4 : public _CEventSource<CObserver> {};
|
||
|
template<class CObserver> class _CEventSource5 : public _CEventSource<CObserver> {};
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CVoid
|
||
|
*
|
||
|
* This is a do-nothing class that is used for the _CEventSourceX
|
||
|
* specializations below. _CEventSourceX used specializations of "void"
|
||
|
* and the default types for the Es2-Es5 template parameters of CEventSource
|
||
|
* used to be "void". Newer (i.e. Win64) compilers, however, will issue
|
||
|
* C2182 ("illegal use of type 'void'") and won't allow void to be the
|
||
|
* default type.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
class CVoid {};
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* SPECIALIZATIONS: _CEventSource1 - _CEventSource5 for void parameters
|
||
|
*
|
||
|
* PURPOSE: This allows to have single template for different count of observers.
|
||
|
* The specialization defines empty and harmless base clases
|
||
|
* for default template parameters
|
||
|
*
|
||
|
* NOTE: These classes ARE NOT intended for external use
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<> class _CEventSource2<CVoid> {}; // specializes _CEventSource2<CVoid> as empty class
|
||
|
template<> class _CEventSource3<CVoid> {}; // specializes _CEventSource3<CVoid> as empty class
|
||
|
template<> class _CEventSource4<CVoid> {}; // specializes _CEventSource4<CVoid> as empty class
|
||
|
template<> class _CEventSource5<CVoid> {}; // specializes _CEventSource5<CVoid> as empty class
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* CLASS: CEventSource
|
||
|
*
|
||
|
* PURPOSE: This class implements Event emitting capability to be used by
|
||
|
* event-emitter classes derived from it.
|
||
|
* Implements ScFireEvent methods and AddObserver method
|
||
|
*
|
||
|
* USAGE: use spacialization of this class as base class for your event-emitter class
|
||
|
* See the list of possible usage paterns below:
|
||
|
* class CMyClassWithEvents : public CEventSource<CMyObserver> ...
|
||
|
* class CMyClassWithEvents : public CEventSource<CMyObserver1, CMyObserver2> ...
|
||
|
* ...
|
||
|
* class CMyClassWithEvents : public CEventSource<CMyObserver1, CMyObserver2, CMyObserver3, CMyObserver4, CMyObserver5> ...
|
||
|
*
|
||
|
* NOTE: Do not derive from this class more than once - add template parameters instead
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<class Es1, class Es2 = CVoid, class Es3 = CVoid, class Es4 = CVoid, class Es5 = CVoid>
|
||
|
class CEventSource : public _CEventSource1<Es1>,
|
||
|
public _CEventSource2<Es2>, // NOTE: this will be empty class if Es2 == CVoid
|
||
|
public _CEventSource3<Es3>, // NOTE: this will be empty class if Es3 == CVoid
|
||
|
public _CEventSource4<Es4>, // NOTE: this will be empty class if Es4 == CVoid
|
||
|
public _CEventSource5<Es5> // NOTE: this will be empty class if Es5 == CVoid
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* METHOD: CEventSource::ScFireEvent<Observer>
|
||
|
*
|
||
|
* PURPOSE: implements ScFireEvent for parameter-less events
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* SC (Observer::*_EventName)() - pointer to Observer's method
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* SC - result code
|
||
|
*
|
||
|
* NOTE: It must be both declared and defined here - will not compile else
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<class observerclass>
|
||
|
SC ScFireEvent(SC (observerclass::*_EventName)())
|
||
|
{
|
||
|
DECLARE_SC(sc, TEXT("CEventSource::ScFireEvent - no parameters"));
|
||
|
|
||
|
typedef _CEventSource<observerclass> BC;
|
||
|
BC::CListIntegrityProtector(this); // protect the list while iterating
|
||
|
|
||
|
for(BC::iterator iter = BC::GetObserverList().begin(); iter != BC::GetObserverList().end(); ++iter)
|
||
|
{
|
||
|
// skip deleted objects
|
||
|
if (iter->bDeleted)
|
||
|
continue;
|
||
|
|
||
|
// sanity check
|
||
|
sc = ScCheckPointers( iter->pObject, E_UNEXPECTED);
|
||
|
if (sc)
|
||
|
return sc;
|
||
|
|
||
|
// invoke method "_EventName" on object pointed by *iter
|
||
|
sc = ((iter->pObject)->*_EventName)();
|
||
|
if(sc)
|
||
|
return sc;
|
||
|
}
|
||
|
return sc;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* METHOD: CEventSource::ScFireEvent<Observer, P1>
|
||
|
*
|
||
|
* PURPOSE: implements ScFireEvent for events with one parameter
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* SC (Observer::*_EventName)() - pointer to Observer's method
|
||
|
* _P1 p1 - parameter to be passed
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* SC - result code
|
||
|
*
|
||
|
* NOTE: It must be both declared and defined here - will not compile else
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<class observerclass, class _P1>
|
||
|
SC ScFireEvent(SC (observerclass::*_EventName)(_P1 p1), _P1 p1)
|
||
|
{
|
||
|
DECLARE_SC(sc, TEXT("CEventSource::ScFireEvent - one parameter"));
|
||
|
|
||
|
typedef _CEventSource<observerclass> BC;
|
||
|
BC::CListIntegrityProtector(this); // protect the list while iterating
|
||
|
|
||
|
for(BC::iterator iter = BC::GetObserverList().begin(); iter != BC::GetObserverList().end(); ++iter)
|
||
|
{
|
||
|
// skip deleted objects
|
||
|
if (iter->bDeleted)
|
||
|
continue;
|
||
|
|
||
|
// sanity check
|
||
|
sc = ScCheckPointers( iter->pObject, E_UNEXPECTED);
|
||
|
if (sc)
|
||
|
return sc;
|
||
|
|
||
|
// invoke method "_EventName" on object pointed by *iter
|
||
|
sc = ((iter->pObject)->*_EventName)(p1);
|
||
|
if(sc)
|
||
|
return sc;
|
||
|
}
|
||
|
return sc;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* METHOD: CEventSource::ScFireEvent<Observer, P1, P2>
|
||
|
*
|
||
|
* PURPOSE: implements ScFireEvent for events with two parameters
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* SC (Observer::*_EventName)() - pointer to Observer's method
|
||
|
* _P1 p1 - parameter to be passed
|
||
|
* _P2 p2 - parameter to be passed
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* SC - result code
|
||
|
*
|
||
|
* NOTE: It must be both declared and defined here - will not compile else
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<class observerclass, class _P1, class _P2>
|
||
|
SC ScFireEvent(SC (observerclass::*_EventName)(_P1 p1, _P2 p2), _P1 p1, _P2 p2)
|
||
|
{
|
||
|
DECLARE_SC(sc, TEXT("CEventSource::ScFireEvent - two parameters"));
|
||
|
|
||
|
typedef _CEventSource<observerclass> BC;
|
||
|
BC::CListIntegrityProtector(this); // protect the list while iterating
|
||
|
|
||
|
for(BC::iterator iter = BC::GetObserverList().begin(); iter != BC::GetObserverList().end(); ++iter)
|
||
|
{
|
||
|
// skip deleted objects
|
||
|
if (iter->bDeleted)
|
||
|
continue;
|
||
|
|
||
|
// sanity check
|
||
|
sc = ScCheckPointers( iter->pObject, E_UNEXPECTED);
|
||
|
if (sc)
|
||
|
return sc;
|
||
|
|
||
|
// invoke method "_EventName" on object pointed by *iter
|
||
|
sc = ((iter->pObject)->*_EventName)(p1, p2);
|
||
|
if(sc)
|
||
|
return sc;
|
||
|
}
|
||
|
return sc;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* METHOD: CEventSource::ScFireEvent<Observer, P1, P2, P3>
|
||
|
*
|
||
|
* PURPOSE: implements ScFireEvent for events with three parameters
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* SC (Observer::*_EventName)() - pointer to Observer's method
|
||
|
* _P1 p1 - parameter to be passed
|
||
|
* _P2 p2 - parameter to be passed
|
||
|
* _P3 p3 - parameter to be passed
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* SC - result code
|
||
|
*
|
||
|
* NOTE: It must be both declared and defined here - will not compile else
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<class observerclass, class _P1, class _P2, class _P3>
|
||
|
SC ScFireEvent(SC (observerclass::*_EventName)(_P1 p1, _P2 p2, _P3 p3), _P1 p1, _P2 p2, _P3 p3)
|
||
|
{
|
||
|
DECLARE_SC(sc, TEXT("CEventSource::ScFireEvent - three parameters"));
|
||
|
|
||
|
typedef _CEventSource<observerclass> BC;
|
||
|
BC::CListIntegrityProtector(this); // protect the list while iterating
|
||
|
|
||
|
for(BC::iterator iter = BC::GetObserverList().begin(); iter != BC::GetObserverList().end(); ++iter)
|
||
|
{
|
||
|
// skip deleted objects
|
||
|
if (iter->bDeleted)
|
||
|
continue;
|
||
|
|
||
|
// sanity check
|
||
|
sc = ScCheckPointers( iter->pObject, E_UNEXPECTED);
|
||
|
if (sc)
|
||
|
return sc;
|
||
|
|
||
|
// invoke method "_EventName" on object pointed by *iter
|
||
|
sc = ((iter->pObject)->*_EventName)(p1, p2, p3);
|
||
|
if(sc)
|
||
|
return sc;
|
||
|
}
|
||
|
return sc;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* METHOD: CEventSource::ScFireEvent<Observer, P1, P2, P3, P4>
|
||
|
*
|
||
|
* PURPOSE: implements ScFireEvent for events with four parameters
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* SC (Observer::*_EventName)() - pointer to Observer's method
|
||
|
* _P1 p1 - parameter to be passed
|
||
|
* _P2 p2 - parameter to be passed
|
||
|
* _P3 p3 - parameter to be passed
|
||
|
* _P4 p4 - parameter to be passed
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* SC - result code
|
||
|
*
|
||
|
* NOTE: It must be both declared and defined here - will not compile else
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<class observerclass, class _P1, class _P2, class _P3, class _P4>
|
||
|
SC ScFireEvent(SC (observerclass::*_EventName)(_P1 p1, _P2 p2, _P3 p3, _P4 p4), _P1 p1, _P2 p2, _P3 p3, _P4 p4)
|
||
|
{
|
||
|
DECLARE_SC(sc, TEXT("CEventSource::ScFireEvent - three parameters"));
|
||
|
|
||
|
typedef _CEventSource<observerclass> BC;
|
||
|
BC::CListIntegrityProtector(this); // protect the list while iterating
|
||
|
|
||
|
for(BC::iterator iter = BC::GetObserverList().begin(); iter != BC::GetObserverList().end(); ++iter)
|
||
|
{
|
||
|
// skip deleted objects
|
||
|
if (iter->bDeleted)
|
||
|
continue;
|
||
|
|
||
|
// sanity check
|
||
|
sc = ScCheckPointers( iter->pObject, E_UNEXPECTED);
|
||
|
if (sc)
|
||
|
return sc;
|
||
|
|
||
|
// invoke method "_EventName" on object pointed by *iter
|
||
|
sc = ((iter->pObject)->*_EventName)(p1, p2, p3, p4);
|
||
|
if(sc)
|
||
|
return sc;
|
||
|
}
|
||
|
return sc;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* METHOD: CEventSource::ScFireEvent<Observer, P1, P2, P3, P4, P5>
|
||
|
*
|
||
|
* PURPOSE: implements ScFireEvent for events with four parameters
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* SC (Observer::*_EventName)() - pointer to Observer's method
|
||
|
* _P1 p1 - parameter to be passed
|
||
|
* _P2 p2 - parameter to be passed
|
||
|
* _P3 p3 - parameter to be passed
|
||
|
* _P4 p4 - parameter to be passed
|
||
|
* _P5 p5 - parameter to be passed
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* SC - result code
|
||
|
*
|
||
|
* NOTE: It must be both declared and defined here - will not compile else
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<class observerclass, class _P1, class _P2, class _P3, class _P4, class _P5>
|
||
|
SC ScFireEvent(SC (observerclass::*_EventName)(_P1 p1, _P2 p2, _P3 p3, _P4 p4, _P5 p5), _P1 p1, _P2 p2, _P3 p3, _P4 p4, _P5 p5)
|
||
|
{
|
||
|
DECLARE_SC(sc, TEXT("CEventSource::ScFireEvent - three parameters"));
|
||
|
|
||
|
typedef _CEventSource<observerclass> BC;
|
||
|
BC::CListIntegrityProtector(this); // protect the list while iterating
|
||
|
|
||
|
for(BC::iterator iter = BC::GetObserverList().begin(); iter != BC::GetObserverList().end(); ++iter)
|
||
|
{
|
||
|
// skip deleted objects
|
||
|
if (iter->bDeleted)
|
||
|
continue;
|
||
|
|
||
|
// sanity check
|
||
|
sc = ScCheckPointers( iter->pObject, E_UNEXPECTED);
|
||
|
if (sc)
|
||
|
return sc;
|
||
|
|
||
|
// invoke method "_EventName" on object pointed by *iter
|
||
|
sc = ((iter->pObject)->*_EventName)(p1, p2, p3, p4, p5);
|
||
|
if(sc)
|
||
|
return sc;
|
||
|
}
|
||
|
return sc;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************\
|
||
|
*
|
||
|
* METHOD: CEventSource::AddObserver<Observer>
|
||
|
*
|
||
|
* PURPOSE: adds observer to the list
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* Observer &observer - observer to add to the list
|
||
|
*
|
||
|
* RETURNS:
|
||
|
*
|
||
|
* NOTE: It must be both declared and defined here - will not compile else
|
||
|
*
|
||
|
\***************************************************************************/
|
||
|
template<class observerclass>
|
||
|
void AddObserver(observerclass &observer)
|
||
|
{
|
||
|
typedef _CEventSource<observerclass> BC;
|
||
|
// NOTE: if you are getting the error here, probably you are passing a type
|
||
|
// derived from actual Observer class to AddObserver(). Please cast it to appr. type
|
||
|
BC::_AddObserver(observer);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* class CObserverBase
|
||
|
*
|
||
|
*
|
||
|
* PURPOSE:
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
class CObserverBase
|
||
|
{
|
||
|
typedef CEventSourceBase * PEVENTSOURCE;
|
||
|
|
||
|
// no std::set, or std::map please
|
||
|
// those cannot be shared by several DLL's
|
||
|
// see KB article Q172/3/96 (Q172396)
|
||
|
typedef std::list<PEVENTSOURCE> CSourceList; // list of all event sources that this object is connected to.
|
||
|
typedef CSourceList::iterator iterator;
|
||
|
|
||
|
CSourceList m_sourceList;
|
||
|
CSourceList & GetSourceList() { return m_sourceList;}
|
||
|
|
||
|
public:
|
||
|
CObserverBase() {};
|
||
|
virtual ~CObserverBase();
|
||
|
|
||
|
void UnadviseObserver(CEventSourceBase &source); // to disconnect a particular source (this side only)
|
||
|
void _AddSource(CEventSourceBase &source);
|
||
|
|
||
|
void UnadviseAll(); // disconnects all connections - both sides
|
||
|
};
|
||
|
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
//
|
||
|
// Implementation of class CObserverBase
|
||
|
//
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CObserverBase::UnadviseObserver
|
||
|
*
|
||
|
* PURPOSE:
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* CEventSourceBase & source :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
inline void
|
||
|
CObserverBase::UnadviseObserver(CEventSourceBase &source)
|
||
|
{
|
||
|
DECLARE_SC(sc, TEXT("CObserverBase::UnadviseObserver"));
|
||
|
|
||
|
iterator it = std::find( GetSourceList().begin(), GetSourceList().end(), &source );
|
||
|
|
||
|
// check if found
|
||
|
if ( it == GetSourceList().end() )
|
||
|
{
|
||
|
sc = E_UNEXPECTED;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
GetSourceList().erase(it);
|
||
|
}
|
||
|
|
||
|
|
||
|
inline void
|
||
|
CObserverBase::_AddSource(CEventSourceBase &source)
|
||
|
{
|
||
|
GetSourceList().push_back(&source);
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CObserverBase::~CObserverBase
|
||
|
*
|
||
|
* PURPOSE: Destructor
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
inline CObserverBase::~CObserverBase()
|
||
|
{
|
||
|
// disconnect all sources connected to this observer.
|
||
|
iterator iter;
|
||
|
for(iter = GetSourceList().begin(); iter != GetSourceList().end(); iter++)
|
||
|
{
|
||
|
(*iter)->UnadviseSource(*this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
//
|
||
|
// Implementation of class CEventSource
|
||
|
//
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* _CEventSource::_AddObserver
|
||
|
*
|
||
|
* PURPOSE:
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* CObserver & observer :
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
template<class CObserver>
|
||
|
void
|
||
|
_CEventSource<CObserver>::_AddObserver(CObserver &observer)
|
||
|
{
|
||
|
ObserverData observerData = { &observer, false /*bDeleted*/ };
|
||
|
|
||
|
GetObserverList().push_back( observerData );
|
||
|
observer._AddSource(*this);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* _CEventSource::~_CEventSource
|
||
|
*
|
||
|
* PURPOSE: Destructor
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
template<class CObserver>
|
||
|
_CEventSource<CObserver>::~_CEventSource()
|
||
|
{
|
||
|
//disconnect all observers connected to this source
|
||
|
|
||
|
{
|
||
|
CListIntegrityProtector(this); // protect the list while iterating
|
||
|
|
||
|
iterator iter;
|
||
|
for(iter = GetObserverList().begin(); iter != GetObserverList().end(); ++iter)
|
||
|
{
|
||
|
if (!iter->bDeleted && iter->pObject)
|
||
|
iter->pObject->UnadviseObserver(*this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ASSERT( m_nStackDepth == 0 );
|
||
|
|
||
|
GetObserverList().clear();
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* _CEventSource<CObserver>::CleanupDeleted()
|
||
|
*
|
||
|
* PURPOSE: removes entries marked as deleted
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
template<class CObserver>
|
||
|
void _CEventSource<CObserver>::CleanupDeleted()
|
||
|
{
|
||
|
ASSERT ( m_nStackDepth == 0 );
|
||
|
|
||
|
for(iterator iter = GetObserverList().begin(); iter != GetObserverList().end();)
|
||
|
{
|
||
|
if (iter->bDeleted)
|
||
|
iter = GetObserverList().erase( iter );
|
||
|
else
|
||
|
++iter; // valid - skip
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CEventSource::UnadviseSource
|
||
|
*
|
||
|
* PURPOSE:
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* CObserver & observer :
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
template<class CObserver>
|
||
|
void
|
||
|
_CEventSource<CObserver>::UnadviseSource(CObserverBase &observer)
|
||
|
{
|
||
|
// we cannot dynamic cast the observer pointer to a CObserver pointer,
|
||
|
// becuase UnadviseSource is called from ~CObserverBase(), by which time
|
||
|
// the CObserver sub-object has already been deleted. Instead, we search for
|
||
|
// the CObserverBase pointer in the Observer List.
|
||
|
iterator iter, iterNext;
|
||
|
bool bFound = false;
|
||
|
|
||
|
CListIntegrityProtector(this); // protect the list while iterating
|
||
|
|
||
|
for(iter = GetObserverList().begin(); iter != GetObserverList().end(); iter++)
|
||
|
{
|
||
|
if(!iter->bDeleted && static_cast<CObserverBase *>(iter->pObject) == &observer)
|
||
|
{
|
||
|
// do not alter the list directly, let the cleanup do the work
|
||
|
// just mark the entry as invalid
|
||
|
iter->bDeleted = true;
|
||
|
iter->pObject = NULL;
|
||
|
bFound = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ASSERT(bFound);
|
||
|
}
|
||
|
|
||
|
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
//
|
||
|
// stoopid test code
|
||
|
//
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
#ifdef TEST_EVENTS
|
||
|
|
||
|
class CTestObserver : public CObserverBase
|
||
|
{
|
||
|
public:
|
||
|
SC ScMyEvent(int a)
|
||
|
{
|
||
|
ASSERT(0 && "Reached here!");
|
||
|
return S_OK;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class CTestObserver2 : public CObserverBase
|
||
|
{
|
||
|
public:
|
||
|
SC ScMyEvent(int a)
|
||
|
{
|
||
|
ASSERT(0 && "Reached here!");
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
SC ScMyEvent2(int a,int d)
|
||
|
{
|
||
|
ASSERT(0 && "Reached here!");
|
||
|
return S_OK;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class CTestObserver3 : public CObserverBase
|
||
|
{
|
||
|
public:
|
||
|
SC ScMyOtherEvent()
|
||
|
{
|
||
|
ASSERT(0 && "Reached here!");
|
||
|
return S_OK;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class CTestEventSource : public CEventSource<CTestObserver,CTestObserver2, CTestObserver3>
|
||
|
{
|
||
|
public:
|
||
|
void FireEvent()
|
||
|
{
|
||
|
DECLARE_SC(sc, TEXT("FireEvent"));
|
||
|
|
||
|
sc = ScFireEvent(CTestObserver::ScMyEvent, 42 /*arg1*/);
|
||
|
sc = ScFireEvent(CTestObserver2::ScMyEvent2, 42 /*arg1*/, 24 /*arg1*/);
|
||
|
sc = ScFireEvent(CTestObserver3::ScMyOtherEvent);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
static void DoEventTest()
|
||
|
{
|
||
|
CTestEventSource source;
|
||
|
CTestObserver observer_1;
|
||
|
CTestObserver2 observer_2;
|
||
|
CTestObserver3 observer_3;
|
||
|
|
||
|
|
||
|
source.AddObserver(observer_2);
|
||
|
source.AddObserver(observer_3);
|
||
|
source.AddObserver(observer_1);
|
||
|
source.FireEvent(); // should fire to observer1 only.
|
||
|
|
||
|
{
|
||
|
// new scope
|
||
|
CTestObserver observer2;
|
||
|
source.AddObserver(observer2);
|
||
|
source.FireEvent(); // should fire to observer1 and observer2.
|
||
|
// observer2 is deleted here.
|
||
|
}
|
||
|
|
||
|
source.FireEvent(); // should fire to observer1 only.
|
||
|
|
||
|
}
|
||
|
|
||
|
class CTestObject
|
||
|
{
|
||
|
public:
|
||
|
CTestObject()
|
||
|
{
|
||
|
DoEventTest();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
#endif
|