SimpleSignal.h 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #pragma once
  2. // CC0 Public Domain: http://creativecommons.org/publicdomain/zero/1.0/
  3. #pragma once
  4. #include <memory>
  5. #include <functional>
  6. #include <list>
  7. #include <vector>
  8. #include <algorithm>
  9. namespace Simple {
  10. namespace Lib {
  11. /// ProtoSignal is the template implementation for callback list.
  12. template<typename, typename> class ProtoSignal; // undefined
  13. /// CollectorInvocation invokes signal handlers differently depending on return type.
  14. template<typename, typename> struct CollectorInvocation;
  15. /// CollectorLast returns the result of the last signal handler from a signal emission.
  16. template<typename Result>
  17. struct CollectorLast {
  18. using CollectorResult = Result;
  19. explicit CollectorLast() : last_() {}
  20. inline bool operator() (Result r) { last_ = r; return true; }
  21. CollectorResult result() { return last_; }
  22. private:
  23. Result last_;
  24. };
  25. /// CollectorDefault implements the default signal handler collection behaviour.
  26. template<typename Result>
  27. struct CollectorDefault : CollectorLast<Result>
  28. {
  29. };
  30. /// CollectorDefault specialisation for signals with void return type.
  31. template<>
  32. struct CollectorDefault<void> {
  33. using CollectorResult = void;
  34. void result() {}
  35. inline bool operator() (void) { return true; }
  36. };
  37. /// CollectorInvocation specialisation for regular signals.
  38. template<class Collector, class R, class... Args>
  39. struct CollectorInvocation<Collector, R(Args...)> {
  40. inline bool
  41. invoke(Collector& collector, const std::function<R(Args...)>& cbf, Args... args) const
  42. {
  43. return collector(cbf(args...));
  44. }
  45. };
  46. /// CollectorInvocation specialisation for signals with void return type.
  47. template<class Collector, class... Args>
  48. struct CollectorInvocation<Collector, void(Args...)> {
  49. inline bool
  50. invoke(Collector& collector, const std::function<void(Args...)>& cbf, Args... args) const
  51. {
  52. cbf(args...); return collector();
  53. }
  54. };
  55. /// ProtoSignal template specialised for the callback signature and collector.
  56. template<class Collector, class R, class... Args>
  57. class ProtoSignal<R(Args...), Collector> : private CollectorInvocation<Collector, R(Args...)> {
  58. protected:
  59. using CbFunction = std::function<R(Args...)>;
  60. using Result = typename CbFunction::result_type;
  61. using CollectorResult = typename Collector::CollectorResult;
  62. private:
  63. /*copy-ctor*/ ProtoSignal(const ProtoSignal&) = delete;
  64. ProtoSignal& operator= (const ProtoSignal&) = delete;
  65. using CallbackSlot = std::shared_ptr<CbFunction>;
  66. using CallbackList = std::list<CallbackSlot>;
  67. CallbackList callback_list_;
  68. size_t add_cb(const CbFunction& cb)
  69. {
  70. callback_list_.emplace_back(std::make_shared<CbFunction>(cb));
  71. return size_t(callback_list_.back().get());
  72. }
  73. bool remove_cb(size_t id)
  74. {
  75. auto it = std::remove_if(begin(callback_list_), end(callback_list_),
  76. [id](const CallbackSlot& slot) { return size_t(slot.get()) == id; });
  77. bool const removed = it != end(callback_list_);
  78. callback_list_.erase(it, end(callback_list_));
  79. return removed;
  80. }
  81. public:
  82. /// ProtoSignal constructor, connects default callback if non-nullptr.
  83. ProtoSignal(const CbFunction& method)
  84. {
  85. if (method)
  86. add_cb(method);
  87. }
  88. /// ProtoSignal destructor releases all resources associated with this signal.
  89. ~ProtoSignal()
  90. {
  91. }
  92. /// Operator to add a new function or lambda as signal handler, returns a handler connection ID.
  93. size_t connect(const CbFunction& cb) { return add_cb(cb); }
  94. /// Operator to remove a signal handler through it connection ID, returns if a handler was removed.
  95. bool disconnect(size_t connection) { return remove_cb(connection); }
  96. /// Emit a signal, i.e. invoke all its callbacks and collect return types with the Collector.
  97. CollectorResult
  98. emit(Args... args) const
  99. {
  100. Collector collector;
  101. for (auto& slot : callback_list_) {
  102. if (slot) {
  103. const bool continue_emission = this->invoke(collector, *slot, args...);
  104. if (!continue_emission)
  105. break;
  106. }
  107. }
  108. return collector.result();
  109. }
  110. // Number of connected slots.
  111. std::size_t
  112. size() const
  113. {
  114. return callback_list_.size();
  115. }
  116. };
  117. } // Lib
  118. // namespace Simple
  119. /**
  120. * Signal is a template type providing an interface for arbitrary callback lists.
  121. * A signal type needs to be declared with the function signature of its callbacks,
  122. * and optionally a return result collector class type.
  123. * Signal callbacks can be added with operator+= to a signal and removed with operator-=, using
  124. * a callback connection ID return by operator+= as argument.
  125. * The callbacks of a signal are invoked with the emit() method and arguments according to the signature.
  126. * The result returned by emit() depends on the signal collector class. By default, the result of
  127. * the last callback is returned from emit(). Collectors can be implemented to accumulate callback
  128. * results or to halt a running emissions in correspondance to callback results.
  129. * The signal implementation is safe against recursion, so callbacks may be removed and
  130. * added during a signal emission and recursive emit() calls are also safe.
  131. * The overhead of an unused signal is intentionally kept very low, around the size of a single pointer.
  132. * Note that the Signal template types is non-copyable.
  133. */
  134. template <typename SignalSignature, class Collector = Lib::CollectorDefault<typename std::function<SignalSignature>::result_type> >
  135. struct Signal /*final*/ :
  136. Lib::ProtoSignal<SignalSignature, Collector>
  137. {
  138. using ProtoSignal = Lib::ProtoSignal<SignalSignature, Collector>;
  139. using CbFunction = typename ProtoSignal::CbFunction;
  140. /// Signal constructor, supports a default callback as argument.
  141. Signal(const CbFunction& method = CbFunction()) : ProtoSignal(method) {}
  142. };
  143. /// This function creates a std::function by binding @a object to the member function pointer @a method.
  144. template<class Instance, class Class, class R, class... Args> std::function<R(Args...)>
  145. slot(Instance& object, R(Class::* method) (Args...))
  146. {
  147. return [&object, method](Args... args) { return (object.*method) (args...); };
  148. }
  149. /// This function creates a std::function by binding @a object to the member function pointer @a method.
  150. template<class Class, class R, class... Args> std::function<R(Args...)>
  151. slot(Class* object, R(Class::* method) (Args...))
  152. {
  153. return [object, method](Args... args) { return (object->*method) (args...); };
  154. }
  155. /// Keep signal emissions going while all handlers return !0 (true).
  156. template<typename Result>
  157. struct CollectorUntil0 {
  158. using CollectorResult = Result;
  159. explicit CollectorUntil0() : result_() {}
  160. const CollectorResult& result() { return result_; }
  161. inline bool
  162. operator() (Result r)
  163. {
  164. result_ = r;
  165. return result_ ? true : false;
  166. }
  167. private:
  168. CollectorResult result_;
  169. };
  170. /// Keep signal emissions going while all handlers return 0 (false).
  171. template<typename Result>
  172. struct CollectorWhile0 {
  173. using CollectorResult = Result;
  174. explicit CollectorWhile0() : result_() {}
  175. const CollectorResult& result() { return result_; }
  176. inline bool
  177. operator() (Result r)
  178. {
  179. result_ = r;
  180. return result_ ? false : true;
  181. }
  182. private:
  183. CollectorResult result_;
  184. };
  185. /// CollectorVector returns the result of the all signal handlers from a signal emission in a std::vector.
  186. template<typename Result>
  187. struct CollectorVector {
  188. using CollectorResult = std::vector<Result>;
  189. const CollectorResult& result() { return result_; }
  190. inline bool
  191. operator() (Result r)
  192. {
  193. result_.push_back(r);
  194. return true;
  195. }
  196. private:
  197. CollectorResult result_;
  198. };
  199. } // Simple