Skip to content

Commit e783285

Browse files
committed
Replace macro approach with IndirectSignalReceiver to force type inference to work.
- Replace macro approach with FnTraits for handling signal receivers. - Restore `variadic.rs` as `signal_receiver.rs`. - Introduce `IndirectSignalReceiver` – workaround used to force type inference to work.
1 parent 9c94abf commit e783285

File tree

4 files changed

+416
-237
lines changed

4 files changed

+416
-237
lines changed

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

Lines changed: 155 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ use super::{make_callable_name, make_godot_fn};
99
use crate::builtin::{Callable, GString, Variant};
1010
use crate::classes::object::ConnectFlags;
1111
use crate::meta;
12-
use crate::meta::FromGodot;
12+
use crate::meta::InParamTuple;
1313
use crate::obj::{bounds, Bounds, Gd, GodotClass, WithSignals};
14+
use crate::registry::signal::signal_receiver::{IndirectSignalReceiver, SignalReceiver};
1415
use crate::registry::signal::{ToSignalObj, TypedSignal};
15-
use std::fmt::Debug;
1616

1717
/// Builder for customizing signal connections.
1818
///
@@ -137,172 +137,173 @@ where
137137
}
138138
}
139139

140-
macro_rules! impl_builder_connect {
141-
($( #[$attr:meta] )? $( $args:ident : $Ps:ident ),*) => {
142-
$( #[$attr] )?
143-
impl<C: WithSignals, $($Ps: Debug + FromGodot + 'static),*>
144-
ConnectBuilder<'_, '_, C, ($($Ps,)*)> {
145-
/// Connect a non-member function (global function, associated function or closure).
146-
///
147-
/// Example usages:
148-
/// ```ignore
149-
/// sig.builder().connect(Self::static_func);
150-
/// sig.builder().flags(ConnectFlags::DEFERRED).connect(global_func);
151-
/// sig.connect(|arg| { /* closure */ });
152-
/// ```
153-
///
154-
/// # Related APIs
155-
/// - To connect to a method on the object that owns this signal, use [`connect_self_mut()`][Self::connect_self_mut] or
156-
/// [`connect_self_gd()`][Self::connect_self_gd].
157-
/// - To connect to methods on other objects, use [`connect_other_mut()`][Self::connect_other_mut] or
158-
/// [`connect_other_gd()`][Self::connect_other_gd].
159-
/// - If you need [`connect flags`](ConnectFlags), call [`flags()`](Self::flags) before this.
160-
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
161-
pub fn connect<F, R>(self, mut function: F)
162-
where
163-
F: FnMut($($Ps),*) -> R + 'static,
164-
{
165-
let godot_fn = make_godot_fn(move |($($args,)*):($($Ps,)*)| {
166-
function($($args),*);
167-
});
140+
impl<C: WithSignals, Ps: InParamTuple + 'static> ConnectBuilder<'_, '_, C, Ps> {
141+
/// Connect a non-member function (global function, associated function or closure).
142+
///
143+
/// Example usages:
144+
/// ```ignore
145+
/// sig.builder().connect(Self::static_func);
146+
/// sig.builder().flags(ConnectFlags::DEFERRED).connect(global_func);
147+
/// sig.connect(|arg| { /* closure */ });
148+
/// ```
149+
///
150+
/// # Related APIs
151+
/// - To connect to a method on the object that owns this signal, use [`connect_self_mut()`][Self::connect_self_mut] or
152+
/// [`connect_self_gd()`][Self::connect_self_gd].
153+
/// - To connect to methods on other objects, use [`connect_other_mut()`][Self::connect_other_mut] or
154+
/// [`connect_other_gd()`][Self::connect_other_gd].
155+
/// - If you need [`connect flags`](ConnectFlags), call [`flags()`](Self::flags) before this.
156+
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
157+
pub fn connect<F>(self, mut function: F)
158+
where
159+
for<'c_rcv> F: SignalReceiver<(), Ps>,
160+
for<'c_rcv> IndirectSignalReceiver<'c_rcv, (), Ps, F>: From<&'c_rcv mut F>,
161+
{
162+
let godot_fn = make_godot_fn(move |args| {
163+
IndirectSignalReceiver::from(&mut function)
164+
.function()
165+
.call((), args);
166+
});
168167

169-
self.inner_connect_godot_fn::<F>(godot_fn);
170-
}
168+
self.inner_connect_godot_fn::<F>(godot_fn);
169+
}
171170

172-
/// Connect a method with `&mut self` as the first parameter (user classes only).
173-
///
174-
/// # Related APIs
175-
/// - Use [`connect_self_gd()`][Self::connect_self_gd] to receive `Gd<Self>` instead and avoid implicit `bind_mut()` on emit. \
176-
/// For engine classes, `&mut self` is not supported at all.
177-
/// - To connect to methods on other objects, use [`connect_other_mut()`][Self::connect_other_mut].
178-
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
179-
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature `experimental-threads`).
180-
pub fn connect_self_mut<F, R>(self, mut function: F)
181-
where
182-
C: Bounds<Declarer = bounds::DeclUser>,
183-
F: FnMut(&mut C, $($Ps),*) -> R + 'static,
184-
{
185-
let mut gd = self.parent_sig.receiver_object();
171+
/// Connect a method with `&mut self` as the first parameter (user classes only).
172+
///
173+
/// # Related APIs
174+
/// - Use [`connect_self_gd()`][Self::connect_self_gd] to receive `Gd<Self>` instead and avoid implicit `bind_mut()` on emit. \
175+
/// For engine classes, `&mut self` is not supported at all.
176+
/// - To connect to methods on other objects, use [`connect_other_mut()`][Self::connect_other_mut].
177+
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
178+
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature `experimental-threads`).
179+
pub fn connect_self_mut<F>(self, mut function: F)
180+
where
181+
C: Bounds<Declarer = bounds::DeclUser>,
182+
for<'c_rcv> F: SignalReceiver<&'c_rcv mut C, Ps>,
183+
for<'c_rcv> IndirectSignalReceiver<'c_rcv, &'c_rcv mut C, Ps, F>: From<&'c_rcv mut F>,
184+
{
185+
let mut gd = self.parent_sig.receiver_object();
186186

187-
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
188-
let mut guard = Gd::bind_mut(&mut gd);
189-
function(&mut *guard, $($args),*);
190-
});
187+
let godot_fn = make_godot_fn(move |args| {
188+
let mut guard = Gd::bind_mut(&mut gd);
189+
IndirectSignalReceiver::from(&mut function)
190+
.function()
191+
.call(&mut *guard, args);
192+
});
191193

192-
self.inner_connect_godot_fn::<F>(godot_fn);
193-
}
194+
self.inner_connect_godot_fn::<F>(godot_fn);
195+
}
194196

195-
/// Connect a method with `&mut Gd<Self>` as the first parameter (user + engine classes).
196-
///
197-
/// # Related APIs
198-
/// - If your class `C` is user-defined and you'd like to have an automatic `bind_mut()` and receive `&mut self`, then
199-
/// use [`connect_self_mut()`][Self::connect_self_mut] instead.
200-
/// - To connect to methods on other objects, use [`connect_other_gd()`][Self::connect_other_gd].
201-
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
202-
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature `experimental-threads`).
203-
pub fn connect_self_gd<F, R>(self, mut function: F)
204-
where
205-
F: FnMut(Gd<C>, $($Ps),*) -> R + 'static,
206-
{
207-
let gd = self.parent_sig.receiver_object();
197+
/// Connect a method with `&mut Gd<Self>` as the first parameter (user + engine classes).
198+
///
199+
/// # Related APIs
200+
/// - If your class `C` is user-defined and you'd like to have an automatic `bind_mut()` and receive `&mut self`, then
201+
/// use [`connect_self_mut()`][Self::connect_self_mut] instead.
202+
/// - To connect to methods on other objects, use [`connect_other_gd()`][Self::connect_other_gd].
203+
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
204+
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature `experimental-threads`).
205+
pub fn connect_self_gd<F>(self, mut function: F)
206+
where
207+
F: SignalReceiver<Gd<C>, Ps>,
208+
for<'c_rcv> IndirectSignalReceiver<'c_rcv, Gd<C>, Ps, F>: From<&'c_rcv mut F>,
209+
{
210+
let gd = self.parent_sig.receiver_object();
208211

209-
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
210-
function(gd.clone(), $($args),*);
211-
});
212+
let godot_fn = make_godot_fn(move |args| {
213+
IndirectSignalReceiver::from(&mut function)
214+
.function()
215+
.call(gd.clone(), args);
216+
});
212217

