/* Copyright 2002 - 2016, The libsigc++ Development Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see .
*/
#ifndef SIGC_SIGNAL_H
#define SIGC_SIGNAL_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace sigc
{
namespace internal
{
/** Special iterator over sigc::internal::signal_impl's slot list that holds extra data.
* This iterators is for use in accumulators. operator*() executes
* the slot. The return value is buffered, so that in an expression
* like @code a = (*i) * (*i); @endcode the slot is executed only once.
*/
template
struct slot_iterator_buf
{
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator_category = std::bidirectional_iterator_tag;
// These are needed just to make this a proper C++ iterator,
// that can be used with standard C++ algorithms.
using value_type = T_result;
using reference = T_result&;
using pointer = T_result*;
using emitter_type = T_emitter;
using slot_type = typename T_emitter::slot_type;
using iterator_type = signal_impl::const_iterator_type;
slot_iterator_buf() : c_(nullptr), invoked_(false) {}
slot_iterator_buf(const iterator_type& i, const emitter_type* c) : i_(i), c_(c), invoked_(false)
{
}
decltype(auto) operator*() const
{
if (!i_->empty() && !i_->blocked() && !invoked_)
{
r_ = (*c_)(static_cast(*i_));
invoked_ = true;
}
return r_;
}
slot_iterator_buf& operator++()
{
++i_;
invoked_ = false;
return *this;
}
slot_iterator_buf operator++(int)
{
slot_iterator_buf tmp(*this);
++i_;
invoked_ = false;
return tmp;
}
slot_iterator_buf& operator--()
{
--i_;
invoked_ = false;
return *this;
}
slot_iterator_buf operator--(int)
{
slot_iterator_buf tmp(*this);
--i_;
invoked_ = false;
return tmp;
}
bool operator==(const slot_iterator_buf& src) const
{
return (!c_ || (i_ == src.i_));
} /* If '!c_' the iterators are empty.
* Unfortunately, empty stl iterators are not equal.
* We are forcing equality so that 'first==last'
* in the accumulator's emit function yields true. */
bool operator!=(const slot_iterator_buf& src) const { return (c_ && (i_ != src.i_)); }
private:
iterator_type i_;
const emitter_type* c_;
mutable T_result r_;
mutable bool invoked_;
};
/** Template specialization of slot_iterator_buf for void return signals.
*/
template
struct slot_iterator_buf
{
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator_category = std::bidirectional_iterator_tag;
using emitter_type = T_emitter;
using slot_type = typename T_emitter::slot_type;
using iterator_type = signal_impl::const_iterator_type;
slot_iterator_buf() : c_(nullptr), invoked_(false) {}
slot_iterator_buf(const iterator_type& i, const emitter_type* c) : i_(i), c_(c), invoked_(false)
{
}
void operator*() const
{
if (!i_->empty() && !i_->blocked() && !invoked_)
{
(*c_)(static_cast(*i_));
invoked_ = true;
}
}
slot_iterator_buf& operator++()
{
++i_;
invoked_ = false;
return *this;
}
slot_iterator_buf operator++(int)
{
slot_iterator_buf tmp(*this);
++i_;
invoked_ = false;
return tmp;
}
slot_iterator_buf& operator--()
{
--i_;
invoked_ = false;
return *this;
}
slot_iterator_buf operator--(int)
{
slot_iterator_buf tmp(*this);
--i_;
invoked_ = false;
return tmp;
}
bool operator==(const slot_iterator_buf& src) const { return i_ == src.i_; }
bool operator!=(const slot_iterator_buf& src) const { return i_ != src.i_; }
private:
iterator_type i_;
const emitter_type* c_;
mutable bool invoked_;
};
/** Temporary slot list used during signal emission.
* Through evolution this class is slightly misnamed. It is now
* an index into the slot_list passed into it. It simply keeps track
* of where the end of this list was at construction, and pretends that's
* the end of your list. This way you may connect during emission without
* inadvertently entering an infinite loop, as well as make other
* modifications to the slot_list at your own risk.
*/
struct temp_slot_list
{
using slot_list = signal_impl::slot_list;
using iterator = signal_impl::iterator_type;
using const_iterator = signal_impl::const_iterator_type;
explicit temp_slot_list(slot_list& slots) : slots_(slots)
{
placeholder = slots_.insert(slots_.end(), slot_base());
}
~temp_slot_list() { slots_.erase(placeholder); }
iterator begin() { return slots_.begin(); }
iterator end() { return placeholder; }
const_iterator begin() const { return slots_.begin(); }
const_iterator end() const { return placeholder; }
private:
slot_list& slots_;
slot_list::iterator placeholder;
};
/** Abstracts signal emission.
* This template implements the emit() function of signal_with_accumulator.
* Template specializations are available to optimize signal
* emission when no accumulator is used, for example when the template
* argument @e T_accumulator is @p void.
*/
template
struct signal_emit
{
using self_type = signal_emit;
using slot_type = slot;
/** Instantiates the class.
* The parameters are stored in member variables. operator()() passes
* the values on to some slot.
*/
explicit signal_emit(type_trait_take_t... a) : a_(a...) {}
/** Invokes a slot using the buffered parameter values.
* @param slot Some slot to invoke.
* @return The slot's return value.
*/
T_return operator()(const slot_type& slot) const { return std::apply(slot, a_); }
/** Executes a list of slots using an accumulator of type @e T_accumulator.
* The arguments are buffered in a temporary instance of signal_emit.
* @param a Arguments to be passed on to the slots.
* @return The accumulated return values of the slot invocations as processed by the accumulator.
*/
static decltype(auto) emit(const std::shared_ptr& impl,
type_trait_take_t... a)
{
using slot_iterator_buf_type = internal::slot_iterator_buf;
T_accumulator accumulator;
if (!impl)
return accumulator(slot_iterator_buf_type(), slot_iterator_buf_type());
signal_impl_holder exec(impl);
const temp_slot_list slots(impl->slots_);
self_type self(a...);
return accumulator(
slot_iterator_buf_type(slots.begin(), &self), slot_iterator_buf_type(slots.end(), &self));
}
private:
std::tuple...> a_;
};
/** Abstracts signal emission.
* This template specialization implements an optimized emit()
* function for the case that no accumulator is used.
*/
template
struct signal_emit
{
private:
using slot_type = slot;
using call_type = typename slot_type::call_type;
public:
/** Executes a list of slots.
* The arguments are passed directly on to the slots.
* The return value of the last slot invoked is returned.
* @param first An iterator pointing to the first slot in the list.
* @param last An iterator pointing to the last slot in the list.
* @param a Arguments to be passed on to the slots.
* @return The return value of the last slot invoked.
*/
static decltype(auto) emit(const std::shared_ptr& impl,
type_trait_take_t... a)
{
if (!impl || impl->slots_.empty())
return T_return();
signal_impl_holder exec(impl);
T_return r_ = T_return();
// Use this scope to make sure that "slots" is destroyed before "exec" is destroyed.
// This avoids a leak on MSVC++ - see http://bugzilla.gnome.org/show_bug.cgi?id=306249
{
const temp_slot_list slots(impl->slots_);
auto it = slots.begin();
for (; it != slots.end(); ++it)
{
if (!it->empty() && !it->blocked())
break;
}
if (it == slots.end())
{
// note that 'T_return r_();' doesn't work => define 'r_' after this line
// and initialize as follows:
return T_return();
}
r_ = (sigc::internal::function_pointer_cast(it->rep_->call_))(it->rep_, a...);
for (++it; it != slots.end(); ++it)
{
if (it->empty() || it->blocked())
continue;
r_ = (sigc::internal::function_pointer_cast(it->rep_->call_))(it->rep_, a...);
}
}
return r_;
}
};
/** Abstracts signal emission.
* This template specialization implements an optimized emit()
* function for the case that no accumulator is used and the
* return type is @p void.
*/
template
struct signal_emit
{
private:
using slot_type = slot;
using call_type = typename slot_type::call_type;
public:
/** Executes a list of slots using an accumulator of type @e T_accumulator.
* The arguments are passed directly on to the slots.
* @param a Arguments to be passed on to the slots.
*/
static decltype(auto) emit(const std::shared_ptr& impl,
type_trait_take_t... a)
{
if (!impl || impl->slots_.empty())
return;
signal_impl_holder exec(impl);
const temp_slot_list slots(impl->slots_);
for (const auto& slot : slots)
{
if (slot.empty() || slot.blocked())
continue;
(sigc::internal::function_pointer_cast(slot.rep_->call_))(
slot.rep_, std::forward>(a)...);
}
}
};
} /* namespace internal */
/** Signal declaration.
* %signal_with_accumulator can be used to connect() slots that are invoked
* during subsequent calls to emit(). Any functor or slot
* can be passed into connect() or connect_first(). It is converted into a slot
* implicitly.
*
* If you want to connect one signal to another, use make_slot()
* to retrieve a functor that emits the signal when invoked.
*
* Be careful if you directly pass one signal into the connect() or
* connect_first() method of another: a shallow copy of the signal is made and
* the signal's slots are not disconnected until both the signal
* and its clone are destroyed, which is probably not what you want!
*
* The following template arguments are used:
* - @e T_return The desired return type for the emit() function (may be overridden by the
* accumulator).
* - @e T_arg Argument types used in the definition of emit().
* - @e T_accumulator The accumulator type used for emission. The default
* @p void means that no accumulator should be used, for example if signal
* emission returns the return value of the last slot invoked.
*
* @ingroup signal
*/
template
class signal_with_accumulator : public signal_base
{
public:
using slot_type = slot;
/** Add a slot at the end of the list of slots.
* Any functor or slot may be passed into %connect().
* It will be converted into a slot implicitly.
* The returned connection may be stored for disconnection
* of the slot at some later point. It stays valid until
* the slot is disconnected from the signal.
* std::function<> and C++11 lambda expressions are functors.
* These are examples of functors that can be connected to a signal.
*
* %std::bind() creates a functor, but this functor typically has an
* %operator()() which is a variadic template.
* Our functor_trait can't deduce the result type
* of such a functor. If you first assign the return value of %std::bind()
* to a std::function, you can connect the std::function to a signal.
*
* @param slot_ The slot to add to the list of slots.
* @return A connection.
*/
connection connect(const slot_type& slot_)
{
auto iter = signal_base::connect(slot_);
auto& slot_base = *iter;
return connection(slot_base);
}
/** Add a slot at the end of the list of slots.
* @see connect(const slot_type& slot_).
*
* @newin{2,8}
*/
connection connect(slot_type&& slot_)
{
auto iter = signal_base::connect(std::move(slot_));
auto& slot_base = *iter;
return connection(slot_base);
}
/** Add a slot at the beginning of the list of slots.
* Any functor or slot may be passed into %connect_first().
* It will be converted into a slot implicitly.
* The returned connection may be stored for disconnection
* of the slot at some later point. It stays valid until
* the slot is disconnected from the signal.
* std::function<> and C++11 lambda expressions are functors.
* These are examples of functors that can be connected to a signal.
*
* %std::bind() creates a functor, but this functor typically has an
* %operator()() which is a variadic template.
* Our functor_trait can't deduce the result type
* of such a functor. If you first assign the return value of %std::bind()
* to a std::function, you can connect the std::function to a signal.
*
* @param slot_ The slot to add to the list of slots.
* @return A connection.
*
* @newin{3,6}
*/
connection connect_first(const slot_type& slot_)
{
auto iter = signal_base::connect_first(slot_);
auto& slot_base = *iter;
return connection(slot_base);
}
/** Add a slot at the beginning of the list of slots.
* @see connect_first(const slot_type& slot_).
*
* @newin{3,6}
*/
connection connect_first(slot_type&& slot_)
{
auto iter = signal_base::connect_first(std::move(slot_));
auto& slot_base = *iter;
return connection(slot_base);
}
/** Triggers the emission of the signal.
* During signal emission all slots that have been connected
* to the signal are invoked unless they are manually set into
* a blocking state. The parameters are passed on to the slots.
* If @e T_accumulated is not @p void, an accumulator of this type
* is used to process the return values of the slot invocations.
* Otherwise, the return value of the last slot invoked is returned.
* @param a Arguments to be passed on to the slots.
* @return The accumulated return values of the slot invocations.
*/
decltype(auto) emit(type_trait_take_t... a) const
{
using emitter_type = internal::signal_emit;
return emitter_type::emit(impl_, std::forward>(a)...);
}
/** Triggers the emission of the signal (see emit()). */
decltype(auto) operator()(type_trait_take_t... a) const
{
return emit(std::forward>(a)...);
}
/** Creates a functor that calls emit() on this signal.
*
* @note %sigc::signal does not derive from sigc::trackable.
* If you connect the returned functor that calls %emit() on signal1,
* to another signal (signal2) and then delete signal1, you must manually
* disconnect signal1 from signal2 before you delete signal1.
* Alternatively, make a slot of a sigc::trackable_signal.
*
* @code
* sigc::mem_fun(mysignal, &sigc::signal_with_accumulator::emit)
* @endcode
* yields the same result.
* @return A functor that calls emit() on this signal.
*/
decltype(auto) make_slot() const
{
// TODO: Instead use std::invoke_result<> on the static emitter_type::emit()
using result_type =
typename internal::member_method_result::type;
return bound_mem_functor...)
const,
type_trait_take_t...>(*this, &signal_with_accumulator::emit);
}
signal_with_accumulator() = default;
signal_with_accumulator(const signal_with_accumulator& src) : signal_base(src) {}
signal_with_accumulator(signal_with_accumulator&& src) : signal_base(std::move(src)) {}
signal_with_accumulator& operator=(const signal_with_accumulator& src)
{
signal_base::operator=(src);
return *this;
}
signal_with_accumulator& operator=(signal_with_accumulator&& src)
{
signal_base::operator=(std::move(src));
return *this;
}
};
/** signal can be used to connect() slots that are invoked
* during subsequent calls to emit(). Any functor or slot
* can be passed into connect() or connect_first(). It is converted into a slot
* implicitly.
*
* If you want to connect one signal to another, use make_slot()
* to retrieve a functor that emits the signal when invoked.
*
* Be careful if you directly pass one signal into the connect() or
* connect_first() method of another: a shallow copy of the signal is made and
* the signal's slots are not disconnected until both the signal
* and its clone are destroyed, which is probably not what you want!
*
* The template arguments determine the function signature of
* the emit() function:
* - @e T_return The desired return type of the emit() function.
* - @e T_arg Argument types used in
* the definition of emit().
*
* For instance, to declare a signal whose connected slot returns void and takes
* two parameters of bool and int:
* @code
* sigc::signal some_signal;
* @endcode
*
* To specify an accumulator type the nested class signal::accumulated can be used.
*
* @par Example:
* @code
* void foo(int) {}
* sigc::signal sig;
* sig.connect(sigc::ptr_fun(&foo));
* sig.emit(19);
* @endcode
*
* @ingroup signal
*/
#ifndef DOXYGEN_SHOULD_SKIP_THIS
template
class signal final {
static_assert(sizeof...(T_arg) < 0, "The signal syntax has been removed. Use the signal syntax.");
};
#endif // DOXYGEN_SHOULD_SKIP_THIS
template
class signal : public signal_with_accumulator
{
public:
using accumulator_type = void;
/** Like @ref sigc::signal "sigc::signal" but the additional
* template parameter @e T_accumulator defines the accumulator type that should be used.
*
* An accumulator is a functor that uses a pair of special iterators
* to step through a list of slots and calculate a return value
* from the results of the slot invocations. The iterators' operator*()
* executes the slot. The return value is buffered, so that in an expression
* like @code a = (*i) * (*i); @endcode the slot is executed only once.
*
* @par Example 1:
* This accumulator calculates the arithmetic mean value:
* @code
* struct arithmetic_mean_accumulator
* {
* template
* double operator()(T_iterator first, T_iterator last) const
* {
* double value_ = 0;
* int n_ = 0;
* for (; first != last; ++first, ++n_)
* value_ += *first;
* return value_ / n_;
* }
* };
* @endcode
*
* @par Example 2:
* This accumulator stops signal emission when a slot returns zero:
* @code
* struct interruptable_accumulator
* {
* template
* bool operator()(T_iterator first, T_iterator last) const
* {
* for (; first != last; ++first, ++n_)
* if (!*first) return false;
* return true;
* }
* };
* @endcode
*
* @ingroup signal
*/
template
class accumulated : public signal_with_accumulator
{
public:
accumulated() = default;
accumulated(const accumulated& src)
: signal_with_accumulator(src)
{
}
};
signal() = default;
signal(const signal& src) : signal_with_accumulator(src) {}
signal(signal&& src)
: signal_with_accumulator(std::move(src))
{
}
signal& operator=(const signal& src)
{
signal_with_accumulator::operator=(src);
return *this;
}
signal& operator=(signal&& src)
{
signal_with_accumulator::operator=(std::move(src));
return *this;
}
};
// TODO: When we can break ABI, let signal_base derive from trackable, as in sigc++2,
// and delete trackable_signal_with_accumulator and trackable_signal.
// https://github.com/libsigcplusplus/libsigcplusplus/issues/80
/** Signal declaration.
* %trackable_signal_with_accumulator can be used to connect() slots that are invoked
* during subsequent calls to emit(). Any functor or slot
* can be passed into connect() or connect_first(). It is converted into a slot
* implicitly.
*
* If you want to connect one signal to another, use make_slot()
* to retrieve a functor that emits the signal when invoked.
*
* Be careful if you directly pass one signal into the connect() or
* connect_first() method of another: a shallow copy of the signal is made and
* the signal's slots are not disconnected until both the signal
* and its clone are destroyed, which is probably not what you want!
*
* The following template arguments are used:
* - @e T_return The desired return type for the emit() function (may be overridden by the
* accumulator).
* - @e T_arg Argument types used in the definition of emit().
* - @e T_accumulator The accumulator type used for emission. The default
* @p void means that no accumulator should be used, for example if signal
* emission returns the return value of the last slot invoked.
*
* @newin{3,4}
*
* @ingroup signal
*/
template
class trackable_signal_with_accumulator
: public signal_base
, public trackable
{
public:
using slot_type = slot;
/** Add a slot at the end of the list of slots.
* Any functor or slot may be passed into %connect().
* It will be converted into a slot implicitly.
* The returned connection may be stored for disconnection
* of the slot at some later point. It stays valid until
* the slot is disconnected from the signal.
* std::function<> and C++11 lambda expressions are functors.
* These are examples of functors that can be connected to a signal.
*
* %std::bind() creates a functor, but this functor typically has an
* %operator()() which is a variadic template.
* Our functor_trait can't deduce the result type
* of such a functor. If you first assign the return value of %std::bind()
* to a std::function, you can connect the std::function to a signal.
*
* @param slot_ The slot to add to the list of slots.
* @return A connection.
*/
connection connect(const slot_type& slot_)
{
auto iter = signal_base::connect(slot_);
auto& slot_base = *iter;
return connection(slot_base);
}
/** Add a slot at the end of the list of slots.
* @see connect(const slot_type& slot_).
*/
connection connect(slot_type&& slot_)
{
auto iter = signal_base::connect(std::move(slot_));
auto& slot_base = *iter;
return connection(slot_base);
}
/** Add a slot at the beginning of the list of slots.
* Any functor or slot may be passed into %connect_first().
* It will be converted into a slot implicitly.
* The returned connection may be stored for disconnection
* of the slot at some later point. It stays valid until
* the slot is disconnected from the signal.
* std::function<> and C++11 lambda expressions are functors.
* These are examples of functors that can be connected to a signal.
*
* %std::bind() creates a functor, but this functor typically has an
* %operator()() which is a variadic template.
* Our functor_trait can't deduce the result type
* of such a functor. If you first assign the return value of %std::bind()
* to a std::function, you can connect the std::function to a signal.
*
* @param slot_ The slot to add to the list of slots.
* @return A connection.
*
* @newin{3,6}
*/
connection connect_first(const slot_type& slot_)
{
auto iter = signal_base::connect_first(slot_);
auto& slot_base = *iter;
return connection(slot_base);
}
/** Add a slot at the beginning of the list of slots.
* @see connect_first(const slot_type& slot_).
*
* @newin{3,6}
*/
connection connect_first(slot_type&& slot_)
{
auto iter = signal_base::connect_first(std::move(slot_));
auto& slot_base = *iter;
return connection(slot_base);
}
/** Triggers the emission of the signal.
* During signal emission all slots that have been connected
* to the signal are invoked unless they are manually set into
* a blocking state. The parameters are passed on to the slots.
* If @e T_accumulated is not @p void, an accumulator of this type
* is used to process the return values of the slot invocations.
* Otherwise, the return value of the last slot invoked is returned.
* @param a Arguments to be passed on to the slots.
* @return The accumulated return values of the slot invocations.
*/
decltype(auto) emit(type_trait_take_t... a) const
{
using emitter_type = internal::signal_emit;
return emitter_type::emit(impl_, std::forward>(a)...);
}
/** Triggers the emission of the signal (see emit()). */
decltype(auto) operator()(type_trait_take_t... a) const
{
return emit(std::forward>(a)...);
}
/** Creates a functor that calls emit() on this signal.
*
* @code
* sigc::mem_fun(mysignal, &sigc::trackable_signal_with_accumulator::emit)
* @endcode
* yields the same result.
* @return A functor that calls emit() on this signal.
*/
decltype(auto) make_slot() const
{
// TODO: Instead use std::invoke_result<> on the static emitter_type::emit()
using result_type = typename internal::member_method_result<
decltype(&trackable_signal_with_accumulator::emit)>::type;
return bound_mem_functor...) const,
type_trait_take_t...>(*this, &trackable_signal_with_accumulator::emit);
}
trackable_signal_with_accumulator() = default;
trackable_signal_with_accumulator(const trackable_signal_with_accumulator& src)
: signal_base(src), trackable(src)
{
}
trackable_signal_with_accumulator(trackable_signal_with_accumulator&& src)
: signal_base(std::move(src)), trackable(std::move(src))
{
}
trackable_signal_with_accumulator& operator=(const trackable_signal_with_accumulator& src)
{
signal_base::operator=(src);
// Don't call trackable::operator=(src).
// It calls notify_callbacks(). This signal is not destroyed.
return *this;
}
trackable_signal_with_accumulator& operator=(trackable_signal_with_accumulator&& src)
{
signal_base::operator=(std::move(src));
if (src.impl_ != impl_)
src.notify_callbacks();
// Don't call trackable::operator=(std::move(src)).
// It calls notify_callbacks(). This signal is not destroyed.
return *this;
}
};
/** %trackable_signal can be used to connect() slots that are invoked
* during subsequent calls to emit(). Any functor or slot
* can be passed into connect() or connect_first(). It is converted into a slot
* implicitly.
*
* If you want to connect one signal to another, use make_slot()
* to retrieve a functor that emits the signal when invoked.
*
* Be careful if you directly pass one signal into the connect() or
* connect_first() method of another: a shallow copy of the signal is made and
* the signal's slots are not disconnected until both the signal
* and its clone are destroyed, which is probably not what you want!
*
* The template arguments determine the function signature of
* the emit() function:
* - @e T_return The desired return type of the emit() function.
* - @e T_arg Argument types used in
* the definition of emit().
*
* For instance, to declare a %trackable_signal whose connected slot returns void and takes
* two parameters of bool and int:
* @code
* sigc::trackable_signal some_signal;
* @endcode
*
* To specify an accumulator type the nested class trackable_signal::accumulated can be used.
*
* @par Example:
* @code
* void foo(int) {}
* sigc::trackable_signal sig;
* sig.connect(sigc::ptr_fun(&foo));
* sig.emit(19);
* @endcode
*
* @newin{3,4}
*
* @ingroup signal
*/
#ifndef DOXYGEN_SHOULD_SKIP_THIS
template
class trackable_signal;
#endif // DOXYGEN_SHOULD_SKIP_THIS
template
class trackable_signal
: public trackable_signal_with_accumulator
{
public:
using accumulator_type = void;
/** Like @ref sigc::trackable_signal "sigc::trackable_signal"
* but the additional template parameter @e T_accumulator defines the accumulator
* type that should be used.
*
* An accumulator is a functor that uses a pair of special iterators
* to step through a list of slots and calculate a return value
* from the results of the slot invocations. The iterators' operator*()
* executes the slot. The return value is buffered, so that in an expression
* like @code a = (*i) * (*i); @endcode the slot is executed only once.
*
* @par Example 1:
* This accumulator calculates the arithmetic mean value:
* @code
* struct arithmetic_mean_accumulator
* {
* template
* double operator()(T_iterator first, T_iterator last) const
* {
* double value_ = 0;
* int n_ = 0;
* for (; first != last; ++first, ++n_)
* value_ += *first;
* return value_ / n_;
* }
* };
* @endcode
*
* @par Example 2:
* This accumulator stops signal emission when a slot returns zero:
* @code
* struct interruptable_accumulator
* {
* template
* bool operator()(T_iterator first, T_iterator last) const
* {
* for (; first != last; ++first, ++n_)
* if (!*first) return false;
* return true;
* }
* };
* @endcode
*
* @newin{3,4}
*
* @ingroup signal
*/
template
class accumulated : public trackable_signal_with_accumulator
{
public:
accumulated() = default;
accumulated(const accumulated& src)
: trackable_signal_with_accumulator(src)
{
}
};
trackable_signal() = default;
trackable_signal(const trackable_signal& src)
: trackable_signal_with_accumulator(src)
{
}
trackable_signal(trackable_signal&& src)
: trackable_signal_with_accumulator(std::move(src))
{
}
trackable_signal& operator=(const trackable_signal& src)
{
trackable_signal_with_accumulator::operator=(src);
return *this;
}
trackable_signal& operator=(trackable_signal&& src)
{
trackable_signal_with_accumulator::operator=(
std::move(src));
return *this;
}
};
} /* namespace sigc */
#endif /* SIGC_SIGNAL_H */