Simulant  21.12-194
A portable game engine for Windows, OSX, Linux, Dreamcast, and PSP
signal.h
1 #pragma once
2 
3 #include <functional>
4 #include <memory>
5 #include <algorithm>
6 
7 #include "../logging.h"
8 #include "../threads/atomic.h"
9 
10 #define DEFINE_SIGNAL(prototype, name) \
11  public: \
12  prototype& name() { return name##_; } \
13  prototype& name() const { return name##_; } \
14  private: \
15  mutable prototype name##_
16 
17 namespace smlt {
18 namespace sig {
19 
20 class ConnectionImpl;
21 
22 class Disconnector {
23 public:
24  virtual ~Disconnector() {}
25 
26  virtual bool disconnect(const ConnectionImpl& conn) = 0;
27  virtual bool connection_exists(const ConnectionImpl& conn) const = 0;
28 };
29 
30 class Connection;
31 
33 public:
34  ConnectionImpl(Disconnector* parent, size_t id, std::weak_ptr<int> marker):
35  id_(id),
36  parent_(parent),
37  marker_(marker) {}
38 
39  ConnectionImpl(const ConnectionImpl& rhs):
40  id_(rhs.id_),
41  parent_(rhs.parent_),
42  marker_(rhs.marker_) {}
43 
44  bool operator!=(const ConnectionImpl& rhs) const { return !(*this == rhs); }
45  bool operator==(const ConnectionImpl& rhs) const { return this->id_ == rhs.id_; }
46 
47  ConnectionImpl& operator=(const ConnectionImpl& rhs) {
48  if(*this == rhs) {
49  return *this;
50  }
51 
52  this->id_ = rhs.id_;
53  this->parent_ = rhs.parent_;
54 
55  return *this;
56  }
57 
58 private:
59  size_t id_;
60  Disconnector* parent_;
61  std::weak_ptr<int> marker_;
62  friend class Connection;
63 };
64 
65 class Connection {
66 public:
67  Connection():
68  impl_({}) {
69 
70  }
71 
72  Connection(std::shared_ptr<ConnectionImpl> impl):
73  impl_(impl) {}
74 
75  bool disconnect() {
76  auto p = impl_.lock();
77  return p && p->marker_.lock() && p->parent_->disconnect(*p);
78  }
79 
80  bool is_connected() const {
81  auto p = impl_.lock();
82  return p && p->marker_.lock() && p->parent_->connection_exists(*p);
83  }
84 
85  operator bool() const {
86  return is_connected();
87  }
88 
89 private:
90  std::weak_ptr<ConnectionImpl> impl_;
91 };
92 
94 public:
96  counter_(new int(1)),
97  conn_(conn) {
98 
99  }
100 
102  counter_(rhs.counter_),
103  conn_(Connection()) {
104 
105  if(counter_) {
106  ++*counter_;
107  }
108  }
109 
110  ScopedConnection& operator=(const ScopedConnection& rhs) {
111  if(&rhs == this) {
112  return *this;
113  }
114 
115  clear();
116  conn_ = rhs.conn_;
117  counter_ = rhs.counter_;
118 
119  if(counter_) {
120  ++*counter_;
121  }
122 
123  return *this;
124  }
125 
126  ~ScopedConnection() {
127  clear();
128  }
129 
130  bool is_connected() const {
131  return conn_.is_connected();
132  }
133 
134  void disconnect() {
135  conn_.disconnect();
136  }
137 private:
138  int* counter_ = nullptr;
139  Connection conn_;
140 
141  void clear() {
142  if(counter_) {
143  if(*counter_ == 1) {
144  if(conn_.is_connected()) {
145  conn_.disconnect();
146  }
147  }
148 
149  if(!--*counter_) {
150  delete counter_;
151  counter_ = nullptr;
152  }
153  }
154  }
155 };
156 
157 template<typename> class ProtoSignal;
158 
159 template<typename R, typename... Args>
160 class ProtoSignal<R (Args...)> : public Disconnector {
161 public:
162  typedef R result;
163  typedef std::function<R (Args...)> callback;
164 
165  ~ProtoSignal() {
166  Link* it = head_;
167  while(it) {
168  auto next = it->next;
169  delete it;
170  it = next;
171  }
172  }
173 
174  Connection connect(const callback& func) {
175  static size_t id_counter = 0;
176  size_t id = ++id_counter;
177  Link new_link;
178  new_link.func = func;
179  new_link.conn_impl = std::make_shared<ConnectionImpl>(this, id, marker_);
180 
181  if(push_link(new_link)) {
182  return Connection(new_link.conn_impl);
183  } else {
184  S_WARN("Error adding connection to signal!");
185  return Connection();
186  }
187  }
188 
189  void operator()(Args... args) {
190  Link* it = head_;
191 
192  ++iterating_;
193  while(it) {
194  if(!it->is_dead) {
195  assert(it->func);
196  it->func(args...);
197  }
198 
199  it = it->next;
200  }
201  --iterating_;
202 
203  shrink_to_fit();
204  }
205 
206  bool connection_exists(const ConnectionImpl& conn) const {
207  Link* it = head_;
208  ++iterating_;
209  while(it) {
210  if((!it->is_dead) && it->conn_impl.get() == &conn) {
211  return true;
212  }
213 
214  it = it->next;
215  }
216  --iterating_;
217  return false;
218  }
219 
220  void shrink_to_fit() {
221  if(iterating_ > 0) {
222  return;
223  }
224 
225  Link* it = head_;
226  Link* prev = nullptr;
227  while(it) {
228  auto next = it->next;
229  if(it->is_dead) {
230  if(prev) {
231  prev->next = next;
232  } else {
233  assert(!prev);
234  head_ = next;
235  }
236 
237  if(it == tail_) {
238  tail_ = prev;
239  }
240 
241  delete it;
242  } else {
243  prev = it;
244  }
245 
246  it = next;
247  }
248  }
249 
250  bool disconnect(const ConnectionImpl& conn_impl) {
251  bool removed = false;
252 
253  Link* it = head_;
254 
255  ++iterating_;
256  while(it) {
257  auto next = it->next;
258  if((!it->is_dead) && it->conn_impl.get() == &conn_impl) {
259  it->is_dead = true;
260 
261  connection_count_--;
262  removed = true;
263  }
264 
265  it = next;
266  }
267  --iterating_;
268 
269  shrink_to_fit();
270 
271  return removed;
272  }
273 
274  std::size_t connection_count() const {
275  return connection_count_;
276  }
277 
278 private:
279  struct Link {
280  callback func;
281  std::shared_ptr<ConnectionImpl> conn_impl;
282  Link* next = nullptr;
283  bool is_dead = false;
284  };
285 
286  Link* head_ = nullptr;
287  Link* tail_ = nullptr;
288 
289  /* Keeps track of whether or not we're currently iterating
290  * the links. This protects us deleting a thing during an iteration */
291  mutable uint8_t iterating_ = 0;
292 
293  uint32_t connection_count_ = 0;
294 
295  /* This marker is passed as a weak_ptr to all connections. It's
296  * used to track whether this signal has been destroyed. If so
297  * then any connections pointing to it will fail to get a lock */
298  std::shared_ptr<int> marker_ = std::make_shared<int>(1);
299 
300  bool push_link(const Link& link) {
301  if(!head_) {
302  assert(!tail_);
303  head_ = tail_ = new Link();
304  *head_ = link;
305  } else {
306  assert(tail_);
307  tail_->next = new Link();
308  tail_ = tail_->next;
309  *tail_ = link;
310  }
311 
312  connection_count_++;
313 
314  return true;
315  }
316 };
317 
318 template<typename Signature>
319 class signal : public ProtoSignal<Signature> {
320 
321 };
322 
323 typedef Connection connection;
325 
326 }
327 }
smlt::sig::Connection
Definition: signal.h:65
smlt
Definition: animation.cpp:25
smlt::sig::Disconnector
Definition: signal.h:22
smlt::sig::ConnectionImpl
Definition: signal.h:32
smlt::sig::ScopedConnection
Definition: signal.h:93
smlt::sig::ProtoSignal
Definition: signal.h:157
smlt::sig::signal
Definition: signal.h:319