Sign in to follow this  
paulecoyote

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

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
Just a few details I'd like to point out...

Quote:
Original post by smr
Boost::Signals is good, but it's a monster. I prefer sigslot over boost::signals + boost::bind.


The modified example I have shown does not use boost::bind except in the commented out version. It's not a required item.

Quote:
The syntax is a little nicer


Your opinion :).

Quote:
The great thing about signal/slot mechanisms is that they manage themselves.


You can optionally do this with boost::signals::trackable (using boost::bind):

#include <boost/bind.hpp>
#include <boost/signal.hpp>
#include <iostream>

struct AlarmClock
{
void Buzz()
{
std::cout << "BUZZZ!!!11one" << std::endl;
SigBuzz();
}
boost::signal< void () > SigBuzz;
};

struct Sleeper : public boost::signals::trackable
{
void OnBuzz() {
std::cout << "Woke up" << std::endl;
}
};

int main () {
AlarmClock clock;
Sleeper sleeper1;
Sleeper sleeper2;
clock.SigBuzz.connect( boost::bind( &Sleeper::OnBuzz, &sleeper1 ) );
clock.SigBuzz.connect( boost::bind( &Sleeper::OnBuzz, &sleeper2 ) );

{
Sleeper sleeper3;
clock.SigBuzz.connect( boost::bind( &Sleeper::OnBuzz , &sleeper3 ) );
}

clock.Buzz();
}




It's not forced upon you, but it's there if you want it.

Quote:
Oh, and sigslot is thread safe.


Now here's an acutal difference :-). Boost.Signals is not yet thread safe (although it's on the TODO list). Also, my example crashed on windows, something iffy with Sleeper's dtor. Not sure if this is a compiler bug or something fixed in CVS or what, but it works fine in 1.32 on my linux box.

You've said only truths in your post (and opinions, as far as I know), I'm just trying to prevent someone from drawing some incorrect conclusions.

Share this post


Link to post
Share on other sites
Quote:
Original post by smr
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.
*** Source Snippet Removed ***

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

Thats a nice looking library too... will investigate further. Seems on the outset to be slightly more lightweight, and it has this nicely formatted documentation too http://sigslot.sourceforge.net/sigslot.pdf

rating+++++


Signed, Paul

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this