libAlgAudio  v1.99-440-g08538e5-dirty
The development library for AlgAudio framework.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Signal.hpp
Go to the documentation of this file.
1 #ifndef CSIGNAL_HPP
2 #define CSIGNAL_HPP
3 /*
4 This file is part of AlgAudio.
5 
6 AlgAudio, Copyright (C) 2015 CeTA - Audiovisual Technology Center
7 
8 AlgAudio is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation, either version 3 of the
11 License, or (at your option) any later version.
12 
13 AlgAudio is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
17 
18 You should have received a copy of the GNU Lesser General Public License
19 along with AlgAudio. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include <vector>
23 #include <list>
24 #include <map>
25 #include <functional>
26 #include <iostream>
27 #include <memory>
28 
29 namespace AlgAudio{
30 
31 class SignalBase;
32 
45 public:
47  Subscription() : id(0), target(nullptr) {}
49  Subscription(Subscription&& other);
54  inline bool IsEmpty() const { return target == nullptr;}
57  void Release();
58  template <typename...>
59  friend class Signal;
60  Subscription(const Subscription& other) = delete; // no copy-constructing
61  Subscription& operator=(const Subscription& other) = delete; // no copy assignment
62  friend class SignalBase;
63 private:
64  Subscription(int id_, SignalBase* parent) : id(id_), target(parent) {}
65  int id;
66  // This field is used to track who is my parent, and who where should I
67  // unsubscribe from when releasing. This cannot be a pointer, because the
68  // signal might have been already destroyed and we would be left with an
69  // invalid pointer. Therefore we store all signals in a global list, and
70  // remove then when they are destroyed.
71  // int parent_signal_id;
72  SignalBase* target = nullptr;
73 };
74 
77 class SignalBase{
78 protected:
79  SignalBase();
80  SignalBase(const SignalBase&) = delete;
81  virtual ~SignalBase();
82  SignalBase& operator=(const SignalBase&) = delete;
83  std::list< Subscription* > subscriptions;
84  void SubscriptionAddressChanged(Subscription* old, Subscription* n);
85  virtual void RemoveSubscriptionByID(int, Subscription*) = 0;
87  friend class Subscription;
88 };
89 
90 
144 template <typename... Types>
145 class Signal : public SignalBase{
146 private:
147  std::list< std::function<void(Types...)> > subscribers_forever;
148  std::map< int, std::function<void(Types...)> > subscribers_with_id;
149  std::list< std::function<void(Types...)> > subscribers_once;
150  void RemoveSubscriptionByID(int id, Subscription* ptr) override{
151  auto it = subscribers_with_id.find(id);
152  if(it == subscribers_with_id.end()){
153  std::cout << "WARNING: removing an unexisting subscription!" << std::endl;
154  }else{
155  //std::cout << "WARNING: Erasing sub with id " << id << " size = "<< subscribers_with_id.size() << std::endl;
156  subscribers_with_id.erase(it);
157  }
158  subscriptions.remove(ptr);
159  }
160 public:
161  Signal<Types...>() {}
163  Signal(const Signal& other) = delete;
164  Signal(Signal&& other) = delete;
165  Signal& operator=(const Signal& other) = delete;
166  Signal& operator=(Signal&& other) = delete;
169  Subscription Subscribe( std::function<void(Types...)> f ) __attribute__((warn_unused_result));
172  void SubscribeForever( std::function<void(Types...)> f ) { subscribers_forever.push_back(f); }
176  void SubscribeOnce( std::function<void(Types...)> f ) { subscribers_once.push_back(f); }
177  template<class C>
178  Subscription Subscribe( C* class_ptr, void (C::*member_ptr)(Types...) ) __attribute__((warn_unused_result));
179  template<class C>
180  void SubscribeForever( C* class_ptr, void (C::*member_ptr)(Types...)) { subscribers_forever.push_back( std::bind(member_ptr,class_ptr,std::placeholders::_1) ); }
181  template<class C>
182  void SubscribeOnce( C* class_ptr, void (C::*member_ptr)(Types...)) { subscribers_once.push_back( std::bind(member_ptr,class_ptr,std::placeholders::_1) ); }
183 
187  bool Happen(Types... t) {
188  // First, gather the list of all stuff to call. It has to be a temporary
189  // local list, because the calees may modify some of the lists by
190  // inserting new subscriptions.
191  std::list< std::function<void(Types...)> > list_to_call;
192  for(auto& f : subscribers_forever) list_to_call.push_back(f);
193  for(auto& f : subscribers_once) list_to_call.push_back(f);
194  for(auto& it : subscribers_with_id) list_to_call.push_back(it.second);
195  // Clearing the list before calling calees, so that if they add new
196  // one-time-subscriptions, they won't be lost.
197  subscribers_once.clear();
198  // Call everybody.
199  for(auto f : list_to_call) f(t...);
200  return !list_to_call.empty();
201  }
202  friend class Subscription;
204  unsigned int Count(){return subscribers_forever.size() + subscribers_once.size() + subscribers_with_id.size();}
205 };
206 
207 template <typename... Types>
208 Subscription __attribute__((warn_unused_result)) Signal<Types...>::Subscribe( std::function<void(Types...)> f ) {
209  int sub_id = ++subscription_id_counter;
210  subscribers_with_id[sub_id] = f;
211  //std::cout << "New SUB registered " << sub_id << std::endl;
212  auto p = Subscription(sub_id, this);
213  subscriptions.push_back(&p);
214  return p;
215 }
216 template <typename... Types> template <class C>
217 Subscription __attribute__((warn_unused_result)) Signal<Types...>::Subscribe( C* class_ptr, void (C::*member_ptr)(Types...) ) {
218  int sub_id = ++subscription_id_counter;
219  subscribers_with_id[sub_id] = std::bind(member_ptr,class_ptr,std::placeholders::_1);
220  //std::cout << "New SUB registered " << sub_id << std::endl;
221  auto p = Subscription(sub_id, this);
222  subscriptions.push_back(&p);
223  return p;
224 }
225 
228 public:
229  SubscriptionList(const SubscriptionList& other) = delete;
230  std::list<Subscription> list;
231  void ReleaseAll() { list.clear(); }
233  list.push_back(std::move(s));
234  return *this;
235  }
236  SubscriptionList() : list(0){}
237 };
248 public:
250 };
251 
252 
253 
254 } //namespace AlgAudio
255 
256 #endif //CSIGNAL_HPP
friend class Subscription
Definition: Signal.hpp:87
~Signal()
Definition: Signal.hpp:162
friend class SignalBase
Definition: Signal.hpp:62
~Subscription()
Definition: Signal.hpp:52
SubscriptionList subscriptions
Definition: Signal.hpp:249
Definition: Signal.hpp:77
Signal & operator=(const Signal &other)=delete
Definition: Signal.hpp:247
friend class Signal
Definition: Signal.hpp:59
void SubscribeOnce(std::function< void(Types...)> f)
Definition: Signal.hpp:176
SubscriptionList()
Definition: Signal.hpp:236
void ReleaseAll()
Definition: Signal.hpp:231
std::list< Subscription > list
Definition: Signal.hpp:230
bool Happen(Types...t)
Definition: Signal.hpp:187
Subscription Subscribe(std::function< void(Types...)> f) __attribute__((warn_unused_result))
SubscriptionList & operator+=(Subscription &&s)
Definition: Signal.hpp:232
Subscription()
Definition: Signal.hpp:47
Definition: Alertable.hpp:26
void SubscribeForever(std::function< void(Types...)> f)
Definition: Signal.hpp:172
Definition: Signal.hpp:44
bool IsEmpty() const
Definition: Signal.hpp:54
Definition: Signal.hpp:227
void SubscribeOnce(C *class_ptr, void(C::*member_ptr)(Types...))
Definition: Signal.hpp:182
static int subscription_id_counter
Definition: Signal.hpp:86
Definition: Signal.hpp:145
Subscription __attribute__((warn_unused_result)) Signal< Types...>
Definition: Signal.hpp:208
unsigned int Count()
Definition: Signal.hpp:204
std::list< Subscription * > subscriptions
Definition: Signal.hpp:83
Subscription & operator=(Subscription &&other)
Signal()
Definition: Signal.hpp:161