SpectMorph
smsignal.hh
1 // Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl-2.1.html
2 
3 #ifndef SPECTMORPH_SIGNAL_HH
4 #define SPECTMORPH_SIGNAL_HH
5 
6 #include "smutils.hh"
7 #include <assert.h>
8 #include <functional>
9 #include <vector>
10 #include <list>
11 
12 namespace SpectMorph
13 {
14 
15 template<class... Args>
16 class Signal;
17 
18 struct SignalBase
19 {
20  static uint64
21  next_signal_id()
22  {
23  static uint64 next_id = 1;
24 
25  return next_id++;
26  }
27  virtual void disconnect_impl (uint64 id) = 0;
28  virtual
29  ~SignalBase()
30  {
31  }
32 };
33 
35 {
36  struct SignalSource
37  {
38  SignalBase *signal;
39  uint64 id;
40  };
41  struct SignalReceiverData
42  {
43  int ref_count = 1;
44 
45  SignalReceiverData *
46  ref()
47  {
48  assert (ref_count > 0);
49  ref_count++;
50  return this;
51  }
52  void
53  unref (bool cleanup)
54  {
55  assert (ref_count > 0);
56  ref_count--;
57 
58  if (cleanup && ref_count == 1) /* ensure nobody is iterating over the data */
59  {
60  sources.remove_if ([](SignalSource& signal_source) -> bool
61  {
62  return signal_source.id == 0;
63  });
64  }
65  else if (ref_count == 0)
66  delete this;
67  }
68  std::list<SignalSource> sources;
69  };
70  struct SignalReceiverData *signal_receiver_data;
71 
72 public:
73  template<class... Args, class CbFunction>
74  uint64
75  connect (Signal<Args...>& signal, const CbFunction& callback)
76  {
77  assert (signal_receiver_data);
78 
79  SignalReceiverData *data = signal_receiver_data->ref();
80 
81  auto id = signal.connect_impl (this, callback);
82  data->sources.push_back ({ &signal, id });
83  data->unref (true);
84 
85  return id;
86  }
87  template<class... Args, class Instance, class Method>
88  uint64
89  connect (Signal<Args...>& signal, Instance *instance, const Method& method)
90  {
91  return SignalReceiver::connect (signal, [instance, method](Args&&... args)
92  {
93  (instance->*method) (std::forward<Args>(args)...);
94  });
95  }
96  void
97  disconnect (uint64 id)
98  {
99  assert (signal_receiver_data);
100 
101  SignalReceiverData *data = signal_receiver_data->ref();
102 
103  for (auto& signal_source : data->sources)
104  {
105  if (signal_source.id == id)
106  {
107  signal_source.signal->disconnect_impl (id);
108  signal_source.id = 0;
109  }
110  }
111  data->unref (true);
112  }
113  SignalReceiver() :
114  signal_receiver_data (new SignalReceiverData())
115  {
116  }
117  virtual
118  ~SignalReceiver()
119  {
120  assert (signal_receiver_data);
121 
122  for (auto& signal_source : signal_receiver_data->sources)
123  {
124  if (signal_source.id)
125  {
126  signal_source.signal->disconnect_impl (signal_source.id);
127  signal_source.id = 0;
128  }
129  }
130  signal_receiver_data->unref (false);
131  signal_receiver_data = nullptr;
132  }
133  void
134  dead_signal (uint64 id)
135  {
136  SignalReceiverData *data = signal_receiver_data->ref();
137 
138  for (auto& signal_source : data->sources)
139  {
140  if (signal_source.id == id)
141  signal_source.id = 0;
142  }
143 
144  data->unref (true);
145  }
146 };
147 
148 template<class... Args>
149 class Signal : public SignalBase
150 {
151  SPECTMORPH_CLASS_NON_COPYABLE(Signal);
152 
153  typedef std::function<void (Args...)> CbFunction;
154 
155  struct Connection
156  {
157  CbFunction func;
158  uint64 id;
159  SignalReceiver *receiver;
160  };
161  struct Data
162  {
163  int ref_count = 1;
164 
165  Data *
166  ref()
167  {
168  assert (ref_count > 0);
169  ref_count++;
170 
171  return this;
172  }
173  void
174  unref (bool cleanup)
175  {
176  assert (ref_count > 0);
177  ref_count--;
178 
179  if (cleanup && ref_count == 1) /* ensure nobody is iterating over the data */
180  {
181  connections.remove_if ([](Connection& conn) -> bool
182  {
183  return conn.id == 0;
184  });
185  }
186  else if (ref_count == 0)
187  delete this;
188  }
189 
190  std::list<Connection> connections;
191  };
192  Data *signal_data;
193 public:
194  uint64
195  connect_impl (SignalReceiver *receiver, const CbFunction& callback)
196  {
197  assert (signal_data);
198 
199  Data *data = signal_data->ref();
200  uint64 id = next_signal_id();
201  data->connections.push_back ({callback, id, receiver});
202  data->unref (true);
203 
204  return id;
205  }
206  void
207  disconnect_impl (uint64 id) override
208  {
209  assert (signal_data);
210 
211  Data *data = signal_data->ref();
212  for (auto& conn : data->connections)
213  {
214  if (conn.id == id)
215  conn.id = 0;
216  }
217  data->unref (true);
218  }
219  void
220  operator()(Args... args)
221  {
222  assert (signal_data);
223 
224  Data *data = signal_data->ref();
225 
226  for (auto& conn : data->connections)
227  {
228  if (conn.id)
229  conn.func (args...);
230  }
231 
232  data->unref (true);
233  }
234  Signal() :
235  signal_data (new Data())
236  {
237  }
238  ~Signal()
239  {
240  assert (signal_data);
241 
242  for (auto& conn : signal_data->connections)
243  {
244  if (conn.id)
245  {
246  conn.receiver->dead_signal (conn.id);
247  conn.id = 0;
248  }
249  }
250 
251  signal_data->unref (false);
252  signal_data = nullptr;
253  }
254 };
255 
256 }
257 
258 #endif
Definition: smsignal.hh:35
Definition: smsignal.hh:150
Definition: smsignal.hh:19