Skip to content

Commit cf98df3

Browse files
committed
Reduce code duplication in connect overloads
1 parent cb6577f commit cf98df3

File tree

3 files changed

+75
-79
lines changed

3 files changed

+75
-79
lines changed

godot-core/src/registry/signal/connect_builder.rs

+38-31
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
use crate::builtin::{Callable, GString, Variant};
99
use crate::classes::object::ConnectFlags;
10+
use crate::meta;
1011
use crate::obj::{bounds, Bounds, Gd, GodotClass, WithBaseField};
1112
use crate::registry::signal::{SignalReceiver, TypedSignal};
12-
use crate::{meta, sys};
1313

1414
/// Type-state builder for customizing signal connections.
1515
///
@@ -88,19 +88,13 @@ impl<'ts, 'c, CSig: WithBaseField, Ps: meta::ParamTuple> ConnectBuilder<'ts, 'c,
8888
where
8989
F: SignalReceiver<(), Ps>,
9090
{
91-
let godot_fn = move |variant_args: &[&Variant]| -> Result<Variant, ()> {
92-
let args = Ps::from_variant_array(variant_args);
91+
let godot_fn = make_godot_fn(move |args| {
9392
function.call((), args);
94-
95-
Ok(Variant::nil())
96-
};
97-
98-
let mut data = self.data;
99-
data.callable_name = Some(sys::short_type_name::<F>().into());
93+
});
10094

10195
ConnectBuilder {
10296
parent_sig: self.parent_sig,
103-
data,
97+
data: self.data.with_callable_name::<F>(),
10498
godot_fn,
10599
receiver_obj: (),
106100
}
@@ -153,22 +147,15 @@ impl<'ts, 'c, CSig: WithBaseField, CRcv: GodotClass, Ps: meta::ParamTuple>
153147
for<'c_rcv> F: SignalReceiver<&'c_rcv mut CRcv, Ps>,
154148
{
155149
let mut gd: Gd<CRcv> = self.receiver_obj;
156-
157-
let godot_fn = move |variant_args: &[&Variant]| -> Result<Variant, ()> {
158-
let args = Ps::from_variant_array(variant_args);
150+
let godot_fn = make_godot_fn(move |args| {
159151
let mut guard = gd.bind_mut();
160152
let instance = &mut *guard;
161153
method_with_mut_self.call(instance, args);
162-
163-
Ok(Variant::nil())
164-
};
165-
166-
let mut data = self.data;
167-
data.callable_name = Some(sys::short_type_name::<F>().into());
154+
});
168155

169156
ConnectBuilder {
170157
parent_sig: self.parent_sig,
171-
data,
158+
data: self.data.with_callable_name::<F>(),
172159
godot_fn,
173160
receiver_obj: (),
174161
}
@@ -192,22 +179,15 @@ impl<'ts, 'c, CSig: WithBaseField, CRcv: GodotClass, Ps: meta::ParamTuple>
192179
for<'c_rcv> F: SignalReceiver<&'c_rcv CRcv, Ps>,
193180
{
194181
let gd: Gd<CRcv> = self.receiver_obj;
195-
196-
let godot_fn = move |variant_args: &[&Variant]| -> Result<Variant, ()> {
197-
let args = Ps::from_variant_array(variant_args);
182+
let godot_fn = make_godot_fn(move |args| {
198183
let guard = gd.bind();
199184
let instance = &*guard;
200185
method_with_shared_self.call(instance, args);
201-
202-
Ok(Variant::nil())
203-
};
204-
205-
let mut data = self.data;
206-
data.callable_name = Some(sys::short_type_name::<F>().into());
186+
});
207187

208188
ConnectBuilder {
209189
parent_sig: self.parent_sig,
210-
data,
190+
data: self.data.with_callable_name::<F>(),
211191
godot_fn,
212192
receiver_obj: (),
213193
}
@@ -315,7 +295,7 @@ where
315295
#[cfg(not(feature = "experimental-threads"))]
316296
let callable = Callable::from_local_fn(callable_name, godot_fn);
317297

318-
parent_sig.connect_untyped(&callable, data.connect_flags);
298+
parent_sig.inner_connect_untyped(&callable, data.connect_flags);
319299
}
320300
}
321301

@@ -336,9 +316,36 @@ struct BuilderData {
336316
}
337317

338318
impl BuilderData {
319+
fn with_callable_name<F>(mut self) -> Self {
320+
self.callable_name = Some(make_callable_name::<F>());
321+
self
322+
}
323+
339324
fn callable_name_ref(&self) -> &GString {
340325
self.callable_name
341326
.as_ref()
342327
.expect("Signal connect name not set; this is a bug.")
343328
}
344329
}
330+
331+
// ----------------------------------------------------------------------------------------------------------------------------------------------
332+
333+
pub(super) fn make_godot_fn<Ps, F>(mut input: F) -> impl FnMut(&[&Variant]) -> Result<Variant, ()>
334+
where
335+
F: FnMut(Ps),
336+
Ps: meta::ParamTuple,
337+
{
338+
move |variant_args: &[&Variant]| -> Result<Variant, ()> {
339+
let args = Ps::from_variant_array(variant_args);
340+
input(args);
341+
342+
Ok(Variant::nil())
343+
}
344+
}
345+
346+
pub(super) fn make_callable_name<F>() -> GString {
347+
// When using sys::short_type_name() in the future, make sure global "func" and member "MyClass::func" are rendered as such.
348+
// PascalCase heuristic should then be good enough.
349+
350+
std::any::type_name::<F>().into()
351+
}

godot-core/src/registry/signal/typed_signal.rs

+35-46
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8-
use crate::builtin::{Callable, GString, Variant};
8+
use crate::builtin::{Callable, Variant};
99
use crate::classes::object::ConnectFlags;
1010
use crate::obj::{bounds, Bounds, Gd, GodotClass, WithBaseField};
11-
use crate::registry::signal::{ConnectBuilder, SignalReceiver};
11+
use crate::registry::signal::{make_callable_name, make_godot_fn, ConnectBuilder, SignalReceiver};
1212
use crate::{classes, meta};
1313
use std::borrow::Cow;
1414
use std::marker::PhantomData;
@@ -117,16 +117,11 @@ impl<'c, C: WithBaseField, Ps: meta::ParamTuple> TypedSignal<'c, C, Ps> {
117117
where
118118
F: SignalReceiver<(), Ps>,
119119
{
120-
let callable_name = std::any::type_name_of_val(&function);
121-
122-
let godot_fn = move |variant_args: &[&Variant]| -> Result<Variant, ()> {
123-
let args = Ps::from_variant_array(variant_args);
120+
let godot_fn = make_godot_fn(move |args| {
124121
function.call((), args);
122+
});
125123

126-
Ok(Variant::nil())
127-
};
128-
129-
self.inner_connect_local(callable_name, godot_fn);
124+
self.inner_connect_godot_fn::<F>(godot_fn);
130125
}
131126

132127
/// Connect a method (member function) with `&mut self` as the first parameter.
@@ -137,27 +132,14 @@ impl<'c, C: WithBaseField, Ps: meta::ParamTuple> TypedSignal<'c, C, Ps> {
137132
where
138133
for<'c_rcv> F: SignalReceiver<&'c_rcv mut C, Ps>,
139134
{
140-
// When using sys::short_type_name() in the future, make sure global "func" and member "MyClass::func" are rendered as such.
141-
// PascalCase heuristic should then be good enough.
142-
let callable_name = std::any::type_name_of_val(&function);
143-
144-
let object = self.owner.to_owned();
145-
let godot_fn = move |variant_args: &[&Variant]| -> Result<Variant, ()> {
146-
let args = Ps::from_variant_array(variant_args);
147-
148-
// let mut function = function;
149-
// function.call(instance, args);
150-
let mut object = object.clone();
151-
152-
// TODO: how to avoid another bind, when emitting directly from Rust?
153-
let mut instance = object.bind_mut();
135+
let mut gd = self.owner.to_owned();
136+
let godot_fn = make_godot_fn(move |args| {
137+
let mut instance = gd.bind_mut();
154138
let instance = &mut *instance;
155139
function.call(instance, args);
140+
});
156141

157-
Ok(Variant::nil())
158-
};
159-
160-
self.inner_connect_local(callable_name, godot_fn);
142+
self.inner_connect_godot_fn::<F>(godot_fn);
161143
}
162144

163145
/// Connect a method (member function) with any `Gd<T>` (not `self`) as the first parameter.
@@ -169,20 +151,14 @@ impl<'c, C: WithBaseField, Ps: meta::ParamTuple> TypedSignal<'c, C, Ps> {
169151
OtherC: GodotClass + Bounds<Declarer = bounds::DeclUser>,
170152
for<'c_rcv> F: SignalReceiver<&'c_rcv mut OtherC, Ps>,
171153
{
172-
let callable_name = std::any::type_name_of_val(&function);
173-
174-
let mut object = object.clone();
175-
let godot_fn = move |variant_args: &[&Variant]| -> Result<Variant, ()> {
176-
let args = Ps::from_variant_array(variant_args);
177-
178-
let mut instance = object.bind_mut();
154+
let mut gd = object.clone();
155+
let godot_fn = make_godot_fn(move |args| {
156+
let mut instance = gd.bind_mut();
179157
let instance = &mut *instance;
180158
function.call(instance, args);
159+
});
181160

182-
Ok(Variant::nil())
183-
};
184-
185-
self.inner_connect_local(callable_name, godot_fn);
161+
self.inner_connect_godot_fn::<F>(godot_fn);
186162
}
187163

188164
/// Fully customizable connection setup.
@@ -193,19 +169,32 @@ impl<'c, C: WithBaseField, Ps: meta::ParamTuple> TypedSignal<'c, C, Ps> {
193169
ConnectBuilder::new(self)
194170
}
195171

196-
fn inner_connect_local<F>(&mut self, callable_name: impl meta::AsArg<GString>, godot_fn: F)
197-
where
198-
F: FnMut(&[&Variant]) -> Result<Variant, ()> + 'static,
199-
{
200-
let signal_name = self.name.as_ref();
201-
let callable = Callable::from_local_fn(callable_name, godot_fn);
172+
/// Directly connect a Rust callable `godot_fn`, with a name based on `F`.
173+
///
174+
/// This exists as a short-hand for the connect methods on [`TypedSignal`] and avoids the generic instantiation of the full-blown
175+
/// type state builder for simple + common connections, thus hopefully being a tiny bit lighter on compile times.
176+
fn inner_connect_godot_fn<F>(
177+
&mut self,
178+
godot_fn: impl FnMut(&[&Variant]) -> Result<Variant, ()> + 'static,
179+
) {
180+
let callable_name = make_callable_name::<F>();
181+
let callable = Callable::from_local_fn(&callable_name, godot_fn);
202182

183+
let signal_name = self.name.as_ref();
203184
self.owner.with_object_mut(|obj| {
204185
obj.connect(signal_name, &callable);
205186
});
206187
}
207188

208-
pub(super) fn connect_untyped(&mut self, callable: &Callable, flags: Option<ConnectFlags>) {
189+
/// Connect an untyped callable, with optional flags.
190+
///
191+
/// Used by [`ConnectBuilder::done()`]. Any other type-state (such as thread-local/sync, callable debug name, etc.) are baked into
192+
/// `callable` and thus type-erased into runtime logic.
193+
pub(super) fn inner_connect_untyped(
194+
&mut self,
195+
callable: &Callable,
196+
flags: Option<ConnectFlags>,
197+
) {
209198
use crate::obj::EngineBitfield;
210199

211200
let signal_name = self.name.as_ref();

godot-core/src/registry/signal/variadic.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub trait SignalReceiver<I, Ps>: 'static {
3232
///
3333
/// Each tuple element is one parameter. This trait provides conversions to and from `Variant` arrays.
3434
// Re-exported under crate::meta. Might be worth splitting, but depends a bit on SignatureVarcall/Ptrcall refactoring.
35-
pub trait ParamTuple {
35+
pub trait ParamTuple: 'static {
3636
fn to_variant_array(&self) -> Vec<Variant>;
3737
fn from_variant_array(array: &[&Variant]) -> Self;
3838
}
@@ -47,7 +47,7 @@ macro_rules! impl_signal_recipient {
4747

4848
impl<$($Ps),*> ParamTuple for ($($Ps,)*)
4949
where
50-
$($Ps: meta::ToGodot + meta::FromGodot),*
50+
$($Ps: meta::ToGodot + meta::FromGodot + 'static),*
5151
{
5252
fn to_variant_array(&self) -> Vec<Variant> {
5353
let ($($args,)*) = self;

0 commit comments

Comments
 (0)