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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy