Click here to Skip to main content
15,896,063 members
Articles / Desktop Programming / MFC

Simple Events System

Rate me:
Please Sign up or sign in to vote.
4.00/5 (6 votes)
2 Dec 20021 min read 70.2K   1.8K   36  
This code is based on Jeffry Ritcher "Applicate .NET Framework"
// Events.h: interface for the events.
//
//////////////////////////////////////////////////////////////////////
//*******************************************************************
//*******************************************************************
//	File:		Events.h					2002NOV17
//
//	Created by: Manuel Alejandro Gomez
//	E-mail:		nsx@tutopia.com
//*******************************************************************
//	This code may be used in compiled form in any way you desire. This
//	file may be redistributed unmodified by any means PROVIDING it is
//	not sold for profit without the authors written consent, and
//	providing that this notice and the authors name and all copyright
//	notices remains intact.
//
//	An email letting me know how you are using it would be nice as well.
//
//	This file is provided "as is" with no expressed or implied warranty.
//	The author accepts no liability for any damage/loss of business that
//	this product may cause.
//******************************************************************* 
#if !defined(MAG_Events_H__0_0_001_INCLUDED)
#	define MAG_Events_H__0_0_001_INCLUDED

#	if _MSC_VER > 1000
#		pragma once
#	endif // _MSC_VER > 1000

// Extracting from ATLBASE.H
#	pragma warning(disable: 4201) // nameless unions are part of C++
#	pragma warning(disable: 4127) // constant expression
#	pragma warning(disable: 4505) // unreferenced local function has been removed
#	pragma warning(disable: 4512) // can't generate assignment operator (so what?)
#	pragma warning(disable: 4514) // unreferenced inlines are common
#	pragma warning(disable: 4103) // pragma pack
#	pragma warning(disable: 4702) // unreachable code
#	pragma warning(disable: 4237) // bool
#	pragma warning(disable: 4710) // function couldn't be inlined
#	pragma warning(disable: 4355) // 'this' : used in base member initializer list
#	pragma warning(disable: 4097) // typedef name used as synonym for class-name
#	pragma warning(disable: 4786) // identifier was truncated in the debug information
#	pragma warning(disable: 4268) // const static/global data initialized to zeros
#	pragma warning(disable: 4291) // allow placement new

#	include <windows.h>
#	include <CrtDbg.h>

// STL files using for events system
#	include <iostream>
#	include <vector>
#	include <map>

using namespace std;

// Aligment
#	ifdef  _MSC_VER
#		pragma pack(push,8)
#	endif  /* _MSC_VER */

// Namespace declaration for including a more extensive
// class helpers

#	if defined(__cplusplus)
#		define _EVENTS			Events::
#		define _EVENTS_BEGIN	namespace Events {
#		define _EVENTS_END		};
#		define _EVENTS_USING	using namespace Events;
#	else
#		define _EVENTS			::
#		define _EVENTS_BEGIN
#		define _EVENTS_END
#		define _EVENTS_USING
#	endif /* __cplusplus */

// Begin namespace for events if you use this in a simple
// system you could remove this declartion for direct use
_EVENTS_BEGIN

// Base structs for polimorphics use.
struct EventArgs { };
class SourceEvents;

// Interface for Delegate class use for polimorphic.
struct IDelegate {
	// Notificator for the subscription join events with
	// events providers
	virtual	void Notify(SourceEvents*, EventArgs*)	= 0;
	// Using for a simple ID system
	virtual	bool Match(long)						= 0;
	// Return a Code ID for a Delegate class
	virtual	long Code()								= 0;
};

// if you use this with MFC inheritance SourceEvents for CObject
class SourceEvents /* : public CObject */{ 
// Attributes
protected: 
	// Events Map definition
	typedef vector<IDelegate*> LIST; 
	typedef LIST* LISTPTR; 
	typedef map< LPTSTR, LISTPTR > EVENTMAP; 
	typedef EVENTMAP::value_type VALUEMAP; 
	// Define a Event handler
	EVENTMAP m_EventsHandler; 

// Methods
protected: 
	// is a abstract class
	SourceEvents()	{ }
	~SourceEvents() { UninitEvents(); }

