[C++] Observer pattern - any existing templates that gives a C# style solution?

Started by
10 comments, last by paulecoyote 18 years, 9 months ago
Hi, I'm coming to a point in my 4E4 entry where the Observer pattern looks like it could be useful. Now I was C++ dude for years and years way before I was in to C#, but C# has spoiled me in a number of ways... one of which is it's excellent event & delegate mechanisms: MSDN Explains Rather then rolling my own set of concrete observers and subjects in good 'ol C++ I was wondering if anyone had come across a C++ implementation in the style of what C# has done... because I really like how C# has done it.
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Advertisement
I believe you might find some of the Boost libraries useful, particularly boost::function and boost::signals.
Just a question: why aren't you using C# for this? [grin]
Quote:Original post by Daniel Miller
Just a question: why aren't you using C# for this? [grin]


Ah well, I could be using C# but thats what I do all day at work, I don't use C++ so much here anymore - the last big project I did was a replacement Gina (winlogin gui) for a resource bookings solution.

Anyway I want to make sure my C++ skills are still up to scratch. It's actually been very educational going back to C++, I'm taking a lot back with me from C# and my C++ code is better. Though that may be because of the extra years of experience too and all that.

My 4E4 entry is me keeping touch with my inner C++ developer [wink].

Rather then reinvent the wheel though, I thought I would hunt around for a mechanism in C++ that was a bit like C#s which I quite like... I'll have a look in to that boost library like the other poster suggested.

EDIT: Had a quick look through those boost functions... interesting still not quite what I had in mind - though very close. clicky was pretty interesting. The use of terminalogy is also interesting, "slots" and "signals"... terms that make me think of threads for some reason.
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Yea, I actually know what you mean. I miss C++ as well (templates were so fun), but I am a slave to events and the GC. [grin]
Quote:Original post by paulecoyote
EDIT: Had a quick look through those boost functions... interesting still not quite what I had in mind - though very close. clicky was pretty interesting. The use of terminalogy is also interesting, "slots" and "signals"... terms that make me think of threads for some reason.


Your little clicky is also, as far as I know, the boost equivilant of events/delegates.

*time passes*

I got sidetracked in boredom to converting this example for events/delegates into C++ using the boost libraries. I tried to make it nearly analagous, so you can open both up side by side and see how everything corresponds.

It's not an exact match (I'm not using managed C++, this compiles and runs successfully on MinGW's GCC 3.4.2 on WinXP), but it should get the idea across.

#include <iostream>#include <string>#include <vector>#include <windows.h> //for Sleep#include <boost/signal.hpp>#include <boost/bind.hpp>namespace EventSample {	class Object {	public:		virtual ~Object( void ) {} //make polymorphic, allows dynamic_cast later on.	};		class AlarmEventArgs {	private:		bool snoozePressed;		int nrings;	public:		AlarmEventArgs( bool snoozePressed , int nrings )			: snoozePressed( snoozePressed )			, nrings( nrings )		{		}				int NumRings( void ) const {			return nrings;		}		bool SnoozePressed( void ) const {			return snoozePressed;		}		std::string AlarmText( void ) const {			if ( snoozePressed ) {				return "Wake Up!!! Snooze time is over.";			} else {				return "Wake up!";			}		}	};		typedef boost::signal< void ( Object & , AlarmEventArgs & ) > AlarmEventHandler;		class AlarmClock : public Object {	private:		bool snoozePressed;		int nrings;		bool stop;	public:		AlarmClock( void )			: snoozePressed( false )			, nrings( 0 )			, stop( false )		{		}				bool Stop( void ) const {			return stop;		}		void Stop( bool value ) {			stop = value;		}		bool SnoozePressed( void ) const {			return snoozePressed;		}		void SnoozePressed( bool value ) {			snoozePressed = value;		}		AlarmEventHandler Alarm;	protected:		virtual void OnAlarm( AlarmEventArgs & e ) {			Alarm( *this , e );		}	public:		void Start( void ) {			while ( true ) {				++nrings;				if ( stop ) {					break;				} else if ( snoozePressed ) {					Sleep( 1000 ); //API Dependant... sleep( 1 ) for POSIX					{						AlarmEventArgs e( snoozePressed , nrings );						OnAlarm( e );					}				} else {					Sleep( 300 ); //API Dependant...					{						AlarmEventArgs e( snoozePressed , nrings );						OnAlarm( e );					}				}			}		}	};		class WakeMeUp {	public:		void AlarmRang( Object & sender , AlarmEventArgs & e ) {			std::cout << e.AlarmText() << std::endl;			if ( ! (e.SnoozePressed()) ) {				if ( e.NumRings() % 10 == 0 ) {					std::cout << " Let alarm ring? Enter Y" << std::endl;					std::cout << " Press Snooze? Enter N" << std::endl;					std::cout << " Stop Alarm? Enter Q" << std::endl;					std::string input;					std::getline( std::cin , input );										if ( (input == "Y") || (input == "y") ) return;					else if ( (input == "N") || (input == "n") ) {						dynamic_cast< AlarmClock & >( sender ).SnoozePressed( true ); //note: this will throw if sender isn't an AlarmClock!						return;					} else {						dynamic_cast< AlarmClock & >( sender ).Stop( true );						return;					}				}			}			else {				std::cout << " Let alarm ring? Enter Y" << std::endl;				std::cout << " Stop Alarm? Enter Q" << std::endl;				std::string input;				std::getline( std::cin , input );				if ( (input == "Y") || (input == "y") ) return;				else {					dynamic_cast< AlarmClock & >( sender ).Stop( true );					return;				}			}		}		//NEW: AlarmRangHandler makes connecting new WakeMeUp::AlarmRang's easier		class AlarmRangHandler {			WakeMeUp & w;		public:			AlarmRangHandler( WakeMeUp & w )				: w( w )			{			}			void operator()( Object & sender , AlarmEventArgs & e ) const {				w.AlarmRang( sender , e );			}		};	};		void Main( std::vector< std::string > args ) {		WakeMeUp w;		AlarmClock clock;		//OLD: Nastyish syntax, but it will work:		//clock.Alarm.connect( boost::bind( &WakeMeUp::AlarmRang , &w , _1 , _2 ) );		//NEW: Using AlarmRangHandler:		clock.Alarm.connect( WakeMeUp::AlarmRangHandler( w ) );		clock.Start();	}}int main ( int argc , char * argv[] ) {	std::vector< std::string > args( argv , argv + argc );	EventSample::Main( args );}


