SpectMorph
smsignal.hh
1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.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  typedef std::function<void (Args...)> CbFunction;
152 
153  struct Connection
154  {
155  CbFunction func;
156  uint64 id;
157  SignalReceiver *receiver;
158  };
159  struct Data
160  {
161  int ref_count = 1;
162 
163  Data *
164  ref()
165  {
166  assert (ref_count > 0);
167  ref_count++;
168 
169  return this;
170  }
171  void
172  unref (bool cleanup)
173  {
174  assert (ref_count > 0);
175  ref_count--;
176 
177  if (cleanup && ref_count == 1) /* ensure nobody is iterating over the data */
178  {
179  connections.remove_if ([](Connection& conn) -> bool
180  {
181  return conn.id == 0;
182  });
183  }
184  else if (ref_count == 0)
185  delete this;
186  }
187 
188  std::list<Connection> connections;
189  };
190  Data *signal_data;
191 public:
192  uint64
193  connect_impl (SignalReceiver *receiver, const CbFunction& callback)
194  {
195  assert (signal_data);
196 
197  Data *data = signal_data->ref();
198  uint64 id = next_signal_id();
199  data->connections.push_back ({callback, id, receiver});
200  data->unref (true);
201 
202  return id;
203  }
204  void
205  disconnect_impl (uint64 id) override
206  {
207  assert (signal_data);
208 
209  Data *data = signal_data->ref();
210  for (auto& conn : data->connections)
211  {
212  if (conn.id == id)
213  conn.id = 0;
214  }
215  data->unref (true);
216  }
217  void
218  operator()(Args... args)
219  {
220  assert (signal_data);
221 
222  Data *data = signal_data->ref();
223 
224  for (auto& conn : data->connections)
225  {
226  if (conn.id)
227  conn.func (args...);
228  }
229 
230  data->unref (true);
231  }
232  Signal() :
233  signal_data (new Data())
234  {
235  }
236  ~Signal()
237  {
238  assert (signal_data);
239 
240  for (auto& conn : signal_data->connections)
241  {
242  if (conn.id)
243  {
244  conn.receiver->dead_signal (conn.id);
245  conn.id = 0;
246  }
247  }
248 
249  signal_data->unref (false);
250  signal_data = nullptr;
251  }
252 };
253 
254 }
255 
256 #endif
Definition: smsignal.hh:16
Definition: smadsrenvelope.hh:8
Definition: smsignal.hh:18
Definition: smsignal.hh:34