	// Initialization of EventsHandler take action in 
	// inherited class using MACROS _BEGIN_EVENTS_HANDLER, _EVENT_HANDLER
	// and _END_EVENTS_HANDLER
	virtual void InitEvents() = 0;
	// Destroy all events slots
	void UninitEvents() {
		EVENTMAP::iterator end = m_EventsHandler.end();
		for(EVENTMAP::iterator it = m_EventsHandler.begin(); it != end; it++ ) { 
			// If correct unsubscription method was do, free extramemory for events system
			if( 0 == (*it).second->size() ) { 
				it->second->clear(); 
				delete it->second; 
			} 
			// If no correct unsubscription methos was do, WARNING 
			else { 
				cerr << "El evento " << (*it).first << " no fue liberado (UnJoinTo -> JoinTo( ..., false)\n"; 
				cerr << "memory leaks was reported" << endl;
			} 
		} 
		m_EventsHandler.clear(); 
	} 
	// Fired method
	void Fire(LPTSTR eventName, SourceEvents* obj, EventArgs* e) { 
		LISTPTR lista = m_EventsHandler[eventName]; 
		_ASSERTE(NULL != lista); 
		LIST::iterator end = lista->end(); 
		for(LIST::iterator it = lista->begin(); it != end; it++) { 
			(*it)->Notify(obj, e); 
		} 
	} 

public:
	// For subscription method
	void Attach(LPTSTR eventName, IDelegate* del) {  
		LISTPTR lista = m_EventsHandler[eventName]; 
		_ASSERTE(NULL != lista); 
		lista->insert( lista->end(), del ); 
	} 
	// For un subscription method
	void Detach(LPTSTR eventName, IDelegate* del) {  
		LISTPTR lista = m_EventsHandler[eventName]; 
		_ASSERTE(NULL != lista); 
		LIST::iterator end = lista->end(); 
		// un subscription a object and delete a delegator
		for(LIST::iterator it = lista->begin(); it != end; it++) { 
			IDelegate* deg = *it; 
			if( deg->Match( del->Code() ) ) { 
				lista->erase(it); 
				delete deg; 
			} 
		} 
		delete del; 
	}
};

// Real Delegate class implement the interface of IDelegate
// and is a template class for improve flexibility
template< typename _source, typename _target, typename _args >
class Delegate : public IDelegate {
public:
	typedef void (_target::* PFN)(_source*, _args*);
	typedef Delegate<_source, _target, _args> CTYPE;
	typedef CTYPE* POINTER;
	typedef CTYPE& REFERENCE;
	
	Delegate(_target* obj, PFN pFn) : m_Obj(*obj), m_pFn(pFn) {
		_ASSERTE(pFn);
		// A simple ID generator, one class only catch a event of
		// certain type once.
		m_code = reinterpret_cast<long>( static_cast<void*>(obj) );
	}

	void Notify(SourceEvents* obj, EventArgs* e) {
		// Notify a event occur
		(m_Obj.*m_pFn)(static_cast<_source*>(obj), static_cast<_args*>(e));
	}
	// Return a ID for thos Delegate
	long Code() { return m_code; }
	// Compare ID for this class with other ID
	bool Match(long code) { return (code == m_code); }

protected:
	long m_code;
	PFN	m_pFn;
	_target& m_Obj;
};

// Ending Events namespace
_EVENTS_END;
/**** Complexity reduction *****************************************
For using this simple event system there are task that can be 
automated using MACROS.

This MACROS provide a way for ease use of this system.
********************************************************************/

// MACRO:	_BEGIN_EVENTS_HANDLER
//
// This MACRO define the initialization for events.
//
// Syntax: _BEGIN_EVENTS_HANDLER
//
/* Example code generated by this macho:
	constructor() : SourceEvents() { InitEvents(); }
	...
	...
	...
	_BEGIN_EVENTS_HANDLER --> void InitEvents() { ...
*/
#define _BEGIN_EVENTS_HANDLER void InitEvents() {

