Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Own Observer Pattern

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:

  1. How do I define the type of action in struct Subscription?
  2. Am I moving the right way?

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 );
        };
    };
};
like image 264
Kolyunya Avatar asked Aug 24 '12 07:08

Kolyunya


3 Answers

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);
   }
};
like image 56
JohnB Avatar answered Oct 25 '22 04:10

JohnB


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).

like image 43
Mark Garcia Avatar answered Oct 25 '22 03:10

Mark Garcia


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;
like image 38
AndersK Avatar answered Oct 25 '22 05:10

AndersK