Rapicorn - Experimental UI Toolkit  16.0.0
aidasignal.hh
Go to the documentation of this file.
1  // Licensed CC0 Public Domain: http://creativecommons.org/publicdomain/zero/1.0
2 #ifndef __RAPICORN_AIDA_SIGNAL_HH__
3 #define __RAPICORN_AIDA_SIGNAL_HH__
4 
5 namespace Rapicorn { namespace Aida {
6 
7 namespace Lib {
8 
10 template<typename,typename> class ProtoSignal; // left undefined
11 
13 template<typename> class AsyncSignal; // left undefined
14 
16 template<typename,typename> struct CollectorInvocation;
17 
19 template<typename,typename> struct PromiseInvocation;
20 
22 template<typename Result>
23 struct CollectorLast {
24  typedef Result CollectorResult;
25  explicit CollectorLast () : last_() {}
26  inline bool operator() (Result r) { last_ = r; return true; }
27  CollectorResult result () { return last_; }
28 private:
29  Result last_;
30 };
31 
33 template<typename Result>
35 {};
36 
38 template<>
39 struct CollectorDefault<void> {
40  typedef void CollectorResult;
41  void result () {}
42  inline bool operator() (void) { return true; }
43 };
44 
46 template<class Collector, class R, class... Args>
47 struct CollectorInvocation<Collector, R (Args...)> {
48  static inline bool
49  invoke (Collector &collector, const std::function<R (Args...)> &cbf, Args... args)
50  {
51  return collector (cbf (args...));
52  }
53 };
54 
56 template<class Collector, class... Args>
57 struct CollectorInvocation<Collector, void (Args...)> {
58  static inline bool
59  invoke (Collector &collector, const std::function<void (Args...)> &cbf, Args... args)
60  {
61  cbf (args...); return collector();
62  }
63 };
64 
66 template<class Function>
67 struct HandlerLink {
68  HandlerLink *next, *prev;
69  Function function;
70  int ref_count;
71  explicit HandlerLink (const Function &callback) : next (NULL), prev (NULL), function (callback), ref_count (1) {}
72  /*dtor*/ ~HandlerLink () { AIDA_ASSERT (ref_count == 0); }
73  void incref () { ref_count += 1; AIDA_ASSERT (ref_count > 0); }
74  void decref () { ref_count -= 1; if (!ref_count) delete this; else AIDA_ASSERT (ref_count > 0); }
75  void
76  unlink ()
77  {
78  function = NULL;
79  if (next)
80  next->prev = prev;
81  if (prev)
82  prev->next = next;
83  next = prev = NULL;
84  decref();
85  }
86  size_t
87  add_before (const Function &callback)
88  {
89  HandlerLink *link = new HandlerLink (callback);
90  link->prev = prev; // link to last
91  link->next = this;
92  prev->next = link; // link from last
93  prev = link;
94  static_assert (sizeof (link) == sizeof (size_t), "sizeof size_t");
95  return size_t (link);
96  }
97  bool
98  deactivate (const Function &callback)
99  {
100  if (callback == function)
101  {
102  function = NULL; // deactivate static head
103  return true;
104  }
105  for (HandlerLink *link = this->next ? this->next : this; link != this; link = link->next)
106  if (callback == link->function)
107  {
108  link->unlink(); // deactivate and unlink sibling
109  return true;
110  }
111  return false;
112  }
113  bool
114  remove_sibling (size_t id)
115  {
116  for (HandlerLink *link = this->next ? this->next : this; link != this; link = link->next)
117  if (id == size_t (link))
118  {
119  link->unlink(); // deactivate and unlink sibling
120  return true;
121  }
122  return false;
123  }
124 };
125 
127 template<class Collector, class R, class... Args>
128 class ProtoSignal<R (Args...), Collector> : private CollectorInvocation<Collector, R (Args...)> {
129 protected:
130  typedef std::function<R (Args...)> CbFunction;
131  typedef typename CbFunction::result_type Result;
132  typedef typename Collector::CollectorResult CollectorResult;
134 private:
135  SignalLink *callback_ring_; // linked ring of callback nodes
136  /*copy-ctor*/ ProtoSignal (const ProtoSignal&) = delete;
137  ProtoSignal& operator= (const ProtoSignal&) = delete;
138  void
139  ensure_ring ()
140  {
141  if (!callback_ring_)
142  {
143  callback_ring_ = new SignalLink (CbFunction()); // ref_count = 1
144  callback_ring_->incref(); // ref_count = 2, head of ring, can be deactivated but not removed
145  callback_ring_->next = callback_ring_; // ring head initialization
146  callback_ring_->prev = callback_ring_; // ring tail initialization
147  }
148  }
149 protected:
151  ProtoSignal (const CbFunction &method) :
152  callback_ring_ (NULL)
153  {
154  if (method != NULL)
155  {
156  ensure_ring();
157  callback_ring_->function = method;
158  }
159  }
162  {
163  if (callback_ring_)
164  {
165  while (callback_ring_->next != callback_ring_)
166  callback_ring_->next->unlink();
167  AIDA_ASSERT (callback_ring_->ref_count >= 2);
168  callback_ring_->decref();
169  callback_ring_->decref();
170  }
171  }
172 public:
174  size_t connect (const CbFunction &cb) { ensure_ring(); return callback_ring_->add_before (cb); }
176  bool disconnect (size_t connection) { return callback_ring_ ? callback_ring_->remove_sibling (connection) : false; }
178  CollectorResult
179  emit (Args... args)
180  {
181  // check if an emission is needed
182  Collector collector;
183  if (!callback_ring_)
184  return collector.result();
185  // capture and incref signal handler list
186  const bool have_link0 = callback_ring_->function != NULL;
187  size_t capacity = 1 * have_link0;
188  SignalLink *link;
189  for (link = callback_ring_->next; link != callback_ring_; link = link->next)
190  capacity++; // capacity measuring is O(n), but we expect n to be small generally
191  SignalLink *links[capacity];
192  size_t nlinks = 0;
193  if (have_link0)
194  {
195  callback_ring_->incref();
196  links[nlinks++] = callback_ring_;
197  }
198  for (link = callback_ring_->next; link != callback_ring_; link = link->next)
199  {
200  link->incref();
201  links[nlinks++] = link;
202  }
203  AIDA_ASSERT (nlinks <= capacity);
204  // walk signal handler list, invoke and decref
205  size_t i;
206  for (i = 0; i < nlinks; i++)
207  {
208  SignalLink *link = links[i];
209  if (link->function)
210  {
211  const bool continue_emission = this->invoke (collector, link->function, args...);
212  if (!continue_emission)
213  break;
214  }
215  link->decref();
216  }
217  for (; i < nlinks; i++)
218  links[i]->decref(); // continue decref after 'break'
219  // done
220  return collector.result();
221  }
222 };
223 
225 template<class Promise, class R, class... Args>
226 struct PromiseInvocation<Promise, R (Args...)> {
227  static inline void
228  invoke (Promise &promise, const std::function<R (Args...)> &callback, Args&&... args)
229  {
230  promise.set_value (callback (std::forward<Args> (args)...));
231  }
232 };
233 
235 template<class Promise, class... Args>
236 struct PromiseInvocation<Promise, void (Args...)> {
237  static inline void
238  invoke (Promise &promise, const std::function<void (Args...)> &callback, Args&&... args)
239  {
240  callback (std::forward<Args> (args)...);
241  promise.set_value();
242  }
243 };
244 
246 template<class R, class... Args>
247 class AsyncSignal<R (Args...)> : private PromiseInvocation<std::promise<R>, R (Args...)> {
248 protected:
249  typedef std::function<std::future<R> (Args...)> FutureFunction;
250  typedef std::function<R (Args...)> CbFunction;
252 private:
253  SignalLink *callback_ring_; // linked ring of callback nodes
254  explicit AsyncSignal (const AsyncSignal&) = delete; // non-copyable
255  AsyncSignal& operator= (const AsyncSignal&) = delete; // non-assignable
256  void
257  ensure_ring ()
258  {
259  if (!callback_ring_)
260  {
261  callback_ring_ = new SignalLink (FutureFunction()); // ref_count = 1
262  callback_ring_->incref(); // ref_count = 2, head of ring, can be deactivated but not removed
263  callback_ring_->next = callback_ring_; // ring head initialization
264  callback_ring_->prev = callback_ring_; // ring tail initialization
265  }
266  }
267 protected:
269  AsyncSignal (const FutureFunction &method) :
270  callback_ring_ (NULL)
271  {
272  if (method != NULL)
273  {
274  ensure_ring();
275  callback_ring_->function = method;
276  }
277  }
280  {
281  if (callback_ring_)
282  {
283  while (callback_ring_->next != callback_ring_)
284  callback_ring_->next->unlink();
285  AIDA_ASSERT (callback_ring_->ref_count >= 2);
286  callback_ring_->decref();
287  callback_ring_->decref();
288  }
289  }
290 public:
291  class Emission {
292  typedef std::function<std::future<R> (const FutureFunction&)> FunctionTrampoline;
294  size_t current_;
295  std::future<R> future_;
296  FunctionTrampoline trampoline_;
297  public:
298  Emission (SignalLink *start_link, Args&&... args) :
299  current_ (0)
300  {
301  SignalLink *link = start_link;
302  if (link)
303  do
304  {
305  if (link->function != NULL)
306  {
307  link->incref();
308  links_.push_back (link);
309  }
310  link = link->next;
311  }
312  while (link != start_link);
313  // wrap args into lambda for deferred execution
314  auto lambda =
315  [link] (const FutureFunction &ff, Args... args) // -> std::future<R>
316  {
317  return std::move (ff (args...));
318  };
319  // FIXME: instead of std::bind, use lambda capture: [args...](){return ff(args...);}, but see gcc bug #41933
320  trampoline_ = std::bind (lambda, std::placeholders::_1, std::forward<Args> (args)...);
321  }
322  ~Emission()
323  {
324  for (size_t i = 0; i < links_.size(); i++)
325  links_[i]->decref();
326  }
327  bool has_value () { return future_.valid() && future_ready(); }
328  R get_value () { future_.wait(); return future_.get(); }
329  bool done () { return current_ >= links_.size() && !future_.valid(); }
330  bool pending () { return has_value() || (!future_.valid() && current_ < links_.size()); }
331  bool dispatch () { emit_stepwise(); return !done(); }
332  private:
333  bool
334  future_ready ()
335  {
336  return future_.wait_for (std::chrono::nanoseconds (0)) == std::future_status::ready;
337  }
338  inline void
339  emit_stepwise()
340  {
341  if (future_.valid())
342  return; // processing handler, use has_value
343  if (current_ >= links_.size())
344  return; // done
345  const FutureFunction &function = links_[current_]->function;
346  current_++;
347  if (AIDA_ISLIKELY (function != NULL))
348  future_ = std::move (trampoline_ (function)); // valid() == true
349  }
350  RAPICORN_CLASS_NON_COPYABLE (Emission);
351  };
353  size_t connect_future (const FutureFunction &fcb) { ensure_ring(); return callback_ring_->add_before (fcb); }
354  size_t connect (const CbFunction &callback)
355  {
356  auto lambda =
357  [this, callback] (Args&&... args) // -> std::future<R>
358  {
359  std::promise<R> promise;
360  this->invoke (promise, callback, std::forward<Args> (args)...);
361  return std::move (promise.get_future());
362  };
363  return connect_future (lambda);
364  }
366  bool disconnect (size_t connection) { return callback_ring_ ? callback_ring_->remove_sibling (connection) : false; }
368  Emission*
369  emission (Args&&... args)
370  {
371  return new Emission (callback_ring_, std::forward<Args> (args)...);
372  }
373 };
374 
375 } // Lib
376 // namespace Rapicorn::Aida
377 
393 template <typename SignalSignature, class Collector = Lib::CollectorDefault<typename std::function<SignalSignature>::result_type> >
394 class Signal : protected Lib::ProtoSignal<SignalSignature, Collector>
395 {
397  typedef typename ProtoSignal::CbFunction CbFunction;
398 public:
399  explicit Signal (const Signal&) = delete; // non-copyable
400  Signal& operator= (const Signal&) = delete; // non-assignable
401  using ProtoSignal::emit;
402  class Connector {
403  friend class Signal;
404  Signal &signal_;
405  Connector& operator= (const Connector&) = delete;
406  explicit Connector (Signal &signal) : signal_ (signal) {}
407  public:
409  size_t operator+= (const CbFunction &cb) { return signal_.connect (cb); }
411  bool operator-= (size_t connection_id) { return signal_.disconnect (connection_id); }
412  };
414  Signal (const CbFunction &method = CbFunction()) : ProtoSignal (method) {}
416  Connector operator() () { return Connector (*this); }
417 };
418 
420 template<class Instance, class Class, class R, class... Args> std::function<R (Args...)>
421 slot (Instance &object, R (Class::*method) (Args...))
422 {
423  return [&object, method] (Args... args) { return (object .* method) (args...); };
424 }
425 
427 template<class Class, class R, class... Args> std::function<R (Args...)>
428 slot (Class *object, R (Class::*method) (Args...))
429 {
430  return [object, method] (Args... args) { return (object ->* method) (args...); };
431 }
432 
434 template<typename Result>
436  typedef Result CollectorResult;
437  explicit CollectorUntil0 () : result_() {}
438  const CollectorResult& result () { return result_; }
439  inline bool
440  operator() (Result r)
441  {
442  result_ = r;
443  return result_ ? true : false;
444  }
445 private:
446  CollectorResult result_;
447 };
448 
450 template<typename Result>
452  typedef Result CollectorResult;
453  explicit CollectorWhile0 () : result_() {}
454  const CollectorResult& result () { return result_; }
455  inline bool
456  operator() (Result r)
457  {
458  result_ = r;
459  return result_ ? false : true;
460  }
461 private:
462  CollectorResult result_;
463 };
464 
466 template<typename Result>
469  const CollectorResult& result () { return result_; }
470  inline bool
471  operator() (Result r)
472  {
473  result_.push_back (r);
474  return true;
475  }
476 private:
477  CollectorResult result_;
478 };
479 
481 template<class Object, class SignalSignature>
482 class Connector {
483  typedef std::function<SignalSignature> CbFunction;
484  typedef size_t (Object::*PMF) (size_t, const CbFunction&);
485  Object &instance_;
486  PMF method_;
487 public:
488  Connector (Object &instance, PMF method) : instance_ (instance), method_ (method) {}
490  size_t operator+= (const CbFunction &cb) { return (instance_.*method_) (0, cb); }
492  bool operator-= (size_t connection_id) { return connection_id ? (instance_.*method_) (connection_id, *(CbFunction*) NULL) : false; }
493 };
494 
518 template <typename SignalSignature>
519 class AsyncSignal : protected Lib::AsyncSignal<SignalSignature>
520 {
522  typedef typename BaseSignal::CbFunction CbFunction;
523  typedef typename BaseSignal::FutureFunction FutureFunction;
524  class Connector {
525  AsyncSignal &signal_;
526  friend class AsyncSignal;
527  Connector& operator= (const Connector&) = delete;
528  explicit Connector (AsyncSignal &signal) : signal_ (signal) {}
529  public:
531  size_t connect_future (const FutureFunction &ff) { return signal_.connect_future (ff); }
533  size_t operator+= (const CbFunction &cb) { return signal_.connect (cb); }
535  bool operator-= (size_t connection_id) { return signal_.disconnect (connection_id); }
536  };
537 public:
538  using BaseSignal::Emission;
539  using BaseSignal::emission;
541  AsyncSignal (const FutureFunction &method = FutureFunction()) : BaseSignal (method) {}
543  Connector operator() () { return Connector (*this); }
544 };
545 
546 } } // Rapicorn::Aida
547 
548 #endif // __RAPICORN_AIDA_SIGNAL_HH__
Signal is a template type providing an interface for arbitrary callback lists.
Definition: aidasignal.hh:394
Definition: aidasignal.hh:402
AsyncSignal(const FutureFunction &method)
AsyncSignal constructor, connects default callback if non-NULL.
Definition: aidasignal.hh:269
T wait(T...args)
bool disconnect(size_t connection)
Operator to remove a signal handler through its connection ID, returns if a handler was removed...
Definition: aidasignal.hh:176
PromiseInvocation invokes handlers differently depending on the promise value type.
Definition: aidasignal.hh:19
The Rapicorn namespace encompasses core utilities and toolkit functionality.
Definition: aida.cc:40
bool disconnect(size_t connection)
Operator to remove a signal handler through its connection ID, returns if a handler was removed...
Definition: aidasignal.hh:366
AsyncSignal is a Signal type with support for std::future returns from handlers.
Definition: aidasignal.hh:519
~AsyncSignal()
AsyncSignal destructor releases all resources associated with this signal.
Definition: aidasignal.hh:279
size_t operator+=(const CbFunction &cb)
Operator to add a new function or lambda as signal handler, returns a handler connection ID...
Definition: aidasignal.hh:490
AsyncSignal is the template implementation for callback lists with std::future returns.
Definition: aidasignal.hh:13
Signal(const CbFunction &method=CbFunction())
Signal constructor, supports a default callback as argument.
Definition: aidasignal.hh:414
Connector provides a simple (dis-)connect interfaces for signals on RemoteHandle. ...
Definition: aidasignal.hh:482
Keep signal emissions going while all handlers return 0 (false).
Definition: aidasignal.hh:451
size_t connect_future(const FutureFunction &fcb)
Operator to add a new function or lambda as signal handler, returns a handler connection ID...
Definition: aidasignal.hh:353
size_t connect(const CbFunction &cb)
Operator to add a new function or lambda as signal handler, returns a handler connection ID...
Definition: aidasignal.hh:174
T get_future(T...args)
T push_back(T...args)
CollectorInvocation invokes handlers differently depending on return type.
Definition: aidasignal.hh:16
size_t operator+=(const CbFunction &cb)
Operator to add a new function or lambda as signal handler, returns a handler connection ID...
Definition: aidasignal.hh:409
T valid(T...args)
T bind(T...args)
CollectorDefault implements the default handler collection behaviour.
Definition: aidasignal.hh:34
signal
CollectorLast returns the result of the last handler from a signal emission.
Definition: aidasignal.hh:23
T move(T...args)
AsyncSignal(const FutureFunction &method=FutureFunction())
Signal constructor, supports a default callback as argument.
Definition: aidasignal.hh:541
T get(T...args)
Keep signal emissions going while all handlers return !0 (true).
Definition: aidasignal.hh:435
STL class.
Connector operator()()
Retrieve a connector object with operator+= and operator-= to connect and disconnect signal handlers...
Definition: aidasignal.hh:543
Emission * emission(Args &&...args)
Create an asynchronous signal emission.
Definition: aidasignal.hh:369
Base class for all interface types.
Definition: interfaces.idl:219
Connector operator()()
Retrieve a connector object with operator+= and operator-= to connect and disconnect signal handlers...
Definition: aidasignal.hh:416
std::function< R(Args...)> slot(Instance &object, R(Class::*method)(Args...))
This function creates a std::function by binding object to the member function pointer method...
Definition: aidasignal.hh:421
~ProtoSignal()
ProtoSignal destructor releases all resources associated with this signal.
Definition: aidasignal.hh:161
typedef size_t
CollectorVector returns the result of the all signal handlers from a signal emission in a std::vector...
Definition: aidasignal.hh:467
T wait_for(T...args)
bool operator-=(size_t connection_id)
Operator to remove a signal handler through its connection ID, returns if a handler was removed...
Definition: aidasignal.hh:492
CollectorResult emit(Args...args)
Emit a signal, i.e. invoke all its callbacks and collect return types with the Collector.
Definition: aidasignal.hh:179
bool operator-=(size_t connection_id)
Operator to remove a signal handler through its connection ID, returns if a handler was removed...
Definition: aidasignal.hh:411
connect
ProtoSignal(const CbFunction &method)
ProtoSignal constructor, connects default callback if non-NULL.
Definition: aidasignal.hh:151
ProtoSignal is the template implementation for callback list.
Definition: aidasignal.hh:10
link