// MACRO:	_EVENTS_HANDLER( _name )
//
// were _name is the name of event
//
// This MACRO define the initialization for events.
//
// Syntax: _EVENTS_HANDLER( Close )
//
/* Example code generated by this macho:
	constructor() : SourceEvents() { InitEvents(); }
	...
	void InitEvents() { 
		_EVENTS_HANDLER( Close ) --> m_EventsHandler.insert( SourceEvents::VALUEMAP( "Close", new SourceEvents::LIST()) );
		...
*/
#define _EVENT_HANDLER( _name ) m_EventsHandler.insert( SourceEvents::VALUEMAP( #_name, new SourceEvents::LIST()) );

// MACRO:	END_EVENTS_HANDLER
//
// This MACRO define the end for initialization events.
//
// Syntax: END_EVENTS_HANDLER
//
/* Example code generated by this macho:
	constructor() : SourceEvents() { InitEvents(); }
	...
	void InitEvents() { 
		m_EventsHandler.insert( SourceEvents::VALUEMAP( "Close", new SourceEvents::LIST()) );
		...
	_END_EVENTS_HANDLER  --> }
*/
#define _END_EVENTS_HANDLER }

// MACRO:	_raise( _name, _arg )
//
// _name is the event name, if the name is incorrect an assert is throw
// _arg is the event struct arguments, a pointer to struct or class inherited 
//      from EventArgs and contain all the data needed for the subscription
//
// This MACRO define the end for initialization events.
//
// Syntax: _raise( Close, e )
//
/* Example code generated by this macho:
class Document : public SourceEvents {
	void OnClose() { 
		DocumentEventArgs e;
		e.Pagina = m_Pagina;
		e.nPaginas = m_nPaginas;
		...
		_raise( Close, &e ); --> Fire( "Close", this, &e );
		...
	}
*/
#define _raise( _name, _arg ) Fire( #_name, this, _arg );

