jle_cpp_tk  0.0 2015-04-03 sh1:"d699093732dd5f321606d0ff7a6b63b229f1922c"
A small, safe, selft contained, soft-realtime C++ toolkit
signal_slot.hpp
1 #ifndef JLE_SIGNAL_SLOT_H
2 #define JLE_SIGNAL_SLOT_H
3 
5 
6 
7 #include <list>
8 #include <stdexcept> // run_time_error exception
9 #include <tuple>
10 
11 #include "core/basic_types.hpp"
12 #include "core/shared_ptr.hpp"
13 
14 #include <iostream> // todo: provisional. to suport cerr
15 
16 
17 #define JLE_CONNECT_METHOD(__SIGNAL__, __INSTANCE__, __METHOD_NAME__) \
18  (__SIGNAL__).connect(&__INSTANCE__, &std::remove_reference<decltype(__INSTANCE__)>::type::__METHOD_NAME__);
19 
20 #define JLE_CONNECT_THIS(__SIGNAL__, __METHOD_NAME__) JLE_CONNECT_METHOD(__SIGNAL__, *this, __METHOD_NAME__)
21 
22 
23 
24 
25 namespace jle
26 {
27 
28 
29 const int SIGNAL_SLOT_MAX_DEEP_EMIT = 20;
30 
31 
32 //----------------------------------------------------------------------------
33 
34 // B a s e C o n n e c t i o n
35 
36 //----------------------------------------------------------------------------
37 namespace internal {
38 
39 
40 class base_connection
41 {
42  bool disconnected;
43 
44 public:
45  base_connection() : disconnected(false) {};
46  base_connection(const base_connection&)=delete;
47  base_connection(base_connection&&)=default;
48  base_connection& operator=(const base_connection&)=delete;
49  base_connection& operator=(base_connection&&)=default;
50 
51  virtual ~base_connection(){};
52  virtual bool isSame(base_connection*)= 0;
53 
54  void disconnect(void) {
55  disconnected = true;
56  };
57 
58  bool is_disconnected(void) const { return disconnected; };
59 
60 };
61 
62 }; // namespace internal
63 
64 
65 
66 
67 
68 
69 
70 //----------------------------------------------------------------------------
71 
72 // s i g n a l _ r e c e p t o r
73 
74 //----------------------------------------------------------------------------
75 
76 
88  std::list<jle::weak_ptr<internal::base_connection>> list_connections;
89 
90 public:
91  signal_receptor(void) {};
92 
93  signal_receptor(const signal_receptor&)=delete;
95 
96  signal_receptor& operator=(const signal_receptor&)=delete;
97  signal_receptor& operator=(signal_receptor&&)=default;
98 
99 
100  void internal_register_connection(jle::shared_ptr<internal::base_connection> pconnection) {
101  list_connections.push_back(pconnection);
102  };
103 
104  void un_internal_register_connection(jle::shared_ptr<internal::base_connection> connection) {
105  auto it_connection = list_connections.begin();
106  while(it_connection != list_connections.end())
107  {
108  // it is safe to delete here
109  // we only itereate the list at this point and in destructor
110  if ( it_connection->expired() ) {
111  it_connection = list_connections.erase(it_connection);
112  }
113  else {
114  auto sp_it = it_connection->lock();
115  // cleaning
116  if ( sp_it->is_disconnected()) {
117  it_connection = list_connections.erase(it_connection);
118  }
119  else if ( sp_it->isSame(connection.get()) ) {
120  sp_it->disconnect();
121  it_connection = list_connections.erase(it_connection);
122  }
123  else
124  ++it_connection;
125  }
126  }
127  };
128 
129  virtual ~signal_receptor(void) {
130  try{
131  // say goodbye to singals pointing to us
132  auto it_connection = list_connections.begin();
133  while(it_connection != list_connections.end())
134  {
135  if ( it_connection->expired() ) {
136  it_connection = list_connections.erase(it_connection);
137  continue;
138  }
139  it_connection->lock()->disconnect();
140  ++it_connection;
141  }
142  } catch(...){
143  std::cerr << __PRETTY_FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << ")" << "exception on destructor" << std::endl;
144  }
145  };
146 
147 };
148 
149 
150 
151 namespace internal {
152 
153 
154 template<typename... Args>
155 class base_connectionParam
156  : public base_connection
157 {
158 
159 public:
160  base_connectionParam() : base_connection() {};
161 
162  virtual ~base_connectionParam(){};
163 
164  virtual void emit(Args...)=0;
165 
166  virtual signal_receptor* get_signal_receptor(void) const = 0;
167 
168 };
169 
170 
171 
172 
173 
174 template<typename TReceiver, typename... Args>
175 class connection
176  : public base_connectionParam<Args...>
177 {
178  TReceiver* pt2object;
179  void (TReceiver::*fpt)(Args...);
180 
181 
182 public:
183  connection(TReceiver* _pt2Object, void (TReceiver::*_fpt)(Args...))
184  : pt2object(_pt2Object),
185  fpt(_fpt)
186  {}
187 
188  void emit(Args... args){
189  (*pt2object.*fpt)(args...);
190  };
191 
192 
193 
194  TReceiver* get_receiver(void) {
195  return pt2object;
196  };
197  void (TReceiver::*get_func_pointer(void)) (Args...) {
198  return fpt;
199  };
200 
201  bool isSame(base_connection* pbc)
202  {
203  connection<TReceiver, Args...>* pc = dynamic_cast<connection<TReceiver, Args...>* > (pbc);
204  if (pc)
205  return this == pc;
206  else
207  return false;
208  }
209 
210  signal_receptor* get_signal_receptor(void) const {
211  return pt2object;
212  }
213 
214 };
215 
216 
217 }; // namespace internal {
218 
219 
220 
231 template <typename... Args>
232 class signal
233  : public signal_receptor
234 {
235  int processing_emit{0};
236  std::list< jle::shared_ptr<internal::base_connectionParam<Args...> > > connections;
237 
238  // connection to funcions (pointer and is_connected?)
239  std::list< std::tuple<void (*)(Args...), bool> > functConnections;
240 
241 
242 public:
243  signal() = default;
244  signal(const signal&)=delete;
245  signal(signal&&)=default;
246  signal& operator=(const signal&)=delete;
247  signal& operator=(signal&&)=default;
248 
249  ~signal() {
250  try{
251  disconnect_all();
252  if (processing_emit>0)
253  throw std::runtime_error("~signal<> on emit");
254  // pending to check
255  } catch(...){
256  std::cerr << __PRETTY_FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << ")" << "exception on destructor" << std::endl;
257  }
258  }
259 
265  int get_processing_emits(void) const { return processing_emit; }
266 
267  template<typename TReceiver>
268  void connect(TReceiver* receiver, void (TReceiver::*fpt)(Args...)) {
269  if(connections.size()%5==0) // small optimization to avoid clean on ervery connection
270  {
271  auto it2ptrbase_connection = connections.begin();
272  while(it2ptrbase_connection != connections.end())
273  {
274  if ((*it2ptrbase_connection)->is_disconnected() == true)
275  {
276  if (processing_emit==0)
277  {
278  it2ptrbase_connection = connections.erase(it2ptrbase_connection);
279  continue; // avoid ++it2ptrbase_connection
280  }
281  }
282  ++it2ptrbase_connection;
283  }
284  }
285 
286 
287  jle::shared_ptr<internal::base_connectionParam<Args...> >
288  pbc (new internal::connection<TReceiver, Args...>(receiver, fpt));
289  connections.push_back(pbc);
290  receiver->internal_register_connection(jle::shared_ptr<internal::base_connection>(pbc));
291  };
292 
299  template<typename TReceiver>
300  bool disconnect(TReceiver* receiver, void (TReceiver::*fpt)(Args...)) {
301  auto it2ptrbase_connection = connections.begin();
302  while(it2ptrbase_connection != connections.end())
303  {
304  internal::connection<TReceiver>* pconnection = dynamic_cast<internal::connection<TReceiver>* > (it2ptrbase_connection->get());
305  if (
306  pconnection
307  &&
308  pconnection->get_receiver() == receiver
309  &&
310  pconnection->get_func_pointer() == fpt
311  &&
312  (*it2ptrbase_connection)->is_disconnected() == false // this line reduces performance
313  )
314  {
315  receiver->un_internal_register_connection(
316  jle::shared_ptr<internal::base_connection>(*it2ptrbase_connection)
317  );
318  // WARNING: someone could disconnect from emit context
319  // it's not safe to delete here from list
320  if (processing_emit>0)
321  (*it2ptrbase_connection)->disconnect();
322  else
323  {
324  connections.erase(it2ptrbase_connection);
325  }
326  return true;
327  }
328  ++it2ptrbase_connection;
329  }
330  return false;
331  };
332 
338  void disconnect_all(void) {
339  // cleaning
340  for(auto&& it2ptrbase_connection : connections)
341  {
342  if ( it2ptrbase_connection->is_disconnected() == false) {
343  signal_receptor* sr = it2ptrbase_connection->get_signal_receptor();
344  sr->un_internal_register_connection(jle::shared_ptr<internal::base_connection>(it2ptrbase_connection));
345  if (processing_emit>0)
346  it2ptrbase_connection->disconnect();
347 
348  }
349  }
350 
351  if (processing_emit>0)
352  {
353  for(auto itconnection2funct : functConnections)
354  {
355  std::get<1>(itconnection2funct) = false;
356  }
357  }
358  else
359  {
360  functConnections.clear();
361  connections.clear();
362  }
363 
364  };
365 
372  int notify(Args... args) {
373  return emit(args...);
374  };
375 
385  int emit(Args... args) {
386  int count=0;
387  try
388  {
389  ++processing_emit;
390  if (processing_emit>jle::SIGNAL_SLOT_MAX_DEEP_EMIT)
391  throw std::runtime_error("too deep recursion on emit");
392 
393  auto itconnection = connections.begin();
394  while(itconnection != connections.end())
395  {
396  try {
397  // WARNING: someone could disconnect from emmit context
398  if ( (*itconnection)->is_disconnected() == false) {
399  (*itconnection)->emit(args...);
400  ++count;
401  ++itconnection;
402  }
403  else {
404  itconnection = connections.erase(itconnection);
405  // Unregister is called automatically
406  }
407  } catch(...) {
408  std::cerr << "error on signal::emit()" << std::endl;
409  }
410  };
411 
412 
413  // functions are different
414  auto itconnection2funct = functConnections.begin();
415  while(itconnection2funct != functConnections.end())
416  {
417  try {
418  if (std::get<1>(*itconnection2funct))
419  {
420  (std::get<0>(*itconnection2funct))(args...);
421  ++count;
422  ++itconnection2funct;
423  }
424  else
425  {
426  itconnection2funct = functConnections.erase(itconnection2funct);
427  }
428  } catch(...) {
429  std::cerr << "error on signal::emit()" << std::endl;
430  }
431  }
432  }
433  catch (...)
434  {
435  --processing_emit;
436  // It could happend if we destroy the signal during processing emit
437  throw;
438  }
439  --processing_emit;
440  return count;
441  };
442 
448  void operator()(Args... args) {
449  emit(args...);
450  };
451 
452  // syntax shortut to connect with other signals
453  template<typename TReceiver>
454  void connect(TReceiver* receiver) {
455  connect(receiver, &jle::signal<Args...>::operator());
456  }
457 
458 
462  void connect( void (*pt2Function)(Args...) ) {
463  functConnections.push_back( std::make_tuple(pt2Function, true));
464  }
465 
469  bool disconnect( void (*pt2Function)(Args...) ) {
470  auto itconnection2funct = functConnections.begin();
471  while(itconnection2funct != functConnections.end())
472  {
473  if (pt2Function == std::get<0>(*itconnection2funct)) {
474  if(processing_emit>0)
475  std::get<1>(*itconnection2funct) = false;
476  else
477  {
478  functConnections.erase(itconnection2funct);
479  }
480  return true;
481  }
482  ++itconnection2funct;
483  }
484  return false;
485  }
486 
487 
488 };
489 
490 
491 
492 }; // end namespace
493 
494 
495 #endif // JLE_SIGNAL_SLOT_H
bool disconnect(TReceiver *receiver, void(TReceiver::*fpt)(Args...))
disconnect a signal from a method
Definition: signal_slot.hpp:300
void connect(void(*pt2Function)(Args...))
connect a signal to a function
Definition: signal_slot.hpp:462
int get_processing_emits(void) const
it will give the number of emits on scope
Definition: signal_slot.hpp:265
bool disconnect(void(*pt2Function)(Args...))
disonnect a signal from a function
Definition: signal_slot.hpp:469
void disconnect_all(void)
disconnect all signals
Definition: signal_slot.hpp:338
Any object connected to signals, has to inherit from signal_receptor.
Definition: signal_slot.hpp:87
A safe std smart pointer WRAPPER.
Definition: shared_ptr.hpp:39
signal instance to connect and emit
Definition: signal_slot.hpp:232
int emit(Args...args)
call all connected to the signal
Definition: signal_slot.hpp:385
void operator()(Args...args)
same as emit
Definition: signal_slot.hpp:448
generic namespace
Definition: alarm.cpp:12
int notify(Args...args)
same as emit
Definition: signal_slot.hpp:372