CFx SDK Documentation  2023 SP0
signal_base.h
Go to the documentation of this file.
1 //
2 // signal_base.h
3 // slimsig
4 //
5 // Created by Christopher Tarquini on 4/21/14.
6 //
7 //
8 
9 
10 // 9/21/18 replaced signal_base::connection with signal_base::Connection to support building on G++ without -fpermissive. Graebert
11 
12 #ifndef slimsig_signal_base_h
13 #define slimsig_signal_base_h
14 
15 #include <functional>
16 #include <iterator>
17 #include <memory>
18 #include <vector>
19 #include <algorithm>
20 #include <atomic>
21 #include <mutex>
22 #include <cmath>
23 #include <cassert>
24 
25 #include "../connection.h"
26 namespace slimsig {
27 
28 template <class Handler>
30 
31 template <class R, class... Args>
32 struct signal_traits<R(Args...)> {
33  using return_type = R;
34  using slot_id_type = std::size_t;
35  using depth_type = unsigned;
36 };
37 
38 template <class Handler, class SignalTraits, class Allocator>
39 class signal;
40 
41 template <class SignalTraits, class Allocator, class Handler>
42 class signal_base;
43 
44 template <class Handler, class SignalTraits, class Allocator>
46 
47 namespace detail {
48  template <class Container, class Callback>
49  inline void each(const Container& container, typename Container::size_type begin, typename Container::size_type end, const Callback& fn)
50  {
51  for(; begin != end; begin++) {
52  fn(container[begin]);
53  }
54  }
55  template <class Container, class Callback>
56  inline void each_n(const Container& container, typename Container::size_type begin, typename Container::size_type count, const Callback& fn)
57  {
58  return each<Container, Callback>(container, begin, begin + count, fn);
59  }
60 }
61 template<class SignalTraits, class Allocator, class R, class... Args>
62 class signal_base<SignalTraits, Allocator, R(Args...)>
63 {
64  struct emit_scope;
65 public:
66  using signal_traits = SignalTraits;
67  using return_type = typename signal_traits::return_type;
68  using callback = std::function<R(Args...)>;
69  using allocator_type = Allocator;
70  using slot = basic_slot<R(Args...), typename signal_traits::slot_id_type>;
71  using list_allocator_type = typename std::allocator_traits<Allocator>::template rebind_traits<slot>::allocator_type;
72  using slot_list = std::vector<slot, list_allocator_type>;
73 
75  using extended_callback = std::function<R(Connection& conn, Args...)>;
76  using slot_id = typename signal_traits::slot_id_type;
79  using size_type = std::size_t;
80 public:
81  static constexpr size_t arity = sizeof...(Args);
82  struct signal_holder {
85  };
86  template <std::size_t N>
87  struct argument
88  {
89  static_assert(N < arity, "error: invalid parameter index.");
90  using type = typename std::tuple_element<N,std::tuple<Args...>>::type;
91  };
92 
93  // allocator constructor
94  signal_base(const allocator_type& alloc) :
95  pending(alloc),
96  m_self(nullptr),
97  last_id(),
98  m_size(0),
99  m_offset(0),
100  allocator(alloc),
101  m_depth(0){};
102 
103  signal_base(size_t capacity, const allocator_type& alloc = allocator_type{})
104  : signal_base(alloc) {
105  pending.reserve(capacity);
106  };
107 
109  this->swap(other);
110  }
112  this->swap(other);
113  return *this;
114  }
115  // no copy
116  signal_base(const signal_base&) = delete;
117  signal_base& operator=(const signal_base&) = delete;
118  void swap(signal_base& rhs) {
119  using std::swap;
120  using std::back_inserter;
121  using std::copy_if;
122  using std::not1;
123  if (this != &rhs) {
124  #if !defined(NDEBUG) || (defined(SLIMSIG_SWAP_GUARD) && SLIMSIG_SWAP_GUARD)
125  if (is_running() || rhs.is_running())
126  throw new std::logic_error("Signals can not be swapped or moved while emitting");
127  #endif
128  swap(pending, rhs.pending);
129  swap(m_self, rhs.m_self);
130  if (m_self) m_self->signal = this;
131  if (rhs.m_self) rhs.m_self->signal = &rhs;
132  swap(last_id, rhs.last_id);
133  swap(m_size, rhs.m_size);
134  swap(m_offset, rhs.m_offset);
136  swap(allocator, rhs.allocator);
137  swap(m_depth, rhs.m_depth);
138  }
139  }
140 
141  return_type emit(Args... args) {
142  using detail::each;
143  // scope guard
144  emit_scope scope { *this };
145 
146  auto end = pending.size();
147  assert(m_offset <= end);
148  if (end - m_offset == 0) return;
149  assert(end > 0);
150  each(pending, m_offset, --end, [&] (const_slot_reference slot) {
151  if (slot) slot(args...);
152  });
153  auto& slot = pending[end];
154  if (slot) slot(std::forward<Args&&>(args)...);
155  }
156  return_type operator()(Args... args) {
157  return emit(std::forward<Args&&>(args)...);
158  }
159 
161  {
162  auto sid = prepare_connection();
163  emplace(sid, std::move(slot));
164  return { m_self, sid };
165  };
166 
168  {
169  struct extended_slot {
170  callback fn;
172  R operator()(Args&&... args) {
173  return fn(connection, std::forward<Args>(args)...);
174  }
175  };
176  return create_connection<extended_slot>(std::move(slot));
177  }
178 
179  template <class TP, class Alloc>
180  inline Connection connect(std::shared_ptr<signal<R(Args...), TP, Alloc>> signal) {
181  using signal_type = slimsig::signal<R(Args...), TP, Alloc>;
182  struct signal_slot {
183  std::weak_ptr<signal_type> handle;
185  R operator()(Args&&... args) {
186  auto signal = handle.lock();
187  if (signal) {
188  return signal->emit(std::forward<Args>(args)...);
189  } else {
191  return;
192  }
193  }
194  };
195  return create_connection<signal_slot>(std::move(signal));
196  }
197 
199  struct fire_once {
200  callback fn;
201  Connection conn;
202  R operator() (Args&&... args) {
203  auto scoped_connection = make_scoped_connection(std::move(conn));
204  return fn(std::forward<Args>(args)...);
205  }
206  };
207  return create_connection<fire_once>(std::move(slot));
208  }
209 
210 
211  void disconnect_all() {
212  using std::for_each;
213  if (is_running()) {
214  m_offset = pending.size();
215  m_size = 0;
216  } else {
217  pending.clear();
218  m_size = 0;
219  }
220  // if we've used up a lot of id's we can take advantage of the fact that
221  // all connections are based on the current signal_holder pointer
222  // and reset our last_id to 0 without causing trouble with old connections
223  if (m_self && last_id > std::numeric_limits<slot_id>::max() / 3) {
224  m_self->signal = nullptr;
225  m_self.reset();
226  last_id = slot_id();
227  }
228  }
229 
230  const allocator_type& get_allocator() const {
231  return allocator;
232  }
233  inline bool empty() const {
234  return m_size == 0;
235  }
236  inline size_type slot_count() const {
237  return m_size;
238  }
239  inline size_type max_size() const {
240  return std::min(std::numeric_limits<slot_id>::max(), pending.max_size());
241  }
242  inline size_type remaining_slots() const {
243  return max_size() - last_id;
244  }
245 
248  }
250  return m_depth;
251  }
252  bool is_running() const {
253  return m_depth > 0;
254  }
255 
257  if (m_self) m_self->signal = nullptr;
258  }
259  template <class FN, class TP, class Alloc>
260  friend class signal;
261  template <class Signal>
262  friend class slimsig::connection;
263 private:
264  struct emit_scope{
266  emit_scope(signal_base& context) : signal(context) {
267  signal.m_depth++;
268  }
269  emit_scope() = delete;
270  emit_scope(const emit_scope&) = delete;
271  emit_scope(emit_scope&&) = delete;
272  ~emit_scope() {
273  using std::move;
274  using std::for_each;
275  using std::remove_if;
276  auto depth = --signal.m_depth;
277  // if we completed iteration (depth = 0) collapse all the levels into the head list
278  if (depth == 0) {
279  auto m_size = signal.m_size;
280  auto& pending = signal.pending;
281  // if the size is different than the expected size
282  // we have some slots we need to remove
283  if (m_size != pending.size()) {
284  // remove slots from disconnect_all
285  pending.erase(pending.begin(), pending.begin() + signal.m_offset);
286  pending.erase(remove_if(pending.begin(), pending.end(), &is_disconnected), pending.end());
287  }
288  signal.m_offset = 0;
289  assert(m_size == pending.size());
290  }
291  }
292  };
293 
294  static bool is_disconnected(const_slot_reference slot) { return !bool(slot); };
295 
296  inline bool connected(slot_id index)
297  {
298  using std::lower_bound;
299  auto end = pending.cend();
300  auto slot = lower_bound(pending.cbegin() + m_offset, end, index, [] (const_slot_reference slot, const slot_id& index) {
301  return slot < index;
302  });
303  if (slot != end && slot->m_slot_id == index) return slot->connected();
304  return false;
305  };
306 
307  inline void disconnect(slot_id index)
308  {
309  using std::lower_bound;
310  auto end = pending.end();
311  auto slot = lower_bound(pending.begin() + m_offset, end, index, [] (slot_reference slot, const slot_id& index) {
312  return slot < index;
313  });
314  if (slot != end && slot->m_slot_id == index) {
315  if (slot->connected()) {
316  slot->disconnect();
317  m_size -= 1;
318  }
319 
320  }
321  };
322 
323  template<class C, class T>
325  Connection create_connection( T&& slot )
326  {
327  auto sid = prepare_connection();
328  emplace(sid, C { std::move(slot), {m_self, sid} });
329  return Connection { m_self, sid };
330  };
331 
333  slot_id prepare_connection()
334  {
335  // lazy initialize to put off heap allocations if the user
336  // has not connected a slot
337  if (!m_self) m_self = std::make_shared<signal_holder>(this);
338  assert((last_id < std::numeric_limits<slot_id>::max() - 1) && "All available slot ids for this signal have been exhausted. This may be a sign you are misusing signals");
339  return last_id++;
340  };
341 
342  template <class... SlotArgs>
344  void emplace(SlotArgs&&... args)
345  {
346  pending.emplace_back(std::forward<SlotArgs>(args)...);
347  m_size++;
348  };
349 protected:
350  std::vector<slot> pending;
351 private:
352  std::shared_ptr<signal_holder> m_self;
353  slot_id last_id;
354  std::size_t m_size;
355  std::size_t m_offset;
356  allocator_type allocator;
357  unsigned m_depth;
358 
359 };
360 
361  template <class Handler, class ThreadPolicy, class Allocator>
363  using std::swap;
364  lhs.swap(rhs);
365  }
366 }
367 
368 #endif
typename signal_traits::slot_id_type slot_id
Definition: signal_base.h:76
typename signal_traits::return_type return_type
Definition: signal_base.h:67
std::vector< slot, list_allocator_type > slot_list
Definition: signal_base.h:72
typename std::allocator_traits< Allocator >::template rebind_traits< slot >::allocator_type list_allocator_type
Definition: signal_base.h:71
Connection connect_extended(extended_callback slot)
Definition: signal_base.h:167
signal_base(size_t capacity, const allocator_type &alloc=allocator_type{})
Definition: signal_base.h:103
signal_base & operator=(const signal_base &)=delete
std::function< R(Connection &conn, Args...)> extended_callback
Definition: signal_base.h:75
typename slot_list::const_reference const_slot_reference
Definition: signal_base.h:78
Connection connect(std::shared_ptr< signal< R(Args...), TP, Alloc >> signal)
Definition: signal_base.h:180
typename container_type::reference reference
Definition: slot.h:167
typename container_type::const_reference const_reference
Definition: slot.h:168
GLuint index
Definition: gles2_ext.h:265
GLsizei GLsizei * count
Definition: gles2_ext.h:276
GLuint GLsizei GLsizei GLint GLenum * type
Definition: gles2_ext.h:274
GLsizei const GLfloat * value
Definition: gles2_ext.h:302
const T & min(const T &x, const T &y)
const T & max(const T &x, const T &y)
void each_n(const Container &container, typename Container::size_type begin, typename Container::size_type count, const Callback &fn)
Definition: signal_base.h:56
void each(const Container &container, typename Container::size_type begin, typename Container::size_type end, const Callback &fn)
Definition: signal_base.h:49
void swap(signal< Handler, ThreadPolicy, Allocator > &lhs, signal< Handler, ThreadPolicy, Allocator > &rhs)
Definition: signal_base.h:362
scoped_connection< connection > make_scoped_connection(connection &&target)
Definition: connection.h:117
void swap(signal< Handler, SignalTraits, Allocator > &lhs, signal< Handler, SignalTraits, Allocator > &rhs)
#define _FORCE_INLINE
Definition: slot.h:23
typename std::tuple_element< N, std::tuple< Args... > >::type type
Definition: signal_base.h:90