godot_core/registry/signal/signal_receiver.rs
1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8//! Emulates variadic argument lists (via tuples), related to functions and signals.
9// https://geo-ant.github.io/blog/2021/rust-traits-and-variadic-functions
10//
11// Could be generalized with R return type, and not special-casing `self`. But keep simple until actually needed.
12
13use std::marker::PhantomData;
14
15use crate::meta::{FromGodot, InParamTuple};
16use crate::obj::{Gd, GodotClass};
17
18/// Trait that is implemented for functions that can be connected to signals.
19///
20/// This is used in [`ConnectBuilder`](crate::registry::signal::connect_builder::ConnectBuilder).
21/// There are three variations of the `C` (class instance) parameter:
22/// - `()` for global and associated ("static") functions.
23/// - `&C` for `&self` methods.
24/// - `&mut C` for `&mut self` methods.
25///
26/// See also [Signals](https://godot-rust.github.io/book/register/signals.html) in the book.
27///
28/// # Usage as a bound
29/// To write generic code that handles different functions and forwards them to [`TypedSignal`] and [`ConnectBuilder`] APIs,
30/// you have to use `SignalReceiver` in combination with [`IndirectSignalReceiver`]. Consult the docs of the latter for elaboration.
31///
32/// [`TypedSignal`]: crate::registry::signal::TypedSignal
33/// [`ConnectBuilder`]: crate::registry::signal::ConnectBuilder
34///
35/// # Hidden `impls` in this doc
36/// To keep this documentation readable, we only document one variant of each `impl SignalReceiver`: arbitrarily the one with three parameters
37/// `(P0, P1, P2)`. Keep this in mind when looking at a concrete signature.
38pub trait SignalReceiver<C, Ps>: 'static {
39 /// Invoke the receiver on the given instance (possibly `()`) with `params`.
40 fn call(&mut self, maybe_instance: C, params: Ps);
41}
42
43// Next-gen trait solver should allow for type inference in closures, when function traits are involved, without using identity struct
44// and other hacks. Since `IndirectSignalReceiver` is just a view, it should be drop-in replacement.
45/// A special "identity struct" which enables type inference while specifying various closures for connections.
46///
47/// If you want to write generic code that can handle different functions and forward them to [`TypedSignal`] and [`ConnectBuilder`] APIs,
48/// you have to use [`SignalReceiver`] in combination with this type. Please check its documentation for a detailed explanation.
49///
50/// [`TypedSignal`]: crate::registry::signal::TypedSignal
51/// [`ConnectBuilder`]: crate::registry::signal::ConnectBuilder
52///
53/// # Background
54///
55/// Individual `connect*` methods on `TypedSignal` and `ConnectBuilder` use the `SignalReceiver` trait as a bound. \
56/// **In addition to that**, they have a rather complex bound involving [`IndirectSignalReceiver`]:
57///
58/// ```no_run
59/// # use godot::register::{IndirectSignalReceiver, SignalReceiver};
60/// # use godot_core::meta::InParamTuple;
61/// # use godot_core::obj::WithSignals;
62/// # struct TypedSignal<'c, C: WithSignals, Ps> { _phantom: std::marker::PhantomData<&'c (C, Ps)> }
63/// impl<C: WithSignals, Ps: InParamTuple + 'static> TypedSignal<'_, C, Ps> {
64/// pub fn connect<F>(&self, mut function: F)
65/// where
66/// for<'c_rcv> F: SignalReceiver<(), Ps> + 'static,
67/// for<'c_rcv> IndirectSignalReceiver<'c_rcv, (), Ps, F>: From<&'c_rcv mut F>,
68/// { /* ... */ }
69/// }
70/// ```
71///
72/// This second bound is necessary because rustc cannot infer parameter types in closures, when dealing with `Fn/FnMut/FnOnce` traits abstracted
73/// behind another trait (in our case [`SignalReceiver`]). Without inference, it wouldn't be reliably possible to pass `|this, arg| { ... }`
74/// closures and would require the more verbose `|this: &mut MyClass, arg: GString| { ... }` syntax.
75///
76/// To make type inference work in such cases, we need to specify concrete type, such as `FnMut(...) -> R` or
77/// `<&mut F as Into<IndirectSignalReceiver<'_, MyClass, (Param0, Param1), Func>>>`. The ~~dirty hack~~ clever trick used here is to "smuggle" a
78/// concrete `Fn*` trait through `IndirectSignalReceiver`. This forces rustc to resolve the concrete `Fn*` type.
79///
80/// Prior designs included declarative macros to generate all `connect*` functions with direct `Fn*` bounds (not through `SignalReceiver`).
81/// This works well, too, but prevents users from extending the functionality through generic programming -- they'd need to use macros, too.
82///
83/// # Usage within `connect*` style methods
84/// When using the trait bounds as described above, you can access the actual function in the following way:
85///
86/// ```no_run
87/// # use godot::register::{IndirectSignalReceiver, SignalReceiver};
88/// # let mut function = || {};
89/// # let args = ();
90/// IndirectSignalReceiver::from(&mut function)
91/// .function()
92/// .call((), args);
93/// ```
94///
95/// # Hidden `impls` in this doc
96/// To keep this documentation readable, we only document one variant of each `impl From<F>`: arbitrarily the one with three parameters
97/// `(P0, P1, P2)`. Keep this in mind when looking at a concrete signature.
98///
99/// # Further reading
100/// - [rustc issue #63702](https://github.com/rust-lang/rust/issues/63702)
101/// - [identity function trick](https://users.rust-lang.org/t/type-inference-in-closures/78399/3)
102/// - [discussion about type-inference limits](https://users.rust-lang.org/t/what-are-the-limits-of-type-inference-in-closures/31519)
103/// - [rustc comments around closure type-check](https://github.com/rust-lang/rust/blob/5ad7454f7503b6af2800bf4a7c875962cb03f913/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs#L306-L317)
104pub struct IndirectSignalReceiver<'view, C, Ps, F>
105where
106 Ps: InParamTuple,
107 F: SignalReceiver<C, Ps> + 'static,
108{
109 inner: &'view mut F,
110 _phantoms: PhantomData<(C, Ps)>,
111}
112
113impl<'view, C, Ps, F> IndirectSignalReceiver<'view, C, Ps, F>
114where
115 Ps: InParamTuple,
116 F: SignalReceiver<C, Ps> + 'static,
117{
118 /// Retrieves inner `&mut F` function ready to be used as [`SignalReceiver`].
119 pub fn function(&'view mut self) -> &'view mut F {
120 self.inner
121 }
122
123 /// Creates a new `IndirectSignalReceiver` from a mutable reference to a function.
124 fn new(inner: &'view mut F) -> Self {
125 Self {
126 inner,
127 _phantoms: PhantomData,
128 }
129 }
130}
131
132// ----------------------------------------------------------------------------------------------------------------------------------------------
133// Generated impls
134
135macro_rules! impl_signal_recipient {
136 ($( #[$attr:meta] )? $( $args:ident : $Ps:ident ),*) => {
137 // --------------------------------------------------------------------------------------------------------------------------------------
138 // SignalReceiver
139
140 // SignalReceiver: Global and associated functions.
141 $( #[$attr] )?
142 impl<F, R, $($Ps: std::fmt::Debug + FromGodot + 'static),*> SignalReceiver<(), ( $($Ps,)* )> for F
143 where F: FnMut( $($Ps,)* ) -> R + 'static
144 {
145 fn call(&mut self, _no_instance: (), ($($args,)*): ( $($Ps,)* )) {
146 self($($args,)*);
147 }
148 }
149
150 // SignalReceiver: Methods with mutable receiver - &mut self.
151 $( #[$attr] )?
152 impl<F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*> SignalReceiver<&mut C, ( $($Ps,)* )> for F
153 where F: FnMut( &mut C, $($Ps,)* ) -> R + 'static
154 {
155 fn call(&mut self, instance: &mut C, ($($args,)*): ( $($Ps,)* )) {
156 self(instance, $($args,)*);
157 }
158 }
159
160 // Methods with immutable receiver - &self. Disabled until needed.
161 /*
162 $( #[$attr] )?
163 impl<F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*> SignalReceiver<&C, ( $($Ps,)* )> for F
164 where F: FnMut( &C, $($Ps,)* ) -> R + 'static
165 {
166 fn call(&mut self, instance: &C, ($($args,)*): ( $($Ps,)* )) {
167 self(instance, $($args,)*);
168 }
169 }
170 */
171
172 // Methods with Gd receiver - Gd<Self>.
173 $( #[$attr] )?
174 impl<F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*> SignalReceiver<Gd<C>, ( $($Ps,)* )> for F
175 where F: FnMut( Gd<C>, $($Ps,)* ) -> R + 'static, C: GodotClass
176 {
177 fn call(&mut self, instance: Gd<C>, ($($args,)*): ( $($Ps,)* )) {
178 self(instance, $($args,)*);
179 }
180 }
181
182 // --------------------------------------------------------------------------------------------------------------------------------------
183 // FnMut -> IndirectSignalReceiver
184
185 // From: Global and associated functions.
186 $( #[$attr] )?
187 impl<'c_view, F, R, $($Ps: std::fmt::Debug + FromGodot + 'static),*>
188 From<&'c_view mut F> for IndirectSignalReceiver<'c_view, (), ($($Ps,)*), F>
189 where F: FnMut( $($Ps,)* ) -> R + 'static
190 {
191 fn from(value: &'c_view mut F) -> Self {
192 IndirectSignalReceiver::new(value)
193 }
194 }
195
196 // From: Methods with mutable receiver - &mut self.
197 $( #[$attr] )?
198 impl<'c_view, F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*>
199 From<&'c_view mut F> for IndirectSignalReceiver<'c_view, &mut C, ($($Ps,)*), F>
200 where F: FnMut( &mut C, $($Ps,)* ) -> R + 'static
201 {
202 fn from(value: &'c_view mut F) -> Self {
203 IndirectSignalReceiver::new(value)
204 }
205 }
206
207 // From: Methods with immutable receiver - &self. Disabled until needed.
208 /*
209 $( #[$attr] )?
210 impl<'c_view, F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*>
211 From<&'c_view mut F> for IndirectSignalReceiver<'c_view, &C, ($($Ps,)*), F>
212 where F: FnMut( &C, $($Ps,)* ) -> R + 'static
213 {
214 fn from(value: &'c_view mut F) -> Self {
215 IndirectSignalReceiver::new(value)
216 }
217 }
218 */
219
220 // From: Methods with Gd receiver - Gd<Self>.
221 $( #[$attr] )?
222 impl<'c_view, F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*>
223 From<&'c_view mut F> for IndirectSignalReceiver<'c_view, Gd<C>, ($($Ps,)*), F>
224 where F: FnMut( Gd<C>, $($Ps,)* ) -> R + 'static, C: GodotClass
225 {
226 fn from(value: &'c_view mut F) -> Self {
227 IndirectSignalReceiver::new(value)
228 }
229 }
230 };
231}
232
233impl_signal_recipient!(#[doc(hidden)] );
234impl_signal_recipient!(#[doc(hidden)] arg0: P0);
235impl_signal_recipient!(#[doc(hidden)] arg0: P0, arg1: P1);
236impl_signal_recipient!( arg0: P0, arg1: P1, arg2: P2);
237impl_signal_recipient!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3);
238impl_signal_recipient!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4);
239impl_signal_recipient!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5);
240impl_signal_recipient!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5, arg6: P6);
241impl_signal_recipient!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5, arg6: P6, arg7: P7);
242impl_signal_recipient!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5, arg6: P6, arg7: P7, arg8: P8);
243impl_signal_recipient!(#[doc(hidden)] arg0: P0, arg1: P1, arg2: P2, arg3: P3, arg4: P4, arg5: P5, arg6: P6, arg7: P7, arg8: P8, arg9: P9);