// MACRO:	_hook( _src_ptr, _name, _source, _target, _eventargs, _fn )
//
// _src_ptr name of pointer to Source events class
// _name is the event name, if the name is incorrect an assert is throw
// _source is the class name of source events
// _target is the class name of target events
// _eventargs is the struct or class name for events arguments
// _fn if the name of the catch events member function 
//
// This MACRO is for internal use.
//
// Syntax: _hook( pObj, Close, Source, Target, SourceEventArgs, OnClose )
//
/* Example code generated by this macho:
class Document : public SourceEvents {
	void OnClose() { 
		DocumentEventArgs e;
		e.Pagina = m_Pagina;
		e.nPaginas = m_nPaginas;
		...
		_raise( Close, &e );
		...
	}

	void JoinTo(Source* pObj) {
		_hook( pObj, Close, Source, Target, SourceEventArgs, OnClose ); --> \
			pObj->Attach( "Close", new Delegate<Source, Target, SourceEventArgs>(this, (void(Target::*)(Source*, SourceEventArgs*)) OnClose) );
		...
	}
*/
#define _hook( _src_ptr, _name, _source, _target, _eventargs, _fn ) \
_src_ptr->Attach( #_name, new Delegate<_source, _target, _eventargs>(this, (void(_target::*)(_source*, _eventargs*)) _fn) )

// MACRO:	_unhook( _src_ptr, _name, _source, _target, _eventargs, _fn )
//
// _src_ptr name of pointer to Source events class
// _name is the event name, if the name is incorrect an assert is throw
// _source is the class name of source events
// _target is the class name of target events
// _eventargs is the struct or class name for events arguments
// _fn if the name of the catch events member function 
//
// This MACRO is for internal use.
//
// Syntax: _unhook( pObj, Close, Source, Target, SourceEventArgs, OnClose )
//
/* Example code generated by this macho:
class Document : public SourceEvents {
	void OnClose() { 
		DocumentEventArgs e;
		e.Pagina = m_Pagina;
		e.nPaginas = m_nPaginas;
		...
		_raise( Close, &e );
		...
	}

	void UnJoinTo(Source* pObj) {
		_unhook( pObj, Close, Source, Target, SourceEventArgs, OnClose ); --> \
			pObj->Detach( "Close", new Delegate<Source, Target, SourceEventArgs>(this, (void(Target::*)(Source*, SourceEventArgs*)) OnClose) );
		...
	}
*/
#define _unhook( _src_ptr, _name, _source, _target, _eventargs, _fn ) \
_src_ptr->Detach( #_name, new Delegate<_source, _target, _eventargs>(this, (void(_target::*)(_source*, _eventargs*)) _fn) )

// MACRO:	_DECLARE_HOOK_LIST( _source )
//
// _source is the class name of source events
//
// This MACRO is for use in target class for implementing the Join 
// and Unjoin methods.
//
// Syntax: _DECLARE_HOOK_LIST( Document )
//
/* Example code generated by this macho:
class View {
	void OnClose(Document* source, DocumentEventArgs* e) { 
		...
	}

	_DECLARE_HOOK_LIST( Document )
*/
#define _DECLARE_HOOK_LIST( _source ) void JoinTo(_source* pObj, bool join = true);

// MACRO:	_BEGIN_HOOK_LIST( _source, _target, _eventargs )
//
// _source is the class name of source events
// _target is the class name of target events
// _eventargs is the struct or class name for events arguments
//
// This MACRO is for use in target class for implementing the Join 
// and Unjoin methods.
//
// Syntax: _BEGIN_HOOK_LIST( Document, View, DocumentEventArgs )
//
/* Example code generated by this macho:
class View {
	void OnClose(Document* source, DocumentEventArgs* e) { 
		...
	}

	_BEGIN_HOOK_LIST(Document, View, DocumentEventArgs) -->
		void JoinTo(Document* pObj, bool join = true) {
		...
	}
*/
#define _BEGIN_HOOK_LIST( _source, _target, _eventargs ) \
	void _target::JoinTo(_source* pObj, bool join) { \
	typedef _source SOURCE; \
	typedef _target TARGET; \
	typedef _eventargs ARGS;


// MACRO:	_HOOK( _name, _fn )
//
// _name is the name of the event
// _fn is the name of the function events (callback)
//
// This MACRO is for use in target class for implementing the Join 
// and Unjoin methods.
//
// Syntax: _HOOK( Close, OnClose )
//
/* Example code generated by this macho:
class View {
	void OnClose(Document* source, DocumentEventArgs* e) { 
		...
	}

	_BEGIN_HOOK_LIST(Document, View, DocumentEventArgs)
		_HOOK( Close, OnClose ) --> 	if(join) _hook( pObj, Close, SOURCE, TARGET, ARGS, OnClose ); \
										else _unhook( pObj, Close, SOURCE, TARGET, ARGS, OnClose ); 
		...
	}
*/
#define _HOOK( _name, _fn ) \
	if(join) _hook( pObj, _name, SOURCE, TARGET, ARGS, _fn ); \
	else _unhook( pObj, _name, SOURCE, TARGET, ARGS, _fn ); 

// MACRO:	_END_HOOK_LIST
//
// This MACRO is for use in target class for implementing the Join 
// and Unjoin methods.
//
// Syntax: _END_HOOK_LIST
//
/* Example code generated by this macho:
class View {
	void OnClose(Document* source, DocumentEventArgs* e) { 
		...
	}

	_BEGIN_HOOK_LIST(Document, View, DocumentEventArgs)
		_HOOK( Close, OnClose )
	_END_HOOK_LIST()
	...
*/
#define _END_HOOK_LIST() }

// MACRO:	UnJoinTo( _name )
//
// _name is the name of the events
//
// This MACRO is for use in target class for implementing the Join 
// and Unjoin methods.
//
// Syntax: pTarget->UnJoinTo(pSource)
//
/* Example code generated by this macho:
	...
	pTarget->UnJoinTo(pSource) --> pTarget->JoinTo(pSource, false);
*/
#define UnJoinTo( _name ) JoinTo( _name, false)


#endif // End of MAG_Events_H__0_0_001_INCLUDED

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Colombia Colombia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions