Jump to content
  • Advertisement
Sign in to follow this  
paulecoyote

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

This topic is 4876 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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
Advertisement
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
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

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
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!