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);