C ++ native observer pattern

I create an observer pattern that should work this way: observer calls the AddEventListener EventDispatcher method and passes a string that is named event , PointerToItself and PointerToItsMemberMethod

After that, the event occurs inside the EventDispatcher ; he looks at the list of subscriptions, and if there are some assigned to this event, he calls the action observer method.

I came to this EventDispatcher.h . CAUTION contains a pseudo-code bit.

These are two questions:

  • How to determine type of action in struct Subscription ?
  • Am I moving right?

PS : No, I will not 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 is implemented as follows: 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 ); }; }; }; 
+6
source share
3 answers

Perhaps you just need to create a class that should be output by "users":

 class Action { public: friend class EventDispatcher; virtual SomeResultType DoThis() = 0; private: /* Some common data */ }; 

Just pass the input variable derived from the class to AddEventListener. When the corresponding event is fired, simply fill in the general 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 ); }; 

Example derived class Action:

 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 ways to implement actions (and callbacks).

+1
source

You can either define an Action class or pass a lambda function (C ++ 11). In the latter case, the action can be defined as

 function<void (EventDispatcher*)> action; 

and you will register an 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 observers in EventDispatcher, so you don't have to take care that they are not logged.

Edit: the following example has been added (only one subscription is possible, but should illustrate the idea - you must be careful not to reference an object that no longer exists)

 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); } }; 
+3
source

In its simplest form, u_u can be a function pointer, for example.

 typedef void (*u_u)(void*); // or whatever arguments u like 

then you just provide a function that is called whenever an event is fired.

 void myaction(void* arg) { ... } Subscription s; ... s.action = myaction; 
+1
source

Source: https://habr.com/ru/post/923633/


All Articles