[libsigc++2] slot: Handle auto-disconnection when a slot contains a slot
- From: Kjell Ahlstedt <kjellahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsigc++2] slot: Handle auto-disconnection when a slot contains a slot
- Date: Fri, 6 Nov 2015 09:43:07 +0000 (UTC)
commit 1ad7f93386aed6afab67249413c8ebef05882b67
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date: Fri Nov 6 10:40:00 2015 +0100
slot: Handle auto-disconnection when a slot contains a slot
* sigc++/functors/macros/slot.h.m4: Add sigc::visitor<> specializations
for slot# and slot.
* tests/test_disconnect.cc: Test auto-disconnection of a slot that contains
a slot. Bug #755003.
sigc++/functors/macros/slot.h.m4 | 94 ++++++++++++++++++++++++++++++++++++--
tests/test_disconnect.cc | 39 +++++++++++++++-
2 files changed, 126 insertions(+), 7 deletions(-)
---
diff --git a/sigc++/functors/macros/slot.h.m4 b/sigc++/functors/macros/slot.h.m4
index a6c7288..0a141a2 100644
--- a/sigc++/functors/macros/slot.h.m4
+++ b/sigc++/functors/macros/slot.h.m4
@@ -39,11 +39,6 @@ FOR(1,$1,[
*
* @ingroup slot
*/
-/* TODO: Where put the following bit of information? I can't make any
- * sense of the "because", by the way!
- *
- * Because slot is opaque, visit_each() will not visit its internal members.
- */
template <LIST(class T_return, LOOP(class T_arg%1, $1))>
class slot$1
: public slot_base
@@ -122,9 +117,48 @@ FOR(1, $1,[
}
};
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//template specialization of visitor<>::do_visit_each<>(action, functor):
+/** Performs a functor on each of the targets of a functor.
+ * The function overloads for sigc::slot$1 are similar to the function
+ * overloads for sigc::slot. See the description of those overloads.
+ *
+ * @ingroup slot
+ */
+template <LIST(typename T_return, LOOP(typename T_arg%1, $1))>
+struct visitor<slot$1<LIST(T_return, LOOP(T_arg%1, $1))>>
+{
+ static void do_visit_each(const internal::limit_derived_target<trackable*, internal::slot_do_bind>&
_A_action,
+ const slot$1<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
+ {
+ if (_A_target.rep_ && _A_target.rep_->parent_ == nullptr)
+ _A_target.rep_->set_parent(_A_action.action_.rep_, &internal::slot_rep::notify);
+ }
+
+ static void do_visit_each(const internal::limit_derived_target<trackable*, internal::slot_do_unbind>&
_A_action,
+ const slot$1<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
+ {
+ if (_A_target.rep_ && _A_target.rep_->parent_ == _A_action.action_.rep_)
+ _A_target.rep_->set_parent(nullptr, nullptr);
+ }
+
+ template <typename T_action>
+ static void do_visit_each(const T_action& _A_action,
+ const slot$1<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
+ {
+ _A_action(_A_target);
+ }
+};
+#endif // DOXYGEN_SHOULD_SKIP_THIS
+
])
define([SLOT],[dnl
ifelse($1, $2,[dnl
+// Because slot is opaque, visit_each() will not visit its internal members.
+// Those members are not reachable by visit_each() after the slot has been
+// constructed. But when a slot contains another slot, the outer slot will become
+// the parent of the inner slot, with similar results. See the description of
+// slot's specialization of the visitor struct.
/** Convenience wrapper for the numbered sigc::slot# templates.
* Slots convert arbitrary functors to unified types which are opaque.
* sigc::slot itself is a functor or to be more precise a closure. It contains
@@ -190,6 +224,56 @@ public:
: parent_type(reinterpret_cast<const parent_type&>(src)) {}
};
+ifelse($1, $2,[dnl
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//template specialization of visitor<>::do_visit_each<>(action, functor):
+/** Performs a functor on each of the targets of a functor.
+ *
+ * There are three function overloads for sigc::slot.
+ *
+ * The first two overloads are very specialized. They handle the (probably unusual)
+ * case when the functor, stored in a slot, contains a slot. They are invoked from
+ * the constructor, destructor or destroy() method of typed_slot_rep.
+ * The first overload, called from the constructor of the outer slot, sets
+ * the outer slot as the parent of the inner slot. The second overload, called from
+ * the destructor or destroy() of the outer slot, unsets the parent of the inner slot.
+ * When an object referenced from the inner slot is deleted, the inner slot calls
+ * its slot_rep::disconnect(), which calls the outer slot's slot_rep::notify().
+ * The outer slot is informed just as if one of its directly referenced objects
+ * had been deleted. Result: The outer slot is disconnected from its parent,
+ * if any (for instance a sigc::signal).
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=755003
+ *
+ * The third overload is identical to do_visit_each() in visitor's primary template.
+ *
+ * @ingroup slot
+ */
+template <LIST(typename T_return, LOOP(typename T_arg%1, $1))>
+struct visitor<slot<LIST(T_return, LOOP(T_arg%1, $1))>>
+{
+ static void do_visit_each(const internal::limit_derived_target<trackable*, internal::slot_do_bind>&
_A_action,
+ const slot<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
+ {
+ if (_A_target.rep_ && _A_target.rep_->parent_ == nullptr)
+ _A_target.rep_->set_parent(_A_action.action_.rep_, &internal::slot_rep::notify);
+ }
+
+ static void do_visit_each(const internal::limit_derived_target<trackable*, internal::slot_do_unbind>&
_A_action,
+ const slot<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
+ {
+ if (_A_target.rep_ && _A_target.rep_->parent_ == _A_action.action_.rep_)
+ _A_target.rep_->set_parent(nullptr, nullptr);
+ }
+
+ template <typename T_action>
+ static void do_visit_each(const T_action& _A_action,
+ const slot<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
+ {
+ _A_action(_A_target);
+ }
+};
+#endif // DOXYGEN_SHOULD_SKIP_THIS
+])
])
define([SLOT_CALL],[dnl
/** Abstracts functor execution.
diff --git a/tests/test_disconnect.cc b/tests/test_disconnect.cc
index b3eab31..53e081a 100644
--- a/tests/test_disconnect.cc
+++ b/tests/test_disconnect.cc
@@ -1,4 +1,3 @@
-// -*- c++ -*-
/* Copyright 2002, The libsigc++ Development Team
* Assigned to public domain. Use as you wish without restriction.
*/
@@ -7,6 +6,7 @@
#include <sigc++/trackable.h>
#include <sigc++/signal.h>
#include <sigc++/connection.h>
+#include <sigc++/adaptors/compose.h>
#include <sigc++/functors/ptr_fun.h>
#include <sigc++/functors/mem_fun.h>
#include <sstream>
@@ -58,7 +58,7 @@ struct B : public sigc::trackable
void destroy() // Calling destroy() during signal emission seems weird!
{ // However, if this works, anything will work!
- delete this;
+ delete this; // valgrind reports a small memory leak, that's all.
}
void boom()
@@ -127,6 +127,41 @@ int main(int argc, char* argv[])
cona.disconnect(); // already disconnected -> legal with connection objects, however, nothing happens
...
+ {
+ A a2;
+ sig.connect(sigc::compose(sigc::mem_fun(&a2, &A::foo), &foo));
+ result_stream << "sig is connected to compose(A::foo, foo) (size=" << sig.size() << "): ";
+ sig(7);
+ util->check_result(result_stream, "sig is connected to compose(A::foo, foo) (size=1): foo(7) A::foo(1)
");
+ }
+ result_stream << "sig is empty (size=" << sig.size() << "): ";
+ sig(8);
+ util->check_result(result_stream, "sig is empty (size=0): ");
+
+ { // A slot# within a slot
+ A a2;
+ sigc::slot1<int, int> setter = sigc::mem_fun(&a2, &A::foo);
+ sig.connect(sigc::compose(setter, &foo));
+ result_stream << "sig is connected to compose(slot1(A::foo), foo) (size=" << sig.size() << "): ";
+ sig(9);
+ util->check_result(result_stream, "sig is connected to compose(slot1(A::foo), foo) (size=1): foo(9)
A::foo(1) ");
+ }
+ result_stream << "sig is empty (size=" << sig.size() << "): ";
+ sig(10);
+ util->check_result(result_stream, "sig is empty (size=0): ");
+
+ { // A slot within a slot
+ A a2;
+ sigc::slot<int, int> setter = sigc::mem_fun(&a2, &A::foo);
+ sig.connect(sigc::compose(setter, &foo));
+ result_stream << "sig is connected to compose(slot(A::foo), foo) (size=" << sig.size() << "): ";
+ sig(11);
+ util->check_result(result_stream, "sig is connected to compose(slot(A::foo), foo) (size=1): foo(11)
A::foo(1) ");
+ }
+ result_stream << "sig is empty (size=" << sig.size() << "): ";
+ sig(12);
+ util->check_result(result_stream, "sig is empty (size=0): ");
+
result_stream << "deleting a signal during emission... ";
auto b = new B;
b->emit();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]