CFx SDK Documentation 2024 SP0
Loading...
Searching...
No Matches
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"
26namespace slimsig {
27
28template <class Handler>
30
31template <class R, class... Args>
32struct signal_traits<R(Args...)> {
33 using return_type = R;
34 using slot_id_type = std::size_t;
35 using depth_type = unsigned;
36};
37
38template <class Handler, class SignalTraits, class Allocator>
39class signal;
40
41template <class SignalTraits, class Allocator, class Handler>
42class signal_base;
43
44template <class Handler, class SignalTraits, class Allocator>
46
47namespace 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}
61template<class SignalTraits, class Allocator, class R, class... Args>
62class signal_base<SignalTraits, Allocator, R(Args...)>
63{
64 struct emit_scope;
65public:
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;
80public:
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
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;
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);
135 if (std::allocator_traits<allocator_type>::propagate_on_container_swap::value)
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
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
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
247 return std::numeric_limits<unsigned>::max();
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>
263private:
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 };
349protected:
350 std::vector<slot> pending;
351private:
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
Connection connect(std::shared_ptr< signal< R(Args...), TP, Alloc > > signal)
Definition: signal_base.h:180
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
std::function< R(Connection &conn, Args...)> extended_callback
Definition: signal_base.h:75
signal_base & operator=(const signal_base &)=delete
typename slot_list::const_reference const_slot_reference
Definition: signal_base.h:78
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
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, SignalTraits, Allocator > &lhs, signal< Handler, SignalTraits, Allocator > &rhs)
scoped_connection< connection > make_scoped_connection(connection &&target)
Definition: connection.h:117
#define _FORCE_INLINE
Definition: slot.h:23
typename std::tuple_element< N, std::tuple< Args... > >::type type
Definition: signal_base.h:90