Skip to content

Commit 3238608

Browse files
db-srckjellahl
authored andcommitted
scoped_connection: new wrapper to auto-disconnect…
…a contained sigc::connection, when the scoped_connection is destructed. #87
1 parent 5c9d60c commit 3238608

File tree

9 files changed

+521
-0
lines changed

9 files changed

+521
-0
lines changed

sigc++/connection.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1717
*
1818
*/
19+
1920
#ifndef SIGC_CONNECTION_HPP
2021
#define SIGC_CONNECTION_HPP
22+
2123
#include <sigc++config.h>
2224
#include <sigc++/functors/slot_base.h>
2325
#include <sigc++/weak_raw_ptr.h>
@@ -30,13 +32,20 @@ namespace sigc
3032
* This may be used to disconnect the referred slot at any time (disconnect()).
3133
* @ref sigc::signal_with_accumulator::connect() "sigc::signal::connect()"
3234
* returns a %sigc::connection.
35+
*
3336
* @code
3437
* sigc::connection conn = sig.connect(sigc::mem_fun(a, &A::foo));
3538
* @endcode
39+
*
3640
* If the slot has already been destroyed, disconnect() does nothing. empty() or
3741
* operator bool() can be used to test whether the connection is
3842
* still active. The connection can be blocked (block(), unblock()).
3943
*
44+
* sigc::connection doesnʼt disconnect the slot automatically upon destruction.
45+
* You do not need to keep the sigc::connection object to retain the connection
46+
* of the slot to the signal. See also @ref sigc::scoped_connection, which does
47+
* diconnect automatically when the connection object is destroyed or replaced.
48+
*
4049
* @ingroup signal
4150
*/
4251
struct SIGC_API connection

sigc++/filelist.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ sigc_public_h = \
2424
member_method_trait.h \
2525
reference_wrapper.h \
2626
retype_return.h \
27+
scoped_connection.h \
2728
signal.h \
2829
signal_base.h \
2930
slot.h \