213-
self.inner_connect_godot_fn::<F>(godot_fn);
214-
}
218+
self.inner_connect_godot_fn::<F>(godot_fn);
219+
}
215220

216-
/// Connect a method with any `&mut OtherC` as the first parameter (user classes only).
217-
///
218-
/// The parameter `object` can be of 2 different "categories":
219-
/// - Any `&Gd<OtherC>` (e.g.: `&Gd<Node>`, `&Gd<MyClass>`).
220-
/// - `&OtherC`, as long as `OtherC` is a user class that contains a `Base<T>` field (it implements the
221-
/// [`WithBaseField`](crate::obj::WithBaseField) trait).
222-
///
223-
/// # Related APIs
224-
/// - Use [`connect_other_gd()`][Self::connect_other_gd] to receive `Gd<Self>` instead and avoid implicit `bind_mut()` on emit. \
225-
/// For engine classes, `&mut self` is not supported at all.
226-
/// - To connect to methods on the object that owns this signal, use [`connect_self_mut()`][Self::connect_self_mut].
227-
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
228-
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
229-
pub fn connect_other_mut<F, R, OtherC>(self, object: &impl ToSignalObj<OtherC>, mut method: F)
230-
where
231-
OtherC: GodotClass + Bounds<Declarer = bounds::DeclUser>,
232-
F: FnMut(&mut OtherC, $($Ps),*) -> R + 'static,
233-
{
234-
let mut gd = object.to_signal_obj();
221+
/// Connect a method with any `&mut OtherC` as the first parameter (user classes only).
222+
///
223+
/// The parameter `object` can be of 2 different "categories":
224+
/// - Any `&Gd<OtherC>` (e.g.: `&Gd<Node>`, `&Gd<MyClass>`).
225+
/// - `&OtherC`, as long as `OtherC` is a user class that contains a `Base<T>` field (it implements the
226+
/// [`WithBaseField`](crate::obj::WithBaseField) trait).
227+
///
228+
/// # Related APIs
229+
/// - Use [`connect_other_gd()`][Self::connect_other_gd] to receive `Gd<Self>` instead and avoid implicit `bind_mut()` on emit. \
230+
/// For engine classes, `&mut self` is not supported at all.
231+
/// - To connect to methods on the object that owns this signal, use [`connect_self_mut()`][Self::connect_self_mut].
232+
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
233+
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
234+
pub fn connect_other_mut<F, OtherC>(self, object: &impl ToSignalObj<OtherC>, mut method: F)
235+
where
236+
OtherC: GodotClass + Bounds<Declarer = bounds::DeclUser>,
237+
for<'c_rcv> F: SignalReceiver<&'c_rcv mut OtherC, Ps>,
238+
for<'c_rcv> IndirectSignalReceiver<'c_rcv, &'c_rcv mut OtherC, Ps, F>: From<&'c_rcv mut F>,
239+
{
240+
let mut gd = object.to_signal_obj();
235241

236-
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
237-
let mut guard = Gd::bind_mut(&mut gd);
238-
method(&mut *guard, $($args),*);
239-
});
242+
let godot_fn = make_godot_fn(move |args| {
243+
let mut guard = Gd::bind_mut(&mut gd);
244+
IndirectSignalReceiver::from(&mut method)
245+
.function()
246+
.call(&mut *guard, args);
247+
});
240248

