NAP
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Pages
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 // Pybind includes
15 #include "python.h"
16 
17 namespace nap
18 {
19 
20  // Forward declarations
21  template<typename... Args> class Slot;
22 
27  template <typename... Args>
28  class Signal final
29  {
30  public:
31  using Function = std::function<void(Args... args)>;
32 
33  // Destruction
34  ~Signal();
35 
42  void connect(Slot<Args...>& slot);
43 
47  void disconnect(Slot<Args...>& slot);
48 
55  void connect(const Function& inFunction);
56 
60  void connect(Signal<Args...>& signal);
61 
65  void disconnect(Signal<Args...>& signal);
66 
67 #ifdef NAP_ENABLE_PYTHON
68 
71  void connect(const pybind11::function pythonFunction);
72 #endif // NAP_ENABLE_PYTHON
73 
77  template <typename U, typename F>
78  void connect(U* object, F memberFunction) { connect(std::bind(memberFunction, object, std::placeholders::_1)); }
79 
83  inline void operator()(Args... args) { trigger(std::forward<Args>(args)...); }
84 
89  void trigger(Args... args);
90 
91  private:
92  // Called by signal when connected
93  void addCause(Signal<Args...>& event);
94  void removeCause(Signal<Args...>& event);
95 
96  private:
104  struct Data
105  {
106  union
107  {
108  struct
109  {
110  Signal<Args...>* mSignal;
111  } USignal;
112 
113  struct
114  {
115  Slot<Args...>* mSlot;
116  } USlot;
117  };
118 
119  enum class EType : uint8_t
120  {
121  SignalCause,
122  SignalEffect,
123  SlotEffect
124  };
125  EType mType;
126  };
127 
128  // Members
129  std::vector<Data> mData; // Signal/slot effects, Signal causes
130  std::unique_ptr<std::vector<Function>> mFunctionEffects; // function objects
131  };
132 
133 
138  template <typename... Args>
139  class Slot final
140  {
141  public:
142  using Function = std::function<void(Args... args)>;
143 
144  public:
145  // Default constructor
146  Slot() = default;
147 
152  Slot(Function inFunction) :
153  mFunction(inFunction)
154  { }
155 
161  template <typename U, typename F>
162  Slot(U* parent, F memberFunction) :
163  mFunction(std::bind(memberFunction, parent, std::placeholders::_1))
164  { }
165 
172  template <typename U, typename F>
173  Slot(U* parent, F memberFunction, Signal<Args...>& signal) :
174  mFunction(std::bind(memberFunction, parent, std::placeholders::_1)) { signal.connect(*this); }
175 
176  // Disconnect slot from signals on destruction
177  ~Slot() { disconnect(); }
178 
182  void disconnect();
183 
188  void setFunction(Function func) { mFunction = func; }
189 
194  void trigger(Args... args);
195 
200  void copyCauses(const Slot& rhs);
201 
206  Slot& operator=(Slot&& other);
207 
213  Slot& operator=(const Slot& other);
214 
215  private:
216 
217  template<typename... Args_> friend class Signal;
218  typedef std::vector<Signal<Args...>*> SignalList;
219 
220  void addCause(Signal<Args...>& event);
221  void removeCause(Signal<Args...>& event);
222 
223  Function mFunction;
224  SignalList mCauses;
225  };
226 
227 
229  // Template Implementations
231 
232  template <typename... Args>
234  {
235  for (auto& data : mData)
236  {
237  if (data.mType == Data::EType::SignalCause)
238  data.USignal.mSignal->disconnect(*this);
239  else if (data.mType == Data::EType::SignalEffect)
240  data.USignal.mSignal->removeCause(*this);
241  else
242  data.USlot.mSlot->removeCause(*this);
243  }
244  }
245 
246  template <typename... Args>
248  {
249  Data data;
250  data.mType = Data::EType::SignalCause;
251  data.USignal.mSignal = &signal;
252  mData.push_back(data);
253  }
254 
255  template <typename... Args>
256  void Signal<Args...>::removeCause(Signal<Args...>& event)
257  {
258  for (int index = 0; index < mData.size(); ++index)
259  {
260  Data& value = mData[index];
261  if (value.mType == Data::EType::SignalCause && value.USignal.mSignal == &event)
262  {
263  mData.erase(mData.begin() + index);
264  break;
265  }
266  }
267  }
268 
269  template <typename... Args>
271  {
272  Data data;
273  data.mType = Data::EType::SignalEffect;
274  data.USignal.mSignal = &signal;
275  mData.push_back(data);
276  signal.addCause(*this);
277  }
278 
279  template <typename... Args>
281  {
282  for (int index = 0; index < mData.size(); ++index)
283  {
284  Data& value = mData[index];
285  if (value.mType == Data::EType::SignalEffect && value.USignal.mSignal == &signal)
286  {
287  value.USignal.mSignal->removeCause(*this);
288  mData.erase(mData.begin() + index);
289  break;
290  }
291  }
292  }
293 
294  template <typename... Args>
296  {
297  Data data;
298  data.mType = Data::EType::SlotEffect;
299  data.USlot.mSlot = &slot;
300  mData.push_back(data);
301  slot.addCause(*this);
302  }
303 
304  template <typename... Args>
306  {
307  for (int index = 0; index < mData.size(); ++index)
308  {
309  Data& data = mData[index];
310  if (data.mType == Data::EType::SlotEffect && data.USlot.mSlot == &slot)
311  {
312  data.USlot.mSlot->removeCause(*this);
313  mData.erase(mData.begin() + index);
314  break;
315  }
316  }
317  }
318 
319 
320  template <typename... Args>
321  void Signal<Args...>::connect(const Function& inFunction)
322  {
323  if (!mFunctionEffects)
324  mFunctionEffects = std::make_unique<std::vector<Function>>();
325 
326  mFunctionEffects->emplace_back(inFunction);
327  }
328 
329 #ifdef NAP_ENABLE_PYTHON
330  template <typename... Args>
331  void Signal<Args...>::connect(const pybind11::function pythonFunction)
332  {
333  Function func = [pythonFunction](Args... args)
334  {
335  try
336  {
337  pythonFunction(pybind11::cast(std::forward<Args>(args)..., std::is_lvalue_reference<Args>::value
338  ? pybind11::return_value_policy::reference : pybind11::return_value_policy::automatic_reference)...);
339  }
340  catch (const pybind11::error_already_set& err)
341  {
342  auto message = std::string("Runtime python error while executing signal: ") + std::string(err.what());
343 
344  // TODO It would be preferable to log python error message using the nap logger.
345  // Unfortunately the logger is not accessible in signalslot.h though because it uses Signals itself.
346  std::cout << message << std::endl;
347  }
348  };
349  connect(func);
350  }
351 #endif // NAP_ENABLE_PYTHON
352 
353  template <typename... Args>
354  void Signal<Args...>::trigger(Args... args)
355  {
356  for (auto& data : mData)
357  {
358  if (data.mType == Data::EType::SignalEffect)
359  data.USignal.mSignal->trigger(std::forward<Args>(args)...);
360  else if (data.mType == Data::EType::SlotEffect)
361  data.USlot.mSlot->trigger(std::forward<Args>(args)...);
362  }
363 
364  if (mFunctionEffects)
365  for (auto& effect : *mFunctionEffects)
366  effect(std::forward<Args>(args)...);
367  }
368 
369  template <typename... Args>
371  {
372  disconnect();
373  for (auto cause : rhs.mCauses)
374  cause->connect(*this);
375  }
376 
377  template <typename... Args>
379  {
380  copyCauses(other);
381  mFunction = other.mFunction;
382  other.disconnect();
383  other.mFunction = nullptr;
384  return *this;
385  }
386 
387  template <typename... Args>
389  {
390  copyCauses(other);
391  mFunction = other.mFunction;
392  return *this;
393  }
394 
395  template <typename... Args>
396  void nap::Slot<Args...>::trigger(Args... args)
397  {
398  if (mFunction)
399  mFunction(std::forward<Args>(args)...);
400  }
401 
402  template <typename... Args>
404  {
405  for (int index = mCauses.size() - 1; index >= 0; --index)
406  mCauses[index]->disconnect(*this);
407  }
408 
409  template <typename... Args>
411  {
412  mCauses.push_back(&event);
413  }
414 
415  template <typename... Args>
416  void Slot<Args...>::removeCause(Signal<Args...>& event)
417  {
418  for (typename SignalList::iterator pos = mCauses.begin(); pos != mCauses.end(); ++pos)
419  {
420  if ((*pos) == &event)
421  {
422  mCauses.erase(pos);
423  break;
424  }
425  }
426  }
427 
428 } // End Namespace nap
429 
430 
432 // Macros
434 
435 
436 // Creates a slot with the given @NAME and @TYPE and binds it to @FUNCTION using a lambda
437 #define NSLOT(NAME, TYPE, FUNCTION) nap::Slot<TYPE> NAME = {[&](TYPE inValue) -> void { FUNCTION(inValue); }};
438 
439 // Creates a slot with @NAME and @TYPE without binding it to a function
440 #define CREATE_SLOT(NAME, TYPE) nap::Slot<TYPE> NAME;
441 
442 // Binds the already defined slot with @NAME and @TYPE to @FUNCTION
443 #define BIND_SLOT(NAME, TYPE, FUNCTION) NAME([&](TYPE inValue) -> void { FUNCTION(inValue); })
444 
445 // SIGNAL MACRO
446 #define NSIGNAL(NAME, TYPE) nap::Signal<TYPE> NAME;
nap::Slot< ControllerValue >::Function
std::function< void(Args... args)> Function
Definition: signalslot.h:142
nap::Slot::~Slot
~Slot()
Definition: signalslot.h:177
nap::Slot
Definition: signalslot.h:21
nap::Signal::disconnect
void disconnect(Slot< Args... > &slot)
Definition: signalslot.h:305
nap::Signal::~Signal
~Signal()
Definition: signalslot.h:233
nap::Slot::Slot
Slot(U *parent, F memberFunction, Signal< Args... > &signal)
Definition: signalslot.h:173
nap::Slot::Slot
Slot()=default
nap::Signal::operator()
void operator()(Args... args)
Definition: signalslot.h:83
nap::Slot::Slot
Slot(U *parent, F memberFunction)
Definition: signalslot.h:162
nap::Signal< const nap::WebSocketConnectionFailedEvent & >::Function
std::function< void(Args... args)> Function
Definition: signalslot.h:31
nap::Slot::trigger
void trigger(Args... args)
Definition: signalslot.h:396
nap::Signal
Definition: signalslot.h:28
nap::Signal::trigger
void trigger(Args... args)
Definition: signalslot.h:354
nap::Slot::operator=
Slot & operator=(Slot &&other)
nap::Slot::copyCauses
void copyCauses(const Slot &rhs)
Definition: signalslot.h:370
nap
Definition: templateapp.h:17
nap::Slot::disconnect
void disconnect()
Definition: signalslot.h:403
nap::Signal::connect
void connect(U *object, F memberFunction)
Definition: signalslot.h:78
nap::Signal::connect
void connect(Slot< Args... > &slot)
Definition: signalslot.h:295
nap::Slot::setFunction
void setFunction(Function func)
Definition: signalslot.h:188
nap::Slot::Slot
Slot(Function inFunction)
Definition: signalslot.h:152