Wayverb
event.h
1 #pragma once
2 
3 #include <functional>
4 #include <mutex>
5 #include <unordered_map>
6 
7 namespace util {
8 
9 template <typename... Ts>
10 class event final {
11  class impl;
12  using key_type = size_t;
13 
14 public:
15  event()
16  : pimpl_{std::make_shared<impl>()} {}
17 
18  event(const event&) = delete;
19  event(event&&) noexcept = default;
20 
21  event& operator=(const event&) = delete;
22  event& operator=(event&&) noexcept = default;
23 
24  ~event() noexcept = default;
25 
26  using callback_type = std::function<void(Ts...)>;
27 
28  class connection final {
29  public:
30  connection() = default;
31  connection(const std::shared_ptr<impl>& pimpl, callback_type callback)
32  : pimpl_{pimpl}
33  , key_{pimpl->connect(std::move(callback))} {}
34 
35  void swap(connection& other) noexcept {
36  using std::swap;
37  swap(pimpl_, other.pimpl_);
38  swap(key_, other.key_);
39  }
40 
41  connection(const connection&) = delete;
42  connection(connection&& other) noexcept { swap(other); }
43 
44  connection& operator=(const connection&) = delete;
45  connection& operator=(connection&& other) noexcept {
46  swap(other);
47  return *this;
48  }
49 
50  void disconnect() {
51  if (auto p = pimpl_.lock()) {
52  p->disconnect(key_);
53  pimpl_.reset();
54  }
55  }
56 
57  void block() {
58  if (auto p = pimpl_.lock()) {
59  p->block(key_);
60  }
61  }
62 
63  void unblock() {
64  if (auto p = pimpl_.lock()) {
65  p->block(key_);
66  }
67  }
68 
69  explicit operator bool() const { return !pimpl_.expired(); }
70 
71  private:
72  std::weak_ptr<impl> pimpl_;
73  key_type key_;
74  };
75 
76  class scoped_connection final {
77  public:
78  scoped_connection() = default;
80  : connection{std::move(connection)} {}
81 
82  scoped_connection(const scoped_connection&) = delete;
83  scoped_connection(scoped_connection&& other) noexcept = default;
84 
85  scoped_connection& operator=(const scoped_connection&) = delete;
86  scoped_connection& operator=(scoped_connection&& other) noexcept =
87  default;
88 
89  ~scoped_connection() noexcept { connection.disconnect(); }
90 
91  connection connection;
92  };
93 
94  auto connect(callback_type callback) {
95  return connection{pimpl_, std::move(callback)};
96  }
97 
98  template <typename... Us>
99  void operator()(Us&&... us) const {
100  pimpl_->operator()(std::forward<Us>(us)...);
101  }
102 
103  bool empty() const { return pimpl_->empty(); }
104  size_t size() const { return pimpl_->size(); }
105 
106 private:
107  class impl final {
108  struct callback_info final {
109  callback_type callback;
110  bool blocked{false};
111  };
112 
113  public:
114  impl() = default;
115 
116  impl(const impl&) = delete;
117  impl(impl&&) noexcept = delete;
118 
119  impl& operator=(const impl&) = delete;
120  impl& operator=(impl&&) noexcept = delete;
121 
122  auto connect(callback_type callback) {
123  std::lock_guard<std::mutex> lck{mutex_};
124  slots_.insert(std::make_pair(++current_key_,
125  callback_info{std::move(callback)}));
126  return current_key_;
127  }
128 
129  void disconnect(key_type key) {
130  std::lock_guard<std::mutex> lck{mutex_};
131  slots_.erase(key);
132  }
133 
134  void block(key_type key) {
135  std::lock_guard<std::mutex> lck{mutex_};
136  auto slot = slots_.find(key);
137  if (slot != end(slots_)) {
138  slot->second.blocked = true;
139  }
140  }
141 
142  void unblock(key_type key) {
143  std::lock_guard<std::mutex> lck{mutex_};
144  auto slot = slots_.find(key);
145  if (slot != end(slots_)) {
146  slot->second.blocked = false;
147  }
148  }
149 
150  bool blocked(key_type key) const {
151  std::lock_guard<std::mutex> lck{mutex_};
152  auto slot = slots_.find(key);
153  if (slot != end(slots_)) {
154  return slot->second.blocked;
155  }
156  throw std::logic_error{"No such key."};
157  }
158 
159  template <typename... Us>
160  void operator()(Us&&... us) const {
161  std::unordered_map<key_type, callback_info> copy;
162 
163  {
164  std::lock_guard<std::mutex> lck{mutex_};
165  copy = slots_;
166  }
167 
168  for (const auto& slot : copy) {
169  if (!slot.second.blocked) {
170  slot.second.callback(std::forward<Us>(us)...);
171  }
172  }
173  }
174 
175  bool empty() const {
176  std::lock_guard<std::mutex> lck{mutex_};
177  return slots_.empty();
178  }
179 
180  size_t size() const {
181  std::lock_guard<std::mutex> lck{mutex_};
182  return slots_.size();
183  }
184 
185  private:
186  mutable std::mutex mutex_;
187 
188  key_type current_key_{0};
189  std::unordered_map<key_type, callback_info> slots_;
190  };
191 
192  std::shared_ptr<impl> pimpl_;
193 };
194 
195 } // namespace util
Definition: allocator.h:6
Definition: event.h:76
Definition: event.h:10
Definition: event.h:28