sigc++/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
source_cc_files = [
1010
'connection.cc',
11+
'scoped_connection.cc',
1112
'signal_base.cc',
1213
'trackable.cc',
1314
'functors' / 'slot_base.cc',
@@ -21,6 +22,7 @@ sigc_h_files = [
2122
'member_method_trait.h',
2223
'reference_wrapper.h',
2324
'retype_return.h',
25+
'scoped_connection.h',
2426
'signal.h',
2527
'signal_base.h',
2628
'slot.h',

sigc++/scoped_connection.cc

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2023, The libsigc++ Development Team
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+
*
18+
*/
19+
20+
#include <sigc++/scoped_connection.h>
21+
#include <utility>
22+
23+
namespace sigc
24+
{
25+
26+
// All we are doing is assigning weak_raw_ptr, which is noexcept, so declare it.
27+
// connectionʼs copy operators can be noexcept for that reason, if breaking ABI.
28+
scoped_connection::scoped_connection(connection c) noexcept
29+
: conn_(std::move(c))
30+
{
31+
}
32+
33+
scoped_connection&
34+
scoped_connection::operator=(connection c)
35+
{
36+
conn_.disconnect();
37+
conn_ = std::move(c);
38+
return *this;
39+
}
40+
41+
// We do not implement move-ctor in terms of move-assign, so we can be noexcept,
42+
// as we do not need to call the maybe-throwing disconnect() for obvious reason.
43+
scoped_connection::scoped_connection(scoped_connection&& sc) noexcept
44+
: conn_(std::exchange(sc.conn_, connection()))
45+
{
46+
}
47+
48+
scoped_connection&
49+
scoped_connection::operator=(scoped_connection&& sc)
50+
{
51+
conn_.disconnect();
52+
conn_ = std::exchange(sc.conn_, connection());
53+
return *this;
54+
}
55+
56+
scoped_connection::~scoped_connection()
57+
{
58+
conn_.disconnect();
59+
}
60+
61+
bool
62+
scoped_connection::empty() const noexcept
63+
{
64+
return conn_.empty();
65+
}
66+
67+
bool
68+
scoped_connection::connected() const noexcept
69+
{
70+
return conn_.connected();
71+
}
72+
73+
bool
74+
scoped_connection::blocked() const noexcept
75+
{
76+
return conn_.blocked();
77+
}
78+
79+
bool
80+
scoped_connection::block(bool should_block) noexcept
81+
{
82+
return conn_.block(should_block);
83+
}
84+
85+
bool
86+
scoped_connection::unblock() noexcept
87+
{
88+
return conn_.unblock();
89+
}
90+
91+
void
92+
scoped_connection::disconnect()
93+
{
94+
conn_.disconnect();
95+
}
96+
97+
scoped_connection::operator bool() const noexcept
98+
{
99+
return conn_.operator bool();
100+
}
101+
102+
// Swapping can be noexcept, as it does not need to disconnect either connection
103+
// because they will still stay alive, just in opposite instances post-swapping.
104+
void
105+
swap(scoped_connection &sca, scoped_connection &scb) noexcept
106+
{
107+
using std::swap;
108+
swap(sca.conn_, scb.conn_);
109+
}
110+
111+
connection
112+
scoped_connection::release() noexcept
113+
{
114+
return std::exchange(conn_, connection());
115+
}
116+
117+
} /* namespace sigc */

sigc++/scoped_connection.h

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Copyright 2023, The libsigc++ Development Team
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+
*
18+
*/
19+
20+
#ifndef SIGC_SCOPED_CONNECTION_HPP
21+
#define SIGC_SCOPED_CONNECTION_HPP
22+
23+
#include <sigc++/connection.h>
24+
25+
namespace sigc
26+
{
27+
28+
/** Convenience class for safe disconnection, including automatic disconnection
29+
* upon destruction.
30+
*
31+
* This is a variant of @ref sigc::connection which also disconnect()s the slot
32+
* automatically when the scoped_connection is destructed or re-assigned. Refer
33+
* to @ref sigc::connection for full information about the common functionality.
34+
*
35+
* You will use sigc::scoped_connection by constructing it from a ‘normal’,
36+
* unscoped @ref sigc::connection, such as those returned by
37+
* @ref sigc::signal_with_accumulator::connect() "sigc::signal::connect()", thus
38+
* ‘wrapping’ the connection in a scoped_connection, adding auto-disconnection.
39+
* It can also be assigned from an unscoped connection, in which case, if there
40+
* was a previous slot referred to by the scoped connection, it is disconnected.
41+
*
42+
* Once a connection is scoped, it canʼt be copied as that would make it unclear
43+
* which of the copies would hold responsibility to auto-disconnect the slot. It
44+
* can, however, be moved, so itʼs usable in containers or so ‘ownership’ of the
45+
* connection/auto-disconnect can be moved to another instance. Moving from the
46+
* scoped_connection clears its reference to the slot so it wonʼt disconnect it.
47+
*
48+
* If you want a reference-counted scoped_connection, wrap in a std::shared_ptr.
49+
*
50+
* @code
51+
* // Automatic disconnection:
52+
* {
53+
* sigc::scoped_connection sconn = sig.connect(&some_function);
54+
* // Do stuff that requires the slot to be connected & called.
55+
* }
56+
* // The scoped_connection was destroyed, so the slot is no longer connected.
57+
*
58+
* // ***
59+
*
60+
* // Moving ownership:
61+
* {
62+
* sigc::scoped_connection sconn = sig.connect(&some_function);
63+
* // Do stuff that requires the slot to be connected & called.
64+
* take_ownership(std::move(sconn)); // Pass by rvalue.
65+
* }
66+
* // Now our `sconn` no longer referred to slot, so it did NOT auto-disconnect.
67+
*
68+
* // ***
69+
*
70+
* // Shared ownership:
71+
* {
72+
* auto shconn = std::make_shared<sigc::scoped_connection>(sig.connect(&some_function));
73+
* take_copy(shconn); // Pass by copy/value
74+
* // Now we AND take_copy() must destroy our shared_ptr to auto-disconnect().
75+
* }
76+
* // take_copy() may still hold a shared_ptr reference, keeping the slot alive.
77+
* @endcode
78+
*
79+
* @ingroup signal
80+
* @newin{3,6}
81+
*/
82+
struct SIGC_API scoped_connection final
83+
{
84+
/** Constructs an empty scoped connection object. */
85+
[[nodiscard]] scoped_connection() noexcept = default;
86+
87+
/** Constructs a scoped connection object from an unscoped connection object.
88+
* The source connection still refers to the slot and can manually disconnect.
89+
* @param c The connection object to make a copy from, whose slot weʼll
90+
* automatically disconnect when the scoped_connection object is destroyed.
91+
*/
92+
[[nodiscard]] scoped_connection(connection c) noexcept;
93+
94+
/** Overrides this scoped connection object copying an unscoped connection.
95+
* The current slot, if any, will be disconnect()ed before being replaced.
96+
* The source connection still refers to the slot and can manually disconnect.
97+
* @param c The connection object to make a copy from, whose slot weʼll
98+
* automatically disconnect when the scoped_connection object is destroyed.
99+
*/
100+
scoped_connection& operator=(connection c);
101+
102+
/// scoped_connection canʼt be copied as it would confuse ownership—see intro.
103+
scoped_connection& operator=(const scoped_connection&) = delete;
104+
/// scoped_connection canʼt be copied as it would confuse ownership—see intro.
105+
scoped_connection(const scoped_connection&) = delete;
106+
107+
/** Constructs a scoped connection object moving an existing one.
108+
* The source scoped connection will no longer refer to / disconnect the slot.
109+
* @param sc The scoped connection object to move from.
110+
*/
111+
scoped_connection(scoped_connection&& sc) noexcept;
112+
113+
/** Overrides this scoped connection object moving another one.
114+
* The current slot, if any, will be disconnect()ed before being replaced.
115+
* The source scoped connection will no longer refer to / disconnect the slot.
116+
* @param sc The scoped connection object to move from.
117+
*/
118+
scoped_connection& operator=(scoped_connection&& sc);
119+
120+
/// Swap two scoped connections.
121+
friend void swap(scoped_connection &sca, scoped_connection &scb) noexcept;
122+
123+
/// scoped_connection disconnects the referred slot, if any, upon destruction.
124+
~scoped_connection();
125+
126+
/** Returns whether the connection is still active.
127+
* @return @p false if the connection is still active.
128+
*/
129+
[[nodiscard]] bool empty() const noexcept;
130+
131+
/** Returns whether the connection is still active.
132+
* @return @p true if the connection is still active.
133+
*/
134+
[[nodiscard]] bool connected() const noexcept;
135+
136+
/** Returns whether the connection is blocked.
137+
* @return @p true if the connection is blocked.
138+
*/
139+
[[nodiscard]] bool blocked() const noexcept;
140+
141+
/** Sets or unsets the blocking state of this connection.
142+
* See slot_base::block() for details.
143+
* @param should_block Indicates whether the blocking state should be set or unset.
144+
* @return @p true if the connection has been in blocking state before.
145+
*/
146+
bool block(bool should_block = true) noexcept;
147+
148+
/** Unsets the blocking state of this connection.
149+
* @return @p true if the connection has been in blocking state before.
150+
*/
151+
bool unblock() noexcept;
152+
153+
/// Disconnects the referred slot. This will also happen upon destruction.
154+
void disconnect();
155+
156+
/** Returns whether the connection is still active.
157+
* @return @p true if the connection is still active.
158+
*/
159+
[[nodiscard]] explicit operator bool() const noexcept;
160+
161+
/** Releases the connection from a scoped connection object.
162+
* The scoped connection will no longer refer to / disconnect the slot.
163+
* @return An unscoped connection object referring to the same slot.
164+
*/
165+
[[nodiscard]] connection release() noexcept;
166+
167+
private:
168+
sigc::connection conn_;
169+
};
170+
171+
void swap(scoped_connection &sca, scoped_connection &scb) noexcept;
172+
173+
} /* namespace sigc */
174+
175+
#endif /* SIGC_SCOPED_CONNECTION_HPP */

sigc++/sigc++.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117

118118
#include <sigc++/signal.h>
119119
#include <sigc++/connection.h>
120+
#include <sigc++/scoped_connection.h>
120121
#include <sigc++/trackable.h>
121122
#include <sigc++/adaptors/adaptors.h>
122123
#include <sigc++/functors/functors.h>

tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ check_PROGRAMS = \
4747
test_retype \
4848
test_retype_return \
4949
test_rvalue_ref \
50+
test_scoped_connection \
5051
test_signal \
5152
test_signal_move \
5253
test_size \

tests/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ test_programs = [
2929
[[], 'test_retype', ['test_retype.cc', 'testutilities.cc']],
3030
[[], 'test_retype_return', ['test_retype_return.cc', 'testutilities.cc']],
3131
[[], 'test_rvalue_ref', ['test_rvalue_ref.cc', 'testutilities.cc']],
32+
[[], 'test_scoped_connection', ['test_scoped_connection.cc', 'testutilities.cc']],
3233
[[], 'test_signal', ['test_signal.cc', 'testutilities.cc']],
3334
[[], 'test_signal_move', ['test_signal_move.cc', 'testutilities.cc']],
3435
[[], 'test_size', ['test_size.cc', 'testutilities.cc']],

0 commit comments

Comments
 (0)