/* Copyright 2003 - 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_SLOT_BASE_HPP
#define SIGC_SLOT_BASE_HPP
#include
#include
namespace sigc
{
namespace internal
{
using hook = void* (*)(void*);
/** Internal representation of a slot.
* Derivations of this class can be considered as a link
* between a slot and the functor that the slot should
* execute in operator(). This link is needed because in
* libsigc++ the slot doesn't necessarily have exactly the
* same function signature as the functor, thus allowing for
* implicit conversions.
*
* The base class slot_rep serves the purpose to
* - form a common pointer type (slot_rep*),
* - offer the possibility to create duplicates (clone()),
* - offer a notification callback (notify_slot_rep_invalidated()),
* - implement some of slot_base's interface that depends
* on the notification callback, i.e.
* -# the possibility to set a single parent with a callback
* (set_parent()) that is executed from notify_slot_rep_invalidated(),
* -# a generic function pointer, call_, that is simply
* set to zero in notify_slot_rep_invalidated() to invalidate the slot.
*
* slot_rep inherits trackable so that connection objects can
* refer to the slot and are notified when the slot is destroyed.
*/
struct SIGC_API slot_rep : public trackable
{
public:
slot_rep(const slot_rep& src) = delete;
slot_rep& operator=(const slot_rep& src) = delete;
slot_rep(slot_rep&& src) = delete;
slot_rep& operator=(slot_rep&& src) = delete;
/* NB: Instead of slot_rep we could inherit slot_base from trackable.
* However, a simple benchmark seems to indicate that this slows
* down dereferencing of slot list iterators. Martin. */
// TODO: Try this now? murrayc.
inline slot_rep(hook call__) noexcept : call_(call__), cleanup_(nullptr), parent_(nullptr) {}
virtual ~slot_rep() {}
// 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
/** Destroys the slot_rep object (but doesn't delete it).
*/
virtual void destroy() = 0;
/** Makes a deep copy of the slot_rep object.
* @return A deep copy of the slot_rep object.
*/
virtual slot_rep* clone() const = 0;
/** Set the parent with a callback.
* slots have one parent exclusively.
* @param parent The new parent.
* @param cleanup The callback to execute from notify_slot_rep_invalidated().
*/
inline void set_parent(notifiable* parent, notifiable::func_destroy_notify cleanup) noexcept
{
parent_ = parent;
cleanup_ = cleanup;
}
/** See set_parent().
*
*/
inline void unset_parent() noexcept
{
parent_ = nullptr;
cleanup_ = nullptr;
}
/// Invalidates the slot and executes the parent's cleanup callback.
void disconnect();
/** Callback that invalidates the slot.
* This callback is registered in every object of a trackable
* inherited type that is referred by this slot_rep object.
* It is executed when the slot becomes invalid because of some
* referred object dying.
* @param data The slot_rep object that is becoming invalid (@p this).
*/
static void notify_slot_rep_invalidated(notifiable* data);
public:
/// Callback that invokes the contained functor.
/* This can't be a virtual function since number of arguments
* must be flexible. We use function pointers to slot_call::call_it()
* instead. call_ is set to zero to indicate that the slot is invalid.
*/
hook call_;
/** Callback of parent_. */
notifiable::func_destroy_notify cleanup_;
/** Parent object whose callback cleanup_ is executed on notification. */
notifiable* parent_;
};
/** Functor used to add a dependency to a trackable.
* Consequently slot_rep::notify_slot_rep_invalidated() gets executed when the
* trackable is destroyed or overwritten.
*/
struct SIGC_API slot_do_bind
{
/** The slot_rep object trackables should notify on destruction. */
slot_rep* rep_;
/** Construct a slot_do_bind functor.
* @param rep The slot_rep object trackables should notify on destruction.
*/
inline explicit slot_do_bind(slot_rep* rep) noexcept : rep_(rep) {}
/** Adds a dependency to @p t.
* @param t The trackable object to add a callback to.
*/
inline void operator()(const trackable& t) const
{
t.add_destroy_notify_callback(rep_, &slot_rep::notify_slot_rep_invalidated);
}
};
/// Functor used to remove a dependency from a trackable.
struct SIGC_API slot_do_unbind
{
/** The slot_rep object trackables don't need to notify on destruction any more. */
slot_rep* rep_;
/** Construct a slot_do_unbind functor.
* @param rep The slot_rep object trackables don't need to notify on destruction any more.
*/
inline explicit slot_do_unbind(slot_rep* rep) noexcept : rep_(rep) {}
/** Removes a dependency from @p t.
* @param t The trackable object to remove the callback from.
*/
inline void operator()(const trackable& t) const { t.remove_destroy_notify_callback(rep_); }
};
} // namespace internal
/** @defgroup slot Slots
* Slots are type-safe representations of callback methods and functions.
* A slot can be constructed from any function object or function, regardless of
* whether it is a global function, a member method, static, or virtual.
*
* @section slots-creating Creating Slots
*
* Use the sigc::mem_fun() or sigc::ptr_fun() template functions to get
* a @ref sigc::slot "sigc::slot", like so:
* @code
* sigc::slot sl = sigc::mem_fun(someobj, &SomeClass::somemethod);
* @endcode
* or
* @code
* sigc::slot sl = sigc::ptr_fun(&somefunction);
* @endcode
* or, in gtkmm,
* @code
* m_Button.signal_clicked().connect( sigc::mem_fun(*this, &MyWindow::on_button_clicked) );
* @endcode
*
* The compiler will complain if SomeClass::somemethod, etc. have the wrong signature.
*
* You can also pass slots as method parameters where you might normally pass a function pointer.
*
* @section slots-auto-disconnect Member Methods and Automatic Disconnection
*
* See @ref mem_fun "sigc::mem_fun()" about deriving from sigc::trackable to prevent member
* methods from being called after the instance has been destroyed.
*
* @section slots-auto auto
*
* sigc::mem_fun() and sigc::ptr_fun() return functors, but those functors are
* not slots.
* @code
* sigc::slot sl = sigc::mem_fun(someobj, &SomeClass::somemethod);
* @endcode
* is not equivalent to
* @code
* auto sl = sigc::mem_fun(someobj, &SomeClass::somemethod); // Not a slot!
* @endcode
*
* If you don't explicitly use a @ref sigc::slot "sigc::slot"
* then the slot could call a method on an instance after it has been destroyed
* even if the method is in a class that derives from sigc::trackable.
*
* @section slots-with-lambdas C++ Lambdas
*
* A C++11 lambda expression is a functor (function object). It is automatically
* wrapped in a slot, if it is connected to a signal.
* @code
* auto on_response = [&someobj] (int response_id)
* {
* someobj.somemethod(response_id);
* somefunction(response_id);
* };
* m_Dialog.signal_response().connect(on_response);
* @endcode
*
* If you connect a C++11 lambda expression or a std::function<> instance to
* a signal or assign it to a slot, if your functor contains references to
* sigc::trackable derived objects, those objects will not be tracked,
* unless you also use sigc::track_obj().
*
* @ingroup sigcfunctors
*/
/** Base type for slots.
* %slot_base integrates most of the interface of the derived
* @ref sigc::slot "sigc::slot" template. Slots
* can be connected to signals, be disconnected at some later point
* (disconnect()) and temporarily be blocked (block(), unblock()).
* The validity of a slot can be tested with empty().
*
* The internal representation of a sigc::internal::slot_rep derived
* type is built from %slot_base's derivations. set_parent() is used to
* register a notification callback that is executed when the slot gets
* invalid. add_destroy_notify_callback() is used by connection objects
* to add a notification callback that is executed on destruction.
*
* @ingroup slot
*/
class SIGC_API slot_base
{
using rep_type = internal::slot_rep;
// Move operations are not declared noexcept because
// 1. they may copy instead of move
// 2. when they don't copy, they call src.rep_->notify_callbacks(), which
// may throw an exception.
public:
/// Constructs an empty slot.
slot_base() noexcept;
/** Constructs a slot from an existing slot_rep object.
* @param rep The slot_rep object this slot should contain.
*/
explicit slot_base(rep_type* rep) noexcept;
/** Constructs a slot, copying an existing one.
* @param src The existing slot to copy.
*/
slot_base(const slot_base& src);
/** Constructs a slot, moving an existing one.
* If @p src is connected to a parent (e.g. a signal), it is copied, not moved.
* @param src The existing slot to move or copy.
*/
slot_base(slot_base&& src);
~slot_base();
/** Tests whether a slot is null, because the default constructor was used.
* Test a slot for null like so:
* @code
* if(slot)
* do_something()
* @endcode
*/
explicit operator bool() const noexcept;
using func_destroy_notify = notifiable::func_destroy_notify;
/** Sets the parent of this slot.
* This function is used by signals to register a notification callback.
* This notification callback is executed when the slot becomes invalid
* because of some referred object dying.
* @param parent The new parent.
* @param cleanup The notification callback.
*/
void set_parent(notifiable* parent, notifiable::func_destroy_notify cleanup) const noexcept;
/** Add a callback that is executed (notified) when the slot is destroyed.
* This function is used internally by connection objects.
* @param data Passed into func upon notification.
* @param func Callback executed upon destruction of the object.
*/
void add_destroy_notify_callback(notifiable* data, notifiable::func_destroy_notify func) const;
/** Remove a callback previously installed with add_destroy_notify_callback().
* The callback is not executed.
* @param data Parameter passed into previous call to add_destroy_notify_callback().
*/
void remove_destroy_notify_callback(notifiable* data) const;
/** Returns whether the slot is invalid.
* @return @p true if the slot is invalid (empty).
*/
inline bool empty() const noexcept { return (!rep_ || !rep_->call_); }
/** Returns whether the slot is blocked.
* @return @p true if the slot is blocked.
*/
inline bool blocked() const noexcept { return blocked_; }
/** Sets the blocking state.
* If @e should_block is @p true then the blocking state is set.
* Subsequent calls to slot::operator()() don't invoke the functor
* contained by this slot until unblock() or block() with
* @e should_block = @p false is called.
* @param should_block Indicates whether the blocking state should be set or unset.
* @return @p true if the slot was in blocking state before.
*/
bool block(bool should_block = true) noexcept;
/** Unsets the blocking state.
* @return @p true if the slot was in blocking state before.
*/
bool unblock() noexcept;
/** Disconnects the slot.
* Invalidates the slot and notifies the parent.
*/
void disconnect();
// The Tru64 and Solaris Forte 5.5 compilers needs this operator=() to be public. I'm not sure
// why, or why it needs to be protected usually. murrayc.
// See bug #168265.
// protected:
/** Overrides this slot, making a copy from another slot.
* @param src The slot from which to make a copy.
* @return @p this.
*/
slot_base& operator=(const slot_base& src);
/** Overrides this slot, making a move from another slot.
* If @p src is connected to a parent (e.g. a signal), it is copied, not moved.
* @param src The slot from which to move or copy.
* @return @p this.
*/
slot_base& operator=(slot_base&& src);
public: // public to avoid template friend declarations
/** Typed slot_rep object that contains a functor. */
mutable rep_type* rep_;
/** Indicates whether the slot is blocked. */
bool blocked_;
private:
void delete_rep_with_check();
};
} // namespace sigc
#endif // SIGC_SLOT_BASE_HPP