Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

837 lines
28 KiB

//+-------------------------------------------------------------------------
//
// 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