Skip to content

Commit 7d85510

Browse files
slavaandrejevkjellahl
authored andcommitted
Allow slots with rvalue reference parameters
In code based on Glibmm it's quite common to convert a Glib C structure to a Glib::Object's descendant before passing it as a parameter to a slot. If the slot is not supposed to modify the object, then everything is fine, we can declare the parameter as a const reference and the temporary object will be accurately dealt with by the compiler. However, if the Glib based object is getting modified, the only way to pass it to the slot is to declare it a Glib::RefPtr shared pointer. This creates a lot of unnecessary churn of memory allocations for a simple deal of calling a signal. However, C++ has a specific mean for this particular semantic of passing a temporary object: rvalue reference. For example, somewhere inside Gtkmm might be a declaration: _WRAP_SIGNAL(void sun_rose(const Glib::RefPtr<Gtk::Sun> &new_sun), "sun-rose") Instead its more semantically correct to write: _WRAP_SIGNAL(void sun_rose(Gtk::Sun &&new_sun), "sun-rose") And later somewhere in your code: world->signal_sun_rose().connect([&](auto &&new_sun) { new_sun.shine(); }); This commit makes a couple of simple modifications that allow declaring signals and slots with rvalue reference parameter.
1 parent 657b1b1 commit 7d85510

File tree

6 files changed

+75
-7
lines changed

6 files changed

+75
-7
lines changed

sigc++/functors/slot.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ struct slot_call
151151
static T_return call_it(slot_rep* rep, type_trait_take_t<T_arg>... a_)
152152
{
153153
auto typed_rep = static_cast<typed_slot_rep<T_functor>*>(rep);
154-
return (*typed_rep->functor_).template operator()<type_trait_take_t<T_arg>...>(a_...);
154+
return (*typed_rep->functor_).template operator()<type_trait_take_t<T_arg>...>(
155+
std::forward<type_trait_take_t<T_arg>>(a_)...);
155156
}
156157

157158
/** Forms a function pointer from call_it().
@@ -220,7 +221,7 @@ class slot<T_return(T_arg...)> : public slot_base
220221
{
221222
return std::invoke(sigc::internal::function_pointer_cast<call_type>(slot_base::rep_->call_),
222223
slot_base::rep_,
223-
a...);
224+
std::forward<type_trait_take_t<T_arg>>(a)...);
224225
}
225226

226227
return T_return();

sigc++/signal.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,9 @@ struct signal_emit<void, void, T_arg...>
363363
if (slot.empty() || slot.blocked())
364364
continue;
365365

366-
(sigc::internal::function_pointer_cast<call_type>(slot.rep_->call_))(slot.rep_, a...);
366+
(sigc::internal::function_pointer_cast<call_type>(slot.rep_->call_))(
367+
slot.rep_,
368+
std::forward<type_trait_take_t<T_arg>>(a)...);
367369
}
368370
}
369371
};
@@ -450,11 +452,13 @@ class signal_with_accumulator : public signal_base
450452
decltype(auto) emit(type_trait_take_t<T_arg>... a) const
451453
{
452454
using emitter_type = internal::signal_emit<T_return, T_accumulator, T_arg...>;
453-
return emitter_type::emit(impl_, a...);
455+
return emitter_type::emit(impl_, std::forward<type_trait_take_t<T_arg>>(a)...);
454456
}
455457

456458
/** Triggers the emission of the signal (see emit()). */
457-
decltype(auto) operator()(type_trait_take_t<T_arg>... a) const { return emit(a...); }
459+
decltype(auto) operator()(type_trait_take_t<T_arg>... a) const {
460+
return emit(std::forward<type_trait_take_t<T_arg>>(a)...);
461+
}
458462

459463
/** Creates a functor that calls emit() on this signal.
460464
* @code

sigc++/type_traits.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ struct type_trait<const T_type&>
5252
using take = const T_type&;
5353
};
5454

55+
template<typename T_type>
56+
struct type_trait<T_type&&>
57+
{
58+
using pass = T_type&&;
59+
using take = T_type&&;
60+
};
61+
5562
template<>
5663
struct type_trait<void>
5764
{

tests/memleakcheck.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ for testprog in test_accum_iter test_accumulated test_bind test_bind_as_slot \
99
test_copy_invalid_slot test_cpp11_lambda test_custom test_disconnect \
1010
test_disconnect_during_emit test_exception_catch test_hide \
1111
test_limit_reference test_member_method_trait test_mem_fun test_ptr_fun \
12-
test_retype test_retype_return test_signal test_signal_move test_size \
13-
test_slot test_slot_disconnect test_slot_move test_trackable \
12+
test_retype test_retype_return test_rvalue_ref test_signal test_signal_move \
13+
test_size test_slot test_slot_disconnect test_slot_move test_trackable \
1414
test_trackable_move test_track_obj test_tuple_cdr test_tuple_end \
1515
test_tuple_for_each test_tuple_start test_tuple_transform_each \
1616
test_visit_each test_visit_each_trackable test_weak_raw_ptr

tests/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ test_programs = [
2828
[[], 'test_ptr_fun', ['test_ptr_fun.cc', 'testutilities.cc']],
2929
[[], 'test_retype', ['test_retype.cc', 'testutilities.cc']],
3030
[[], 'test_retype_return', ['test_retype_return.cc', 'testutilities.cc']],
31+
[[], 'test_rvalue_ref', ['test_rvalue_ref.cc', 'testutilities.cc']],
3132
[[], 'test_signal', ['test_signal.cc', 'testutilities.cc']],
3233
[[], 'test_signal_move', ['test_signal_move.cc', 'testutilities.cc']],
3334
[[], 'test_size', ['test_size.cc', 'testutilities.cc']],

tests/test_rvalue_ref.cc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "testutilities.h"
2+
#include <iostream>
3+
#include <sigc++/signal.h>
4+
5+
struct MoveableStruct {};
6+
7+
namespace
8+
{
9+
TestUtilities* util = nullptr;
10+
std::ostringstream result_stream;
11+
12+
struct foo
13+
{
14+
void operator()(MoveableStruct &&x)
15+
{
16+
result_stream << "foo(MoveableStruct&&)";
17+
}
18+
};
19+
20+
} // end anonymous namespace
21+
22+
void
23+
test_signal()
24+
{
25+
sigc::signal<void (MoveableStruct &&)> signal;
26+
foo f;
27+
signal.connect(f);
28+
MoveableStruct x;
29+
signal(std::move(x));
30+
util->check_result(result_stream, "foo(MoveableStruct&&)");
31+
}
32+
33+
void
34+
test_slot()
35+
{
36+
sigc::slot<void (MoveableStruct &&)> slot;
37+
foo f;
38+
slot = f;
39+
MoveableStruct x;
40+
slot(std::move(x));
41+
util->check_result(result_stream, "foo(MoveableStruct&&)");
42+
}
43+
44+
int
45+
main(int argc, char* argv[])
46+
{
47+
util = TestUtilities::get_instance();
48+
if (!util->check_command_args(argc, argv))
49+
return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE;
50+
51+
test_signal();
52+
test_slot();
53+
54+
return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE;
55+
} // end main()

0 commit comments

Comments
 (0)