Skip to content

Commit 9694691

Browse files
committed
- Added ObservableWithBuckets implementation
1 parent b0cf7c6 commit 9694691

File tree

3 files changed

+95
-2
lines changed

3 files changed

+95
-2
lines changed

src/ESPressio_Observable.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace ESPressio {
2020
std::vector<IObserverHandle*> _observers;
2121
protected:
2222
/// Will call the `callback` for each Observer
23-
virtual void WithObservers(std::function<void(IObserver*)> callback) {
23+
void WithObservers(std::function<void(IObserver*)> callback) {
2424
for (auto observer : _observers) {
2525
callback(observer->GetObserver());
2626
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <typeindex>
5+
#include <unordered_map>
6+
#include <vector>
7+
8+
#include "ESPressio_IObservable.hpp"
9+
#include "ESPressio_IObserver.hpp"
10+
#include "ESPressio_ObserverHandle.hpp"
11+
12+
namespace ESPressio {
13+
14+
namespace Observable {
15+
16+
/// An `ObservableWithBuckets` is an object that can be observed by any number of `IObserver` descendant types
17+
/// This is a concrete implementation of `IObservable`, but it is NOT Thread-Safe.
18+
/// This variation uses "Buckets" (a Map, effectively) keyed on the `IObserver` type.
19+
/// It may be more performant when your descendant Observable is observed by a large number of DIFFERENT Observer types!
20+
/// Registering or Unregistering Observers while Observers are being notified can lead to undefined behavior.
21+
/// If you need a Thread-Safe Implementation, use the `ThreadSafeObservableWithBuckets` class instead.
22+
class ObservableWithBuckets : public IObservable {
23+
private:
24+
std::unordered_map<std::type_index, std::vector<IObserverHandle*>*> _observers;
25+
protected:
26+
/// Will call the `callback` for each Observer that is of type `ObserverType`
27+
template <class ObserverType>
28+
void WithObservers(std::function<void(ObserverType*)> callback) {
29+
auto observerType = std::type_index(typeid(ObserverType)); // Get the Type Index of the ObserverType
30+
std::vector<IObserverHandle*>* observers = _observers[observerType]; // Get the Observers for this Type Index
31+
if (observers == nullptr || observers->empty()) { return; } // If there are no Observers, return
32+
33+
for (auto observer : *observers) { // For each Observer...
34+
callback(observer); // ...call the callback (we know that it is of type `ObserverType`)
35+
}
36+
}
37+
public:
38+
~ObservableWithBuckets() {
39+
for (auto& observer : _observers) { // Iterate all of the Type Buckets...
40+
for (auto& observerHandle : *observer.second) { // ...and for each Observer in the Bucket...
41+
static_cast<ObserverHandle*>(observerHandle)->__invalidate(); // ...invalidate it
42+
}
43+
delete observer.second; // ...and delete the Bucket
44+
}
45+
_observers.clear(); // Clear the Map
46+
}
47+
48+
virtual IObserverHandle* RegisterObserver(IObserver* observer) {
49+
auto observerType = std::type_index(typeid(*observer)); // Get the Type Index of the Observer
50+
std::vector<IObserverHandle*>* observers = _observers[observerType]; // Get the Observers for this Type Index
51+
if (observers == nullptr) { // If there are no Observers for this Type Index...
52+
observers = new std::vector<IObserverHandle*>(); // ...create a new vector
53+
_observers[observerType] = observers; // ...and add it to the Map
54+
}
55+
56+
for (auto thisObserver : *observers) { // For each Observer in the vector...
57+
if (thisObserver->GetObserver() == observer) { return thisObserver; } // ...if it is the same as the one we are trying to register, return it
58+
}
59+
60+
IObserverHandle* handle = new ObserverHandle(this, observer); // Create a new ObserverHandle
61+
observers->push_back(handle); // Add it to the vector
62+
return handle; // Return the ObserverHandle
63+
}
64+
65+
virtual void UnregisterObserver(IObserver* observer) {
66+
auto observerType = std::type_index(typeid(*observer)); // Get the Type Index of the Observer
67+
std::vector<IObserverHandle*>* observers = _observers[observerType]; // Get the Observers for this Type Index
68+
if (observers == nullptr || observers->empty()) { return; } // If there are no Observers, return
69+
70+
for (auto thisObserver = observers->begin(); thisObserver != observers->end(); thisObserver++) { // For each Observer in the vector...
71+
if ((*thisObserver)->GetObserver() == observer) { // ...if it is the same as the one we are trying to unregister...
72+
static_cast<ObserverHandle*>((*thisObserver))->__invalidate(); // ...invalidate it
73+
observers->erase(thisObserver); // ...and erase it from the vector
74+
return; // ...and return
75+
}
76+
}
77+
}
78+
79+
virtual bool IsObserverRegistered(IObserver* observer) {
80+
auto observerType = std::type_index(typeid(*observer)); // Get the Type Index of the Observer
81+
std::vector<IObserverHandle*>* observers = _observers[observerType]; // Get the Observers for this Type Index
82+
if (observers == nullptr || observers->empty()) { return false; } // If there are no Observers, return false
83+
84+
for (auto thisObserver : *observers) { // For each Observer in the vector...
85+
if (thisObserver->GetObserver() == observer) { return true; } // ...if it is the same as the one we are checking, return true
86+
}
87+
return false; // If we didn't find it, return false
88+
}
89+
};
90+
91+
}
92+
93+
}

src/ESPressio_ThreadSafeObservable.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace ESPressio {
2828

2929
protected:
3030
/// Will call the `callback` for each Observer
31-
virtual void WithObservers(std::function<void(IObserver*)> callback) {
31+
void WithObservers(std::function<void(IObserver*)> callback) {
3232
std::vector<IObserverHandle*>* observers = CopyObservers();
3333
for (auto observer : *observers) {
3434
callback(observer->GetObserver());

0 commit comments

Comments
 (0)