EDIT: Cleaner version that dosn't need to use _1 and _2 placeholders, and generally a little nicer looking. See OLD: and NEW: comments.

[Edited by - MaulingMonkey on July 11, 2005 12:04:49 PM]
Use this as your basis. An event is really just a MulticastDelegate in .NET land, which holds the list of receivers. So your MulticastDelegate implementation would hold a list of receivers, and when you called operator(), you'd just loop through your receiver list and call operator() on each of them.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
Quote:Original post by MaulingMonkey
Quote:Original post by paulecoyote
EDIT: Had a quick look through those boost functions... interesting still not quite what I had in mind - though very close. clicky was pretty interesting. The use of terminalogy is also interesting, "slots" and "signals"... terms that make me think of threads for some reason.


Your little clicky is also, as far as I know, the boost equivilant of events/delegates.

*time passes*

I got sidetracked in boredom to converting this example for events/delegates into C++ using the boost libraries. I tried to make it nearly analagous, so you can open both up side by side and see how everything corresponds.

It's not an exact match (I'm not using managed C++, this compiles and runs successfully on MinGW's GCC 3.4.2 on WinXP), but it should get the idea across.

*** Source Snippet Removed ***

EDIT: Cleaner version that dosn't need to use _1 and _2 placeholders, and generally a little nicer looking. See OLD: and NEW: comments.


Very useful stuff... you put loads of effort in to that answer!
Rated u up as high as it would let me [smile]

Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Quote:Original post by paulecoyote
Very useful stuff... you put loads of effort in to that answer!
Rated u up as high as it would let me [smile]


Heck, I was just bored, it really didn't take me long :-). Glad it helps, though.

-Mike
Boost::Signals is good, but it's a monster. I prefer sigslot over boost::signals + boost::bind. The syntax is a little nicer and compile times are MUCH faster. It abandons the concept of signal return values, which is okay for me because I don't like the boost concept of combiners. The great thing about signal/slot mechanisms is that they manage themselves. If you destroy an object with a connected slot, the signal it was connected to automagically removes the slot from it's list of subscribers.

Oh, and sigslot is thread safe.

Writing this all from memory... It might not compile.
#include "sigslot.h"#include <iostream>struct AlarmClock{    void Buzz()     {         std::cout << "BUZZZ!!!11one" << std::endl;         SigBuzz();     }    sigslot::signal0<> SigBuzz;};struct Sleeper : public sigslot::has_slots<>{    void OnBuzz()     {         std::cout << "Woke up" << std::endl;    }};int main(int argc, char** argv){    AlarmClock clock;    Sleeper sleeper1;    Sleeper sleeper2;    clock.SigBuzz.connect(&sleeper1, &Sleeper::OnBuzz);    clock.SigBuzz.connect(&sleeper2, &Sleeper::OnBuzz);    clock.Buzz();}


Outputs:
BUZZZ!!!11one
Woke up
Woke up

This topic is closed to new replies.

Advertisement