Skip to content

Commit 1ad7f93

Browse files
author
Kjell Ahlstedt
committed
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.
1 parent d14750e commit 1ad7f93

File tree

2 files changed

+126
-7
lines changed

2 files changed

+126
-7
lines changed

sigc++/functors/macros/slot.h.m4

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@ FOR(1,$1,[
3939
*
4040
* @ingroup slot
4141
*/
42-
/* TODO: Where put the following bit of information? I can't make any
43-
* sense of the "because", by the way!
44-
*
45-
* Because slot is opaque, visit_each() will not visit its internal members.
46-
*/
4742
template <LIST(class T_return, LOOP(class T_arg%1, $1))>
4843
class slot$1
4944
: public slot_base
@@ -122,9 +117,48 @@ FOR(1, $1,[
122117
}
123118
};
124119
120+
#ifndef DOXYGEN_SHOULD_SKIP_THIS
121+
//template specialization of visitor<>::do_visit_each<>(action, functor):
122+
/** Performs a functor on each of the targets of a functor.
123+
* The function overloads for sigc::slot$1 are similar to the function
124+
* overloads for sigc::slot. See the description of those overloads.
125+
*
126+
* @ingroup slot
127+
*/
128+
template <LIST(typename T_return, LOOP(typename T_arg%1, $1))>
129+
struct visitor<slot$1<LIST(T_return, LOOP(T_arg%1, $1))>>
130+
{
131+
static void do_visit_each(const internal::limit_derived_target<trackable*, internal::slot_do_bind>& _A_action,
132+
const slot$1<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
133+
{
134+
if (_A_target.rep_ && _A_target.rep_->parent_ == nullptr)
135+
_A_target.rep_->set_parent(_A_action.action_.rep_, &internal::slot_rep::notify);
136+
}
137+
138+
static void do_visit_each(const internal::limit_derived_target<trackable*, internal::slot_do_unbind>& _A_action,
139+
const slot$1<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
140+
{
141+
if (_A_target.rep_ && _A_target.rep_->parent_ == _A_action.action_.rep_)
142+
_A_target.rep_->set_parent(nullptr, nullptr);
143+
}
144+
145+
template <typename T_action>
146+
static void do_visit_each(const T_action& _A_action,
147+
const slot$1<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
148+
{
149+
_A_action(_A_target);
150+
}
151+
};
152+
#endif // DOXYGEN_SHOULD_SKIP_THIS
153+
125154
])
126155
define([SLOT],[dnl
127156
ifelse($1, $2,[dnl
157+
// Because slot is opaque, visit_each() will not visit its internal members.
158+
// Those members are not reachable by visit_each() after the slot has been
159+
// constructed. But when a slot contains another slot, the outer slot will become
160+
// the parent of the inner slot, with similar results. See the description of
161+
// slot's specialization of the visitor struct.
128162
/** Convenience wrapper for the numbered sigc::slot# templates.
129163
* Slots convert arbitrary functors to unified types which are opaque.
130164
* sigc::slot itself is a functor or to be more precise a closure. It contains
@@ -190,6 +224,56 @@ public:
190224
: parent_type(reinterpret_cast<const parent_type&>(src)) {}
191225
};
192226
227+
ifelse($1, $2,[dnl
228+
#ifndef DOXYGEN_SHOULD_SKIP_THIS
229+
//template specialization of visitor<>::do_visit_each<>(action, functor):
230+
/** Performs a functor on each of the targets of a functor.
231+
*
232+
* There are three function overloads for sigc::slot.
233+
*
234+
* The first two overloads are very specialized. They handle the (probably unusual)
235+
* case when the functor, stored in a slot, contains a slot. They are invoked from
236+
* the constructor, destructor or destroy() method of typed_slot_rep.
237+
* The first overload, called from the constructor of the outer slot, sets
238+
* the outer slot as the parent of the inner slot. The second overload, called from
239+
* the destructor or destroy() of the outer slot, unsets the parent of the inner slot.
240+
* When an object referenced from the inner slot is deleted, the inner slot calls
241+
* its slot_rep::disconnect(), which calls the outer slot's slot_rep::notify().
242+
* The outer slot is informed just as if one of its directly referenced objects
243+
* had been deleted. Result: The outer slot is disconnected from its parent,
244+
* if any (for instance a sigc::signal).
245+
* See https://bugzilla.gnome.org/show_bug.cgi?id=755003
246+
*
247+
* The third overload is identical to do_visit_each() in visitor's primary template.
248+
*
249+
* @ingroup slot
250+
*/
251+
template <LIST(typename T_return, LOOP(typename T_arg%1, $1))>
252+
struct visitor<slot<LIST(T_return, LOOP(T_arg%1, $1))>>
253+
{
254+
static void do_visit_each(const internal::limit_derived_target<trackable*, internal::slot_do_bind>& _A_action,
255+
const slot<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
256+
{
257+
if (_A_target.rep_ && _A_target.rep_->parent_ == nullptr)
258+
_A_target.rep_->set_parent(_A_action.action_.rep_, &internal::slot_rep::notify);
259+
}
260+
261+
static void do_visit_each(const internal::limit_derived_target<trackable*, internal::slot_do_unbind>& _A_action,
262+
const slot<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
263+
{
264+
if (_A_target.rep_ && _A_target.rep_->parent_ == _A_action.action_.rep_)
265+
_A_target.rep_->set_parent(nullptr, nullptr);
266+
}
267+
268+
template <typename T_action>
269+
static void do_visit_each(const T_action& _A_action,
270+
const slot<LIST(T_return, LOOP(T_arg%1, $1))>& _A_target)
271+
{
272+
_A_action(_A_target);
273+
}
274+
};
275+
#endif // DOXYGEN_SHOULD_SKIP_THIS
276+
])
193277
])
194278
define([SLOT_CALL],[dnl
195279
/** Abstracts functor execution.

tests/test_disconnect.cc

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// -*- c++ -*-
21
/* Copyright 2002, The libsigc++ Development Team
32
* Assigned to public domain. Use as you wish without restriction.
43
*/
@@ -7,6 +6,7 @@
76
#include <sigc++/trackable.h>
87
#include <sigc++/signal.h>
98
#include <sigc++/connection.h>
9+
#include <sigc++/adaptors/compose.h>
1010
#include <sigc++/functors/ptr_fun.h>
1111
#include <sigc++/functors/mem_fun.h>
1212
#include <sstream>
@@ -58,7 +58,7 @@ struct B : public sigc::trackable
5858

5959
void destroy() // Calling destroy() during signal emission seems weird!
6060
{ // However, if this works, anything will work!
61-
delete this;
61+
delete this; // valgrind reports a small memory leak, that's all.
6262
}
6363

6464
void boom()
@@ -127,6 +127,41 @@ int main(int argc, char* argv[])
127127

128128
cona.disconnect(); // already disconnected -> legal with connection objects, however, nothing happens ...
129129

130+
{
131+
A a2;
132+
sig.connect(sigc::compose(sigc::mem_fun(&a2, &A::foo), &foo));
133+
result_stream << "sig is connected to compose(A::foo, foo) (size=" << sig.size() << "): ";
134+
sig(7);
135+
util->check_result(result_stream, "sig is connected to compose(A::foo, foo) (size=1): foo(7) A::foo(1) ");
136+
}
137+
result_stream << "sig is empty (size=" << sig.size() << "): ";
138+
sig(8);
139+
util->check_result(result_stream, "sig is empty (size=0): ");
140+
141+
{ // A slot# within a slot
142+
A a2;
143+
sigc::slot1<int, int> setter = sigc::mem_fun(&a2, &A::foo);
144+
sig.connect(sigc::compose(setter, &foo));
145+
result_stream << "sig is connected to compose(slot1(A::foo), foo) (size=" << sig.size() << "): ";
146+
sig(9);
147+
util->check_result(result_stream, "sig is connected to compose(slot1(A::foo), foo) (size=1): foo(9) A::foo(1) ");
148+
}
149+
result_stream << "sig is empty (size=" << sig.size() << "): ";
150+
sig(10);
151+
util->check_result(result_stream, "sig is empty (size=0): ");
152+
153+
{ // A slot within a slot
154+
A a2;
155+
sigc::slot<int, int> setter = sigc::mem_fun(&a2, &A::foo);
156+
sig.connect(sigc::compose(setter, &foo));
157+
result_stream << "sig is connected to compose(slot(A::foo), foo) (size=" << sig.size() << "): ";
158+
sig(11);
159+
util->check_result(result_stream, "sig is connected to compose(slot(A::foo), foo) (size=1): foo(11) A::foo(1) ");
160+
}
161+
result_stream << "sig is empty (size=" << sig.size() << "): ";
162+
sig(12);
163+
util->check_result(result_stream, "sig is empty (size=0): ");
164+
130165
result_stream << "deleting a signal during emission... ";
131166
auto b = new B;
132167
b->emit();

0 commit comments

Comments
 (0)