/* 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_BASE_H
#define SIGC_SIGNAL_BASE_H
#include
#include
#include //For std::shared_ptr<>
#include
#include
#include
#include
/** The libsigc++ namespace.
*/
namespace sigc
{
namespace internal
{
/** Implementation of the signal interface.
* signal_impl manages a list of slots. When a slot becomes invalid (because some
* referred object dies), notify_self_and_iter_of_invalidated_slot() is executed.
* notify_self_and_iter_of_invalidated_slot() either calls slots_.erase() directly
* or defers the execution of erase() to sweep() when the signal is being emitted.
* sweep() removes all invalid slots from the list.
*/
struct SIGC_API signal_impl : public std::enable_shared_from_this
{
using size_type = std::size_t;
using slot_list = std::list;
using iterator_type = slot_list::iterator;
using const_iterator_type = slot_list::const_iterator;
signal_impl();
~signal_impl();
signal_impl(const signal_impl& src) = delete;
signal_impl& operator=(const signal_impl& src) = delete;
signal_impl(signal_impl&& src) = delete;
signal_impl& operator=(signal_impl&& src) = delete;
// only MSVC needs this to guarantee that all new/delete are executed from the DLL module
#ifdef SIGC_NEW_DELETE_IN_LIBRARY_ONLY
void* operator new(size_t size_);
void operator delete(void* p);
#endif
/// Increments the reference and execution counter.
inline void reference_exec() noexcept { ++exec_count_; }
/** Decrements the reference and execution counter.
* Invokes sweep() if the execution counter reaches zero and the
* removal of one or more slots has been deferred.
*/
inline void unreference_exec()
{
if (!(--exec_count_) && deferred_)
sweep();
}
/** Returns whether the list of slots is empty.
* @return @p true if the list of slots is empty.
*/
inline bool empty() const noexcept { return slots_.empty(); }
/// Empties the list of slots.
void clear();
/** Returns the number of slots in the list.
* @return The number of slots in the list.
*/
size_type size() const noexcept;
/** Returns whether all slots in the list are blocked.
* @return @p true if all slots are blocked or the list is empty.
*
* @newin{2,4}
*/
bool blocked() const noexcept;
/** Sets the blocking state of all slots in the list.
* If @e should_block is @p true then the blocking state is set.
* Subsequent emissions of the signal don't invoke the functors
* contained in the slots until block() with @e should_block = @p false is called.
* sigc::slot_base::block() and sigc::slot_base::unblock() can change the
* blocking state of individual slots.
* @param should_block Indicates whether the blocking state should be set or unset.
*
* @newin{2,4}
*/
void block(bool should_block = true) noexcept;
/** Adds a slot at the end of the list of slots.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*/
iterator_type connect(const slot_base& slot_);
/** Adds a slot at the end of the list of slots.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*
* @newin{2,8}
*/
iterator_type connect(slot_base&& slot_);
/** Adds a slot at the beginning of the list of slots.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*
* @newin{3,6}
*/
iterator_type connect_first(const slot_base& slot_);
/** Adds a slot at the beginning of the list of slots.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*
* @newin{3,6}
*/
iterator_type connect_first(slot_base&& slot_);
/** Adds a slot at the given position into the list of slots.
* @param i An iterator indicating the position where @p slot_ should be inserted.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*/
iterator_type insert(iterator_type i, const slot_base& slot_);
/** Adds a slot at the given position into the list of slots.
* @param i An iterator indicating the position where @p slot_ should be inserted.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*
* @newin{2,8}
*/
iterator_type insert(iterator_type i, slot_base&& slot_);
/// Removes invalid slots from the list of slots.
void sweep();
private:
/** Callback that is executed when some slot becomes invalid.
* This callback is registered in every slot when inserted into
* the list of slots. It is executed when a slot becomes invalid
* because of some referred object being destroyed.
* It either calls slots_.erase() directly or defers the execution of
* erase() to sweep() when the signal is being emitted.
* @param d A local structure, created in insert().
*/
static void notify_self_and_iter_of_invalidated_slot(notifiable* d);
void add_notification_to_iter(const signal_impl::iterator_type& iter);
public:
/// The list of slots.
std::list slots_;
private:
/** Execution counter.
* Indicates whether the signal is being emitted.
*/
short exec_count_;
/// Indicates whether the execution of sweep() is being deferred.
bool deferred_;
};
struct SIGC_API signal_impl_exec_holder
{
/** Increments the execution counter of the parent sigc::signal_impl object.
* @param sig The parent sigc::signal_impl object.
*/
inline explicit signal_impl_exec_holder(signal_impl* sig) noexcept : sig_(sig)
{
sig_->reference_exec();
}
signal_impl_exec_holder(const signal_impl_exec_holder& src) = delete;
signal_impl_exec_holder operator=(const signal_impl_exec_holder& src) = delete;
signal_impl_exec_holder(signal_impl_exec_holder&& src) = delete;
signal_impl_exec_holder operator=(signal_impl_exec_holder&& src) = delete;
/// Decrements the reference and execution counter of the parent sigc::signal_impl object.
inline ~signal_impl_exec_holder() { sig_->unreference_exec(); }
protected:
/// The parent sigc::signal_impl object.
signal_impl* sig_;
};
/// Exception safe sweeper for cleaning up invalid slots on the slot list.
struct SIGC_API signal_impl_holder
{
/** Increments the reference and execution counter of the parent sigc::signal_impl object.
* @param sig The parent sigc::signal_impl object.
*/
inline signal_impl_holder(const std::shared_ptr& sig) noexcept
: sig_(sig), exec_holder_(sig.get())
{
}
signal_impl_holder(const signal_impl_holder& src) = delete;
signal_impl_holder operator=(const signal_impl_holder& src) = delete;
signal_impl_holder(signal_impl_holder&& src) = delete;
signal_impl_holder operator=(signal_impl_holder&& src) = delete;
protected:
/// The parent sigc::signal_impl object.
const std::shared_ptr sig_;
signal_impl_exec_holder exec_holder_;
};
} /* namespace internal */
/** @defgroup signal Signals
* Use @ref sigc::signal_with_accumulator::connect() "sigc::signal::connect()"
* or @ref sigc::signal_with_accumulator::connect_first() "sigc::signal::connect_first()"
* with sigc::mem_fun() and sigc::ptr_fun() to connect a method or function with a signal.
*
* @code
* signal_clicked.connect( sigc::mem_fun(*this, &MyWindow::on_clicked) );
* @endcode
*
* When the signal is emitted your method will be called.
*
* signal::connect() returns a connection, which you can later use to disconnect your method.
* If the type of your object inherits from sigc::trackable the method is disconnected
* automatically when your object is destroyed.
*
* When signals are copied they share the underlying information, so you can have
* a protected/private @ref sigc::signal "sigc::signal"
* member and a public accessor method.
* A sigc::signal is a kind of reference-counting pointer. It's similar to
* std::shared_ptr<>, although sigc::signal is restricted to holding a pointer to
* a sigc::internal::signal_impl object that contains the implementation of the signal.
*
* @code
* class MyClass
* {
* public:
* using MySignalType = sigc::signal;
* MySignalType get_my_signal() { return m_my_signal; }
* private:
* MySignalType m_my_signal;
* };
* @endcode
*
* signal and slot objects provide the core functionality of this
* library. A slot is a container for an arbitrary functor.
* A signal is a list of slots that are executed on emission.
* For compile time type safety a list of template arguments
* must be provided for the signal template that determines the
* parameter list for emission. Functors and closures are converted
* into slots implicitly on connection, triggering compiler errors
* if the given functor or closure cannot be invoked with the
* parameter list of the signal to connect to.
*
* Almost any functor with the correct signature can be converted to
* a @ref sigc::slot "sigc::slot"
* and connected to a signal. See @ref slot "Slots" and
* @ref sigc::signal_with_accumulator::connect() "sigc::signal::connect()".
*
* Use @ref sigc::signal_connect() to connect a method or function to a signal
* without having to explicitly write the required template parameters in case
* of method or function overloading.
*
* @code
* sigc::signal sig;
* void fun(int);
* void fun(double);
* sig.connect(sigc::ptr_fun(fun));
* // or more simply:
* sigc::signal_connect(sig, fun);
* @endcode
*
* It can also be used as a replacement for calling signal::connect() with a
* sigc::mem_fun() or a sigc::ptr_fun().
*/
// TODO: When we can break ABI, let signal_base derive from trackable again,
// as in sigc++2. Otherwise the slot returned from signal::make_slot()
// is not automatically disconnected when the signal is deleted.
// And delete trackable_signal_with_accumulator and trackable_signal.
// https://github.com/libsigcplusplus/libsigcplusplus/issues/80
/** Base class for the @ref sigc::signal "sigc::signal" template.
* %signal_base integrates most of the interface of the derived
* @ref sigc::signal "sigc::signal" template.
* The implementation, however, resides in sigc::internal::signal_impl.
* A sigc::internal::signal_impl object is dynamically allocated from %signal_base
* when first connecting a slot to the signal. This ensures that empty signals
* don't waste memory.
*
* sigc::internal::signal_impl is reference-counted.
* When a @ref sigc::signal "sigc::signal" object
* is copied, the reference count of its sigc::internal::signal_impl object is
* incremented. Both @ref sigc::signal "sigc::signal" objects
* then refer to the same sigc::internal::signal_impl object.
*
* @ingroup signal
*/
struct SIGC_API signal_base
{
using size_type = std::size_t;
signal_base() noexcept;
signal_base(const signal_base& src) noexcept;
signal_base(signal_base&& src);
~signal_base();
signal_base& operator=(const signal_base& src);
signal_base& operator=(signal_base&& src);
/** Returns whether the list of slots is empty.
* @return @p true if the list of slots is empty.
*/
inline bool empty() const noexcept { return (!impl_ || impl_->empty()); }
/// Empties the list of slots.
void clear();
/** Returns the number of slots in the list.
* @return The number of slots in the list.
*/
size_type size() const noexcept;
/** Returns whether all slots in the list are blocked.
* @return @p true if all slots are blocked or the list is empty.
*
* @newin{2,4}
*/
bool blocked() const noexcept;
/** Sets the blocking state of all slots in the list.
* If @e should_block is @p true then the blocking state is set.
* Subsequent emissions of the signal don't invoke the functors
* contained in the slots until unblock() or block() with
* @e should_block = @p false is called.
* sigc::slot_base::block() and sigc::slot_base::unblock() can change the
* blocking state of individual slots.
* @param should_block Indicates whether the blocking state should be set or unset.
*
* @newin{2,4}
*/
void block(bool should_block = true) noexcept;
/** Unsets the blocking state of all slots in the list.
*
* @newin{2,4}
*/
void unblock() noexcept;
protected:
using iterator_type = internal::signal_impl::iterator_type;
/** Adds a slot at the end of the list of slots.
* With %connect(), slots can also be added during signal emission.
* In this case, they won't be executed until the next emission occurs.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*/
iterator_type connect(const slot_base& slot_);
/** Adds a slot at the end of the list of slots.
* With %connect(), slots can also be added during signal emission.
* In this case, they won't be executed until the next emission occurs.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*
* @newin{2,8}
*/
iterator_type connect(slot_base&& slot_);
/** Adds a slot at the beginning of the list of slots.
* With %connect_first(), slots can also be added during signal emission.
* In this case, they won't be executed until the next emission occurs.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*
* @newin{3,6}
*/
iterator_type connect_first(const slot_base& slot_);
/** Adds a slot at the beginning of the list of slots.
* With %connect_fist(), slots can also be added during signal emission.
* In this case, they won't be executed until the next emission occurs.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*
* @newin{3,6}
*/
iterator_type connect_first(slot_base&& slot_);
/** Adds a slot at the given position into the list of slots.
* Note that this function does not work during signal emission!
* @param i An iterator indicating the position where @e slot_ should be inserted.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*/
iterator_type insert(iterator_type i, const slot_base& slot_);
/** Adds a slot at the given position into the list of slots.
* Note that this function does not work during signal emission!
* @param i An iterator indicating the position where @e slot_ should be inserted.
* @param slot_ The slot to add to the list of slots.
* @return An iterator pointing to the new slot in the list.
*
* @newin{2,8}
*/
iterator_type insert(iterator_type i, slot_base&& slot_);
/** Returns the signal_impl object encapsulating the list of slots.
* @return The signal_impl object encapsulating the list of slots.
*/
std::shared_ptr impl() const;
/// The signal_impl object encapsulating the slot list.
mutable std::shared_ptr impl_;
};
} // namespace sigc
#endif /* SIGC_SIGNAL_BASE_H */