Btk
signal.hpp
1 #if !defined(_BTKSIGNAL_HPP_)
2 #define _BTKSIGNAL_HPP_
3 #include <type_traits>
4 #include <utility>
5 #include <cstddef>
6 #include <list>
7 #include "../defs.hpp"
8 namespace Btk{
9  //TODO: improve thread safety
10 
11  [[noreturn]] void throwEmptySignal();
12  namespace Impl{
13  struct SlotBase{
14  //pointer to delete
15  typedef void(*DeleteFn)(void *self,bool from_hasslots);
16  SlotBase(DeleteFn fn):delete_ptr(fn){};
17  //cleanup self
18  //from has_slots = true if it is called by ~HasSlots()
19  void cleanup(bool from_hasslots = false){
20  delete_ptr(this,from_hasslots);
21  };
22  DeleteFn delete_ptr;
23  };
24  //Slot
25  template<class RetT,class ...Args>
26  struct Slot:public SlotBase{
27  //Invoke Ptr
28  typedef RetT(*InvokeFn)(const void *self,Args ...args);
29  //construct Slot
30  Slot(DeleteFn df,InvokeFn fn):
31  SlotBase(df),
32  invoke_ptr(fn){};
33  //invoke self
34  RetT invoke(Args ...args) const{
35  return invoke_ptr(this,std::forward<Args>(args)...);
36  }
37  InvokeFn invoke_ptr;
38  };
39  //SlotDetail
40  template<class T,class RetT,class ...Args>
41  struct SlotDetail:public Slot<RetT,Args...>{
42  SlotDetail(T &&f):
43  Slot<RetT,Args...>(Delete,Invoke),
44  fn(std::forward<T>(f)){};
45 
46  T fn;
47  //DeleteSelf
48  static void Delete(void *self,bool){
49  delete static_cast<SlotDetail*>(self);
50  }
51  //InvokeSelf
52  static RetT Invoke(const void *__self,Args ...args){
53  const SlotDetail *slot = static_cast<const SlotDetail*>(__self);
54  return slot->fn(std::forward<Args>(args)...);
55  }
56  };
57  };
58  //Signal
59  struct BTKAPI SignalBase{
60  SignalBase();
61  SignalBase(const SignalBase &) = delete;
62  ~SignalBase();
63  //disconnect all slots
64  void disconnect_all(){
65  for(auto iter = slots.begin();iter != slots.end();){
66  (*iter)->cleanup();
67  iter = slots.erase(iter);
68  }
69  }
70  bool empty() const noexcept{
71  return slots.empty();
72  };
73  //check is empty
74  bool operator ==(std::nullptr_t) const noexcept{
75  return empty();
76  };
77  bool operator !=(std::nullptr_t) const noexcept{
78  return not empty();
79  };
80  operator bool() const noexcept{
81  return not empty();
82  };
83  size_t connected_slots() const noexcept{
84  return slots.size();
85  };
86  std::list<Impl::SlotBase*> slots;
87  };
88 
89  struct Connection{
90  SignalBase *current;
91  std::list<Impl::SlotBase*>::iterator iter;
92  //disconnect this connection
93  void disconnect(bool from_hasslots = false){
94  (*iter)->cleanup(from_hasslots);
95  current->slots.erase(iter);
96  }
97  //disconnect after checking
98  //return false if failed
99  bool try_disconnect(bool from_hasslots = false){
100  std::list<Impl::SlotBase*>::iterator i;
101  for(i = current->slots.begin();i != current->slots.end(); ++i){
102  if(i == iter){
103  //get it
104  disconnect(from_hasslots);
105  return true;
106  }
107  }
108  return false;
109  }
110  };
111 
112  template<class RetT>
113  struct Signal;
114 
115  template<class RetT,class ...Args>
116  struct Signal<RetT(Args...)>;
117 
118  //a class to auto track connections
119  struct BTKAPI HasSlots{
120  HasSlots();
121  HasSlots(const HasSlots &) = delete;
122  ~HasSlots();
123 
124  //disconnect all signal
125  void disconnect_all();
126 
127  std::list<Connection> _connections;
128  };
129 
130  namespace Impl{
131  //ClassSlot to hold member pointer
132  template<class Class,class Method,class RetT,class ...Args>
133  struct ClassSlot:public Slot<RetT,Args...>{
134  ClassSlot(Class *ptr,Method m)
135  :Slot<RetT,Args...>(Delete,Invoke),
136  class_ptr(ptr),
137  method(m){
138 
139  }
140  Class *class_ptr;
141  Method method;
142  std::list<Connection>::iterator iter;
143  //track HasSlots connection
144  static RetT Invoke(const void *__self,Args ...args){
145  const ClassSlot *slot = static_cast<const ClassSlot*>(__self);
146  return ((slot->class_ptr)->*(slot->method))(
147  std::forward<Args>(args)...
148  );
149  }
150  static void Delete(void *self,bool from_hasslots){
151  ClassSlot *slot = static_cast<ClassSlot*>(self);
152  if(not from_hasslots){
153  //not called by ~HasSlots()
154  //unregister it
155  slot->class_ptr->HasSlots::_connections.erase(
156  slot->iter
157  );
158  }
159  delete slot;
160  }
161  };
162  };
169  template<class RetT,class ...Args>
170  struct Signal<RetT(Args...)>:public SignalBase{
171  Signal(){}
172  Signal(const Signal &) = delete;
173 
174  RetT emit(Args ...args) const{
175  if constexpr(std::is_same<RetT,void>::value){
176  //no return value
177  for(auto s:slots){
178  static_cast<Impl::Slot<RetT,Args...>*>(s)->invoke(
179  std::forward<Args>(args)...
180  );
181  }
182  }
183  else{
184  //Get return value
185  RetT ret{};
186  for(auto s:slots){
187  ret = static_cast<Impl::Slot<RetT,Args...>*>(s)->invoke(
188  std::forward<Args>(args)...
189  );
190  }
191  return ret;
192  }
193  }
194  //connect anything callable
195  template<class Callable>
196  Connection connect(Callable &&callable){
197  slots.push_back(
199  std::forward<Callable>(callable)
200  )
201  );
202  return Connection{
203  this,
204  --slots.end()
205  };
206  }
207  //connect object inherit HasSlots
208  template<class Callable,class Object>
209  void connect(Callable &&callable,Object *object){
210  static_assert(std::is_base_of<HasSlots,Object>::value,"Object must inherit HasSlots");
211 
212  auto *solt = new Impl::ClassSlot<Object,Callable,RetT,Args...>(
213  object,callable
214  );
215  slots.push_back(solt);
216 
217  object->HasSlots::_connections.push_back({
218  this,
219  --slots.end()
220  });
221  //set hasslots iterator
222 
223  solt->iter = --(object->HasSlots::_connections.end());
224  }
225  RetT operator ()(Args ...args) const{
226  return Signal::emit(std::forward<Args>(args)...);
227  }
228 
229  typedef RetT result_type;
230  };
231 };
232 
233 
234 #endif // _BTKSIGNAL_HPP_
Definition: signal.hpp:13
Definition: signal.hpp:113
Definition: signal.hpp:89
This header include many useful containers.
Definition: async.hpp:7
Definition: signal.hpp:133
Definition: signal.hpp:26
Definition: signal.hpp:119
Definition: signal.hpp:59
Definition: signal.hpp:41