12#ifndef slimsig_signal_base_h
13#define slimsig_signal_base_h
25#include "../connection.h"
28template <
class Handler>
31template <
class R,
class... Args>
38template <
class Handler,
class SignalTraits,
class Allocator>
41template <
class SignalTraits,
class Allocator,
class Handler>
44template <
class Handler,
class SignalTraits,
class Allocator>
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)
51 for(; begin != end; begin++) {
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)
58 return each<Container, Callback>(container, begin, begin +
count, fn);
61template<
class SignalTraits,
class Allocator,
class R,
class... Args>
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>;
76 using slot_id =
typename signal_traits::slot_id_type;
81 static constexpr size_t arity =
sizeof...(Args);
82 struct signal_holder {
86 template <std::
size_t N>
89 static_assert(N < arity,
"error: invalid parameter index.");
90 using type =
typename std::tuple_element<N,std::tuple<Args...>>
::type;
105 pending.reserve(capacity);
120 using std::back_inserter;
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");
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);
144 emit_scope scope { *
this };
146 auto end = pending.size();
147 assert(m_offset <= end);
148 if (end - m_offset == 0)
return;
153 auto&
slot = pending[end];
154 if (
slot)
slot(std::forward<Args&&>(args)...);
157 return emit(std::forward<Args&&>(args)...);
162 auto sid = prepare_connection();
163 emplace(sid, std::move(
slot));
164 return { m_self, sid };
169 struct extended_slot {
172 R operator()(Args&&... args) {
173 return fn(
connection, std::forward<Args>(args)...);
176 return create_connection<extended_slot>(std::move(
slot));
179 template <
class TP,
class Alloc>
183 std::weak_ptr<signal_type> handle;
185 R operator()(Args&&... args) {
186 auto signal = handle.lock();
188 return signal->emit(std::forward<Args>(args)...);
195 return create_connection<signal_slot>(std::move(
signal));
202 R operator() (Args&&... args) {
204 return fn(std::forward<Args>(args)...);
207 return create_connection<fire_once>(std::move(
slot));
214 m_offset = pending.size();
223 if (m_self && last_id > std::numeric_limits<slot_id>::max() / 3) {
224 m_self->signal =
nullptr;
240 return std::min(std::numeric_limits<slot_id>::max(), pending.max_size());
243 return max_size() - last_id;
247 return std::numeric_limits<unsigned>::max();
257 if (m_self) m_self->signal =
nullptr;
259 template <
class FN,
class TP,
class Alloc>
261 template <
class Signal>
269 emit_scope() =
delete;
270 emit_scope(
const emit_scope&) =
delete;
271 emit_scope(emit_scope&&) =
delete;
275 using std::remove_if;
276 auto depth = --
signal.m_depth;
279 auto m_size =
signal.m_size;
280 auto& pending =
signal.pending;
283 if (m_size != pending.size()) {
285 pending.erase(pending.begin(), pending.begin() +
signal.m_offset);
286 pending.erase(remove_if(pending.begin(), pending.end(), &is_disconnected), pending.end());
289 assert(m_size == pending.size());
294 static bool is_disconnected(const_slot_reference slot) {
return !bool(slot); };
296 inline bool connected(slot_id
index)
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) {
303 if (slot != end && slot->m_slot_id ==
index)
return slot->connected();
307 inline void disconnect(slot_id
index)
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) {
314 if (slot != end && slot->m_slot_id ==
index) {
315 if (slot->connected()) {
323 template<
class C,
class T>
325 Connection create_connection( T&& slot )
327 auto sid = prepare_connection();
328 emplace(sid, C { std::move(slot), {m_self, sid} });
329 return Connection { m_self, sid };
333 slot_id prepare_connection()
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");
342 template <
class... SlotArgs>
344 void emplace(SlotArgs&&... args)
346 pending.emplace_back(std::forward<SlotArgs>(args)...);
352 std::shared_ptr<signal_holder> m_self;
355 std::size_t m_offset;
361 template <
class Handler,
class ThreadPolicy,
class Allocator>
size_type remaining_slots() const
typename signal_traits::slot_id_type slot_id
Connection connect(std::shared_ptr< signal< R(Args...), TP, Alloc > > signal)
signal_base(signal_base &&other)
typename signal_traits::return_type return_type
std::function< R(Args...)> callback
std::vector< slot, list_allocator_type > slot_list
size_type max_depth() const
typename slot_list::reference slot_reference
size_type slot_count() const
void swap(signal_base &rhs)
typename std::allocator_traits< Allocator >::template rebind_traits< slot >::allocator_type list_allocator_type
std::vector< slot > pending
return_type operator()(Args... args)
Connection connect_extended(extended_callback slot)
signal_base(size_t capacity, const allocator_type &alloc=allocator_type{})
return_type emit(Args... args)
size_type get_depth() const
Connection connect(callback slot)
Connection connect_once(callback slot)
signal_base & operator=(signal_base &&other)
size_type max_size() const
std::function< R(Connection &conn, Args...)> extended_callback
signal_base & operator=(const signal_base &)=delete
signal_base(const signal_base &)=delete
SignalTraits signal_traits
const allocator_type & get_allocator() const
signal_base(const allocator_type &alloc)
typename slot_list::const_reference const_slot_reference
typename container_type::reference reference
typename container_type::const_reference const_reference
GLuint GLsizei GLsizei GLint GLenum * type
void each_n(const Container &container, typename Container::size_type begin, typename Container::size_type count, const Callback &fn)
void each(const Container &container, typename Container::size_type begin, typename Container::size_type end, const Callback &fn)
void swap(signal< Handler, SignalTraits, Allocator > &lhs, signal< Handler, SignalTraits, Allocator > &rhs)
scoped_connection< connection > make_scoped_connection(connection &&target)
typename std::tuple_element< N, std::tuple< Args... > >::type type
signal_holder(signal_base *p)