NAP
signalslot.h
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 #pragma once
6 
7 // Std includes
8 #include <functional>
9 #include <set>
10 #include <vector>
11 #include <memory>
12 #include <iostream>
13 
14 namespace nap
15 {
16 
17  // Forward declarations
18  template<typename... Args> class Slot;
19 
24  template <typename... Args>
25  class Signal final
26  {
27  public:
28  using Function = std::function<void(Args... args)>;
29 
30  // Destruction
31  ~Signal();
32 
39  void connect(Slot<Args...>& slot);
40 
44  void disconnect(Slot<Args...>& slot);
45 
52  void connect(const Function& inFunction);
53 
57  void connect(Signal<Args...>& signal);
58 
62  void disconnect(Signal<Args...>& signal);
63 
67  template <typename U, typename F>
68  void connect(U* object, F memberFunction) { connect(std::bind(memberFunction, object, std::placeholders::_1)); }
69 
73  inline void operator()(Args... args) { trigger(std::forward<Args>(args)...); }
74 
79  void trigger(Args... args);
80 
81  private:
82  // Called by signal when connected
83  void addCause(Signal<Args...>& event);
84  void removeCause(Signal<Args...>& event);
85 
86  private:
94  struct Data
95  {
96  union
97  {
98  struct
99  {
100  Signal<Args...>* mSignal;
101  } USignal;
102 
103  struct
104  {
105  Slot<Args...>* mSlot;
106  } USlot;
107  };
108 
109  enum class EType : uint8_t
110  {
111  SignalCause,
112  SignalEffect,
113  SlotEffect
114  };
115  EType mType;
116  };
117 
118  // Members
119  std::vector<Data> mData; // Signal/slot effects, Signal causes
120  std::unique_ptr<std::vector<Function>> mFunctionEffects; // function objects
121  };
122 
123 
128  template <typename... Args>
129  class Slot final
130  {
131  public:
132  using Function = std::function<void(Args... args)>;
133 
134  public:
135  // Default constructor
136  Slot() = default;
137 
142  Slot(Function inFunction) :
143  mFunction(inFunction)
144  { }
145 
151  template <typename U, typename F>
152  Slot(U* parent, F memberFunction) :
153  mFunction(std::bind(memberFunction, parent, std::placeholders::_1))
154  { }
155 
162  template <typename U, typename F>
163  Slot(U* parent, F memberFunction, Signal<Args...>& signal) :
164  mFunction(std::bind(memberFunction, parent, std::placeholders::_1)) { signal.connect(*this); }
165 
166  // Disconnect slot from signals on destruction
167  ~Slot() { disconnect(); }
168 
172  void disconnect();
173 
178  void setFunction(Function func) { mFunction = func; }
179 
184  void trigger(Args... args);
185 
190  void copyCauses(const Slot& rhs);
191 
196  Slot& operator=(Slot&& other);
197 
203  Slot& operator=(const Slot& other);
204 
205  private:
206 
207  template<typename... Args_> friend class Signal;
208  typedef std::vector<Signal<Args...>*> SignalList;
209 
210  void addCause(Signal<Args...>& event);
211  void removeCause(Signal<Args...>& event);
212 
213  Function mFunction;
214  SignalList mCauses;
215  };
216 
217 
219  // Template Implementations
221 
222  template <typename... Args>
224  {
225  for (auto& data : mData)
226  {
227  if (data.mType == Data::EType::SignalCause)
228  data.USignal.mSignal->disconnect(*this);
229  else if (data.mType == Data::EType::SignalEffect)
230  data.USignal.mSignal->removeCause(*this);
231  else
232  data.USlot.mSlot->removeCause(*this);
233  }
234  }
235 
236  template <typename... Args>
238  {
239  Data data;
240  data.mType = Data::EType::SignalCause;
241  data.USignal.mSignal = &signal;
242  mData.push_back(data);
243  }
244 
245  template <typename... Args>
246  void Signal<Args...>::removeCause(Signal<Args...>& event)
247  {
248  for (int index = 0; index < mData.size(); ++index)
249  {
250  Data& value = mData[index];
251  if (value.mType == Data::EType::SignalCause && value.USignal.mSignal == &event)
252  {
253  mData.erase(mData.begin() + index);
254  break;
255  }
256  }
257  }
258 
259  template <typename... Args>
261  {
262  Data data;
263  data.mType = Data::EType::SignalEffect;
264  data.USignal.mSignal = &signal;
265  mData.push_back(data);
266  signal.addCause(*this);
267  }
268 
269  template <typename... Args>
271  {
272  for (int index = 0; index < mData.size(); ++index)
273  {
274  Data& value = mData[index];
275  if (value.mType == Data::EType::SignalEffect && value.USignal.mSignal == &signal)
276  {
277  value.USignal.mSignal->removeCause(*this);
278  mData.erase(mData.begin() + index);
279  break;
280  }
281  }
282  }
283 
284  template <typename... Args>
286  {
287  Data data;
288  data.mType = Data::EType::SlotEffect;
289  data.USlot.mSlot = &slot;
290  mData.push_back(data);
291  slot.addCause(*this);
292  }
293 
294  template <typename... Args>
296  {
297  for (int index = 0; index < mData.size(); ++index)
298  {
299  Data& data = mData[index];
300  if (data.mType == Data::EType::SlotEffect && data.USlot.mSlot == &slot)
301  {
302  data.USlot.mSlot->removeCause(*this);
303  mData.erase(mData.begin() + index);
304  break;
305  }
306  }
307  }
308 
309 
310  template <typename... Args>
311  void Signal<Args...>::connect(const Function& inFunction)
312  {
313  if (!mFunctionEffects)
314  mFunctionEffects = std::make_unique<std::vector<Function>>();
315 
316  mFunctionEffects->emplace_back(inFunction);
317  }
318 
319 
320  template <typename... Args>
321  void Signal<Args...>::trigger(Args... args)
322  {
323  for (auto& data : mData)
324  {
325  if (data.mType == Data::EType::SignalEffect)
326  data.USignal.mSignal->trigger(std::forward<Args>(args)...);
327  else if (data.mType == Data::EType::SlotEffect)
328  data.USlot.mSlot->trigger(std::forward<Args>(args)...);
329  }
330 
331  if (mFunctionEffects)
332  for (auto& effect : *mFunctionEffects)
333  effect(std::forward<Args>(args)...);
334  }
335 
336  template <typename... Args>
338  {
339  disconnect();
340  for (auto cause : rhs.mCauses)
341  cause->connect(*this);
342  }
343 
344  template <typename... Args>
346  {
347  copyCauses(other);
348  mFunction = other.mFunction;
349  other.disconnect();
350  other.mFunction = nullptr;
351  return *this;
352  }
353 
354  template <typename... Args>
356  {
357  copyCauses(other);
358  mFunction = other.mFunction;
359  return *this;
360  }
361 
362  template <typename... Args>
363  void nap::Slot<Args...>::trigger(Args... args)
364  {
365  if (mFunction)
366  mFunction(std::forward<Args>(args)...);
367  }
368 
369  template <typename... Args>
371  {
372  for (int index = mCauses.size() - 1; index >= 0; --index)
373  mCauses[index]->disconnect(*this);
374  }
375 
376  template <typename... Args>
378  {
379  mCauses.push_back(&event);
380  }
381 
382  template <typename... Args>
383  void Slot<Args...>::removeCause(Signal<Args...>& event)
384  {
385  for (typename SignalList::iterator pos = mCauses.begin(); pos != mCauses.end(); ++pos)
386  {
387  if ((*pos) == &event)
388  {
389  mCauses.erase(pos);
390  break;
391  }
392  }
393  }
394 
395 } // End Namespace nap
396 
397 
399 // Macros
401 
402 
403 // Creates a slot with the given @NAME and @TYPE and binds it to @FUNCTION using a lambda
404 #define NSLOT(NAME, TYPE, FUNCTION) nap::Slot<TYPE> NAME = {[&](TYPE inValue) -> void { FUNCTION(inValue); }};
405 
406 // Creates a slot with @NAME and @TYPE without binding it to a function
407 #define CREATE_SLOT(NAME, TYPE) nap::Slot<TYPE> NAME;
408 
409 // Binds the already defined slot with @NAME and @TYPE to @FUNCTION
410 #define BIND_SLOT(NAME, TYPE, FUNCTION) NAME([&](TYPE inValue) -> void { FUNCTION(inValue); })
411 
412 // SIGNAL MACRO
413 #define NSIGNAL(NAME, TYPE) nap::Signal<TYPE> NAME;
nap::Slot< ControllerValue >::Function
std::function< void(Args... args)> Function
Definition: signalslot.h:132
nap::Slot::~Slot
~Slot()
Definition: signalslot.h:167
nap::Slot
Definition: signalslot.h:18
nap::Signal::disconnect
void disconnect(Slot< Args... > &slot)
Definition: signalslot.h:295
nap::Signal::~Signal
~Signal()
Definition: signalslot.h:223
nap::Slot::Slot
Slot(U *parent, F memberFunction, Signal< Args... > &signal)
Definition: signalslot.h:163
nap::Slot::Slot
Slot()=default
nap::Signal::operator()
void operator()(Args... args)
Definition: signalslot.h:73
nap::Slot::Slot
Slot(U *parent, F memberFunction)
Definition: signalslot.h:152
nap::Signal< ControllerValue >::Function
std::function< void(Args... args)> Function
Definition: signalslot.h:28
nap::Slot::trigger
void trigger(Args... args)
Definition: signalslot.h:363
nap::Signal
Definition: signalslot.h:25
nap::Signal::trigger
void trigger(Args... args)
Definition: signalslot.h:321
nap::Slot::operator=
Slot & operator=(Slot &&other)
nap::Slot::copyCauses
void copyCauses(const Slot &rhs)
Definition: signalslot.h:337
nap
Definition: templateapp.h:17
nap::Slot::disconnect
void disconnect()
Definition: signalslot.h:370
nap::Signal::connect
void connect(U *object, F memberFunction)
Definition: signalslot.h:68
nap::Signal::connect
void connect(Slot< Args... > &slot)
Definition: signalslot.h:285
nap::Slot::setFunction
void setFunction(Function func)
Definition: signalslot.h:178
nap::Slot::Slot
Slot(Function inFunction)
Definition: signalslot.h:142