241-
self.inner_connect_godot_fn::<F>(godot_fn);
242-
}
249+
self.inner_connect_godot_fn::<F>(godot_fn);
250+
}
243251

244-
/// Connect a method with any `&mut Gd<OtherC>` as the first parameter (user + engine classes).
245-
///
246-
/// The parameter `object` can be of 2 different "categories":
247-
/// - Any `&Gd<OtherC>` (e.g.: `&Gd<Node>`, `&Gd<MyClass>`).
248-
/// - `&OtherC`, as long as `OtherC` is a user class that contains a `Base<T>` field (it implements the
249-
/// [`WithBaseField`](crate::obj::WithBaseField) trait).
250-
///
251-
/// # Related APIs
252-
/// - To connect to methods on the object that owns this signal, use [`connect_self_gd()`][Self::connect_self_gd].
253-
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
254-
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
255-
pub fn connect_other_gd<F, R, OtherC>(self, object: &impl ToSignalObj<OtherC>, mut method: F)
256-
where
257-
OtherC: GodotClass,
258-
F: FnMut(Gd<OtherC>, $($Ps),*) -> R + 'static,
259-
{
260-
let gd = object.to_signal_obj();
252+
/// Connect a method with any `&mut Gd<OtherC>` as the first parameter (user + engine classes).
253+
///
254+
/// The parameter `object` can be of 2 different "categories":
255+
/// - Any `&Gd<OtherC>` (e.g.: `&Gd<Node>`, `&Gd<MyClass>`).
256+
/// - `&OtherC`, as long as `OtherC` is a user class that contains a `Base<T>` field (it implements the
257+
/// [`WithBaseField`](crate::obj::WithBaseField) trait).
258+
///
259+
/// # Related APIs
260+
/// - To connect to methods on the object that owns this signal, use [`connect_self_gd()`][Self::connect_self_gd].
261+
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
262+
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
263+
pub fn connect_other_gd<F, OtherC>(self, object: &impl ToSignalObj<OtherC>, mut method: F)
264+
where
265+
OtherC: GodotClass,
266+
F: SignalReceiver<Gd<OtherC>, Ps>,
267+
for<'c_rcv> IndirectSignalReceiver<'c_rcv, Gd<OtherC>, Ps, F>: From<&'c_rcv mut F>,
268+
{
269+
let gd = object.to_signal_obj();
261270

262-
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
263-
method(gd.clone(), $($args),*);
264-
});
271+
let godot_fn = make_godot_fn(move |args| {
272+
IndirectSignalReceiver::from(&mut method)
273+
.function()
274+
.call(gd.clone(), args);
275+
});
265276

