summaryrefslogtreecommitdiff
path: root/src/observer_list.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/observer_list.hh')
-rw-r--r--src/observer_list.hh148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/observer_list.hh b/src/observer_list.hh
new file mode 100644
index 0000000..86cdc03
--- /dev/null
+++ b/src/observer_list.hh
@@ -0,0 +1,148 @@
+#ifndef OBSERVER_LIST_HH
+#define OBSERVER_LIST_HH
+
+#include "common.hh"
+
+#include <algorithm>
+#include <vector>
+
+template<typename T>
+class ObserverList {
+public:
+ class iterator {
+ public:
+ iterator()
+ : list_(nullptr), index_(0), end_(0) {}
+
+ ~iterator() {
+ if (list_)
+ list_->release();
+ }
+
+ iterator(iterator const& it)
+ : list_(it.list_), index_(it.index_), end_(it.end_) {
+ if (list_) {
+ list_->aquire();
+ while (index_ < end_ && !list_->observers_[index_])
+ ++index_;
+ }
+ }
+
+ iterator& operator=(iterator const& it) {
+ if (list_ != it.list_) {
+ if (list_)
+ list_->release();
+ list_ = it.list_;
+ if (list_)
+ list_->aquire();
+ }
+ index_ = it.index_;
+ end_ = it.end_;
+ return *this;
+ }
+
+ explicit operator bool() {
+ return index_ < end_;
+ }
+
+ T& operator*() {
+ return *list_->observers_[index_];
+ }
+
+ T* operator->() {
+ return list_->observers_[index_];
+ }
+
+ iterator operator++(int) {
+ iterator ret(*this);
+ ++(*this);
+ return ret;
+ }
+
+ iterator& operator++() {
+ if (index_ < end_) {
+ do {
+ ++index_;
+ } while (index_ < end_ && !list_->observers_[index_]);
+ }
+ return *this;
+ }
+
+ private:
+ friend class ObserverList;
+
+ iterator(ObserverList* list, size_t index, size_t end)
+ : list_(list), index_(index), end_(end) {
+ list_->aquire();
+ while (index_ < end_ && !list_->observers_[index_])
+ ++index_;
+ }
+
+ ObserverList* list_;
+ size_t index_;
+ size_t end_;
+ };
+
+ ObserverList() = default;
+ ~ObserverList() {
+ assert(active_ == 0);
+ }
+
+ bool empty() const {
+ return observers_.empty();
+ }
+
+ void add(T* observer) {
+ assert(std::find(observers_.begin(), observers_.end(), observer)
+ == observers_.end());
+ observers_.push_back(observer);
+ }
+
+ void remove(T* observer) {
+ auto it = std::find(observers_.begin(), observers_.end(), observer);
+ if (it != observers_.end()) {
+ if (active_) {
+ *it = nullptr;
+ ++deleted_;
+ } else {
+ observers_.erase(it);
+ }
+ } else {
+ assert(false);
+ }
+ }
+
+ iterator notify() {
+ return iterator(this, 0, observers_.size());
+ }
+
+private:
+ void aquire() {
+ ++active_;
+ }
+
+ void release() {
+ assert(active_ > 0);
+ --active_;
+ if (active_ == 0 && deleted_)
+ cleanup();
+ }
+
+ void cleanup() {
+ size_t i = observers_.size();
+ while (deleted_ && i > 0) {
+ --i;
+ if (!observers_[i]) {
+ --deleted_;
+ observers_.erase(observers_.begin() + i);
+ }
+ }
+ assert(deleted_ == 0);
+ }
+
+ std::vector<T*> observers_;
+ unsigned active_{0};
+ unsigned deleted_{0};
+};
+
+#endif // OBSERVER_LIST_HH