I'm designing an observer pattern which should work this way: observer calls AddEventListener
method of EventDispatcher
and passes a string which is the name of the event
, PointerToItself and a PointerToItsMemberMethod
After that event
happens inside of the EventDispatcher
; it looks through the list of subscriptions and if there are some, assigned to this event calls the action
method of the observer
.
I've come to this EventDispatcher.h
. CAUTION contains bit of pseudo-code.
The are two questions:
action
in struct Subscription
?PS: No, I'm not gonna use boost
or any other libraries .
#pragma once
#include <vector>
#include <string>
using namespace std;
struct Subscription
{
void* observer;
string event;
/* u_u */ action;
};
class EventDispatcher
{
private:
vector<Subscription> subscriptions;
protected:
void DispatchEvent ( string event );
public:
void AddEventListener ( Observer* observer , string event , /* u_u */ action );
void RemoveEventListener ( Observer* observer , string event , /* u_u */ action );
};
This header implements like this in EventDispatcher.cpp
#include "EventDispatcher.h"
void EventDispatcher::DispatchEvent ( string event )
{
int key = 0;
while ( key < this->subscriptions.size() )
{
Subscription subscription = this->subscriptions[key];
if ( subscription.event == event )
{
subscription.observer->subscription.action;
};
};
};
void EventDispatcher::AddEventListener ( Observer* observer , string event , /* */ action )
{
Subscription subscription = { observer , event , action );
this->subscriptions.push_back ( subscription );
};
void EventDispatcher::RemoveEventListener ( Observer* observer , string event , /* */ action )
{
int key = 0;
while ( key < this->subscriptions.size() )
{
Subscription subscription = this->subscriptions[key];
if ( subscription.observer == observer && subscription.event == event && subscription.action == action )
{
this->subscriptions.erase ( this->subscriptions.begin() + key );
};
};
};
You could either define an Action class or pass a lambda function (C++11). In the latter case, action could be defined as
function<void (EventDispatcher*)> action;
and you would register the observer as follows
Observer * me = this;
observable->AddEventListener (this, "EventName", [me] (EventDispatcher* dispatcher) {
// code here; me is available
});
You should probably use smart weak pointers to store the Observers in the EventDispatcher, such that you do not have to care for un-registering.
Edit: Added following example (just one subscription possible, but should illustrate the idea -- you have to be careful that you do not reference an object that does no longer exist)
struct Observable {
std::weak_ptr<function<void (const Observable&)>> action;
void AddEventListener (std::weak_ptr<function<void (const Observable&)>> theAction) {
action = theAction;
}
void EventRaised () {
if (!action.expired ()) {
auto theAction = action.lock ();
(*theAction) (*this);
}
}
};
struct Observer {
...
void CallOnEvent (const Observable & observable) {
// do something
}
// field to store the action as long as it is needed
std::shared_ptr<function<void (const Observable&)>> action;
void ... {
auto me = this;
action = std::make_shared<function<void (const Observable&)>> (
[me] (const Observable& observable) {
me->CallOnEvent (observable);
}
);
// we could have as well used std::bind
observable.AddEventListener (action);
}
};
Perhaps you should just create a class to be derived by "users":
class Action {
public:
friend class EventDispatcher;
virtual SomeResultType DoThis() = 0;
private:
/* Some common data */
};
Just pass some derived-from-class-Action typed variable to AddEventListener. When the corresponding event is triggered, just fill in the common data and call the DoThis() method.
void EventDispatcher::DispatchEvent ( string event )
{
int key = 0;
while ( key < this->subscriptions.size() )
{
Subscription subscription = this->subscriptions[key];
if ( subscription.event == event )
{
subscription->action();
};
};
};
For AddEventListener:
void EventDispatcher::AddEventListener ( Observer* observer , string event , Action* action )
{
Subscription subscription = { observer , event , action );
this->subscriptions.push_back ( subscription );
};
An example of a Action derived class:
class myAction: public Action {
public:
// Implement the DoThis() method
void SomeResultType DoThis() {
cout << "Hello World!";
return SomeValue;
}
};
// To use the action,
myAction* act = new myAction;
myEventDispatcher.AddEventListener(someObserver, "HelloWorld", act);
This is one of the safest way to implement actions (and callbacks).
In its simplest form u_u could be a function pointer e.g.
typedef void (*u_u)(void*); // or whatever arguments u like
then you just supply a function that is called whenever the event is triggered.
void myaction(void* arg)
{
...
}
Subscription s;
...
s.action = myaction;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With