266-
self.inner_connect_godot_fn::<F>(godot_fn);
267-
}
277+
self.inner_connect_godot_fn::<F>(godot_fn);
278+
}
268279

269-
/// Connect to this signal using a thread-safe function, allows the signal to be called across threads.
270-
///
271-
/// Requires `Send` + `Sync` bounds on the provided function `F`, and is only available for the `experimental-threads`
272-
/// Cargo feature.
273-
///
274-
/// If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
275-
#[cfg(feature = "experimental-threads")]
276-
pub fn connect_sync<F, R>(self, mut function: F)
277-
where
278-
// Why both Send+Sync: closure can not only impact another thread (Sync), but it's also possible to share such Callables across threads
279-
// (Send) or even call them from multiple threads (Sync). We don't differentiate the fine-grained needs, it's either thread-safe or not.
280-
F: FnMut($($Ps),*) -> R + Send + Sync + 'static,
281-
{
282-
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
283-
function($($args),*);
284-
});
280+
/// Connect to this signal using a thread-safe function, allows the signal to be called across threads.
281+
///
282+
/// Requires `Send` + `Sync` bounds on the provided function `F`, and is only available for the `experimental-threads`
283+
/// Cargo feature.
284+
///
285+
/// If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
286+
#[cfg(feature = "experimental-threads")]
287+
pub fn connect_sync<F>(self, mut function: F)
288+
where
289+
// Why both Send+Sync: closure can not only impact another thread (Sync), but it's also possible to share such Callables across threads
290+
// (Send) or even call them from multiple threads (Sync). We don't differentiate the fine-grained needs, it's either thread-safe or not.
291+
for<'c_rcv> F: SignalReceiver<(), Ps> + Send + Sync,
292+
for<'c_rcv> IndirectSignalReceiver<'c_rcv, (), Ps, F>: From<&'c_rcv mut F>,
293+
{
294+
let godot_fn = make_godot_fn(move |args| {
295+
IndirectSignalReceiver::from(&mut function)
296+
.function()
297+
.call((), args);
298+
});
285299

286-
let callable_name = match &self.data.callable_name {
287-
Some(user_provided_name) => user_provided_name,
288-
None => &make_callable_name::<F>(),
289-
};
300+
let callable_name = match &self.data.callable_name {
301+
Some(user_provided_name) => user_provided_name,
302+
None => &make_callable_name::<F>(),
303+
};
290304

291-
let callable = Callable::from_sync_fn(callable_name, godot_fn);
292-
self.parent_sig.inner_connect_untyped(&callable, self.data.connect_flags);
293-
}
294-
}
295-
};
305+
let callable = Callable::from_sync_fn(callable_name, godot_fn);
306+
self.parent_sig
307+
.inner_connect_untyped(&callable, self.data.connect_flags);
308+
}
296309
}
297-
298-
impl_builder_connect!(#[doc(hidden)] );
299-
impl_builder_connect!(#[doc(hidden)] arg0: P0);
300-
impl_builder_connect!(#[doc(hidden)] arg0: P0, arg1: P1);
301-
impl_builder_connect!( arg0: P0, arg1: P1, arg2: P2);
302-
impl_builder_connect!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3);
303-
impl_builder_connect!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4);
304-
impl_builder_connect!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5);
305-
impl_builder_connect!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5, arg6: P6);
306-
impl_builder_connect!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5, arg6: P6, arg7: P7);
307-
impl_builder_connect!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5, arg6: P6, arg7: P7, arg8: P8);
308-
impl_builder_connect!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5, arg6: P6, arg7: P7, arg8: P8, arg9: P9);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
mod connect_builder;
1111
mod signal_object;
12+
mod signal_receiver;
1213
mod typed_signal;
1314

1415
use crate::builtin::{GString, Variant};
@@ -20,6 +21,8 @@ pub(crate) use typed_signal::*;
2021
// Used in `godot` crate.
2122
pub mod re_export {
2223
pub use super::connect_builder::ConnectBuilder;
24+
pub use super::signal_receiver::IndirectSignalReceiver;
25+
pub use super::signal_receiver::SignalReceiver;
2326
pub use super::typed_signal::TypedSignal;
2427
}
2528

0 commit comments

Comments
 (0)