From 73f520fffbba3a6782aa82146129d5192259bc7a Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Mon, 6 Jan 2025 20:38:12 +0100 Subject: [PATCH] Own module for typed signals + variadic args --- godot-core/src/registry/as_func.rs | 46 -------- godot-core/src/registry/functional/mod.rs | 12 +++ .../registry/{ => functional}/typed_signal.rs | 100 +----------------- .../src/registry/functional/variadic.rs | 85 +++++++++++++++ godot-core/src/registry/mod.rs | 8 +- godot/src/lib.rs | 6 +- .../builtin_tests/containers/signal_test.rs | 2 + 7 files changed, 107 insertions(+), 152 deletions(-) delete mode 100644 godot-core/src/registry/as_func.rs create mode 100644 godot-core/src/registry/functional/mod.rs rename godot-core/src/registry/{ => functional}/typed_signal.rs (70%) create mode 100644 godot-core/src/registry/functional/variadic.rs diff --git a/godot-core/src/registry/as_func.rs b/godot-core/src/registry/as_func.rs deleted file mode 100644 index f5769e36c..000000000 --- a/godot-core/src/registry/as_func.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) godot-rust; Bromeon and contributors. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -// https://geo-ant.github.io/blog/2021/rust-traits-and-variadic-functions -// -// Could be generalized with R return type, and not special-casing `self`. But keep simple until actually needed. - -pub trait AsFunc { - fn call(&mut self, maybe_instance: I, params: Ps); -} - -// pub trait AsMethod { -// fn call(&mut self, instance: &mut C, params: Ps); -// } - -// Now generalize via macro: -macro_rules! impl_signal_recipient { - ($( $args:ident : $Ps:ident ),*) => { - // Global and associated functions. - impl AsFunc<(), ( $($Ps,)* )> for F - where F: FnMut( $($Ps,)* ) -> R - { - fn call(&mut self, _no_instance: (), ($($args,)*): ( $($Ps,)* )) { - self($($args,)*); - } - } - - // Methods. - impl AsFunc<&mut C, ( $($Ps,)* )> for F - where F: FnMut( &mut C, $($Ps,)* ) -> R - { - fn call(&mut self, instance: &mut C, ($($args,)*): ( $($Ps,)* )) { - self(instance, $($args,)*); - } - } - }; -} - -impl_signal_recipient!(); -impl_signal_recipient!(arg0: P0); -impl_signal_recipient!(arg0: P0, arg1: P1); -impl_signal_recipient!(arg0: P0, arg1: P1, arg2: P2); diff --git a/godot-core/src/registry/functional/mod.rs b/godot-core/src/registry/functional/mod.rs new file mode 100644 index 000000000..d084defe1 --- /dev/null +++ b/godot-core/src/registry/functional/mod.rs @@ -0,0 +1,12 @@ +/* + * Copyright (c) godot-rust; Bromeon and contributors. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +mod typed_signal; +mod variadic; + +pub use typed_signal::*; +pub use variadic::*; diff --git a/godot-core/src/registry/typed_signal.rs b/godot-core/src/registry/functional/typed_signal.rs similarity index 70% rename from godot-core/src/registry/typed_signal.rs rename to godot-core/src/registry/functional/typed_signal.rs index abf6b8f49..af6d5c983 100644 --- a/godot-core/src/registry/typed_signal.rs +++ b/godot-core/src/registry/functional/typed_signal.rs @@ -9,51 +9,11 @@ use crate::builtin::{Callable, Variant}; use crate::obj::{Gd, GodotClass, WithBaseField}; -use crate::registry::as_func::AsFunc; -use crate::{classes, meta, sys}; +use crate::registry::functional::{AsFunc, ParamTuple}; +use crate::{classes, sys}; use std::borrow::Cow; use std::fmt; -pub trait ParamTuple { - fn to_variant_array(&self) -> Vec; - fn from_variant_array(array: &[&Variant]) -> Self; -} - -macro_rules! impl_param_tuple { - // Recursive case for tuple with N elements - ($($args:ident : $Ps:ident),*) => { - impl<$($Ps),*> ParamTuple for ($($Ps,)*) - where - $($Ps: meta::ToGodot + meta::FromGodot),* - { - fn to_variant_array(&self) -> Vec { - let ($($args,)*) = self; - - vec![ - $( $args.to_variant(), )* - ] - } - - #[allow(unused_variables, unused_mut, clippy::unused_unit)] - fn from_variant_array(array: &[&Variant]) -> Self { - let mut iter = array.iter(); - ( $( - <$Ps>::from_variant( - iter.next().unwrap_or_else(|| panic!("ParamTuple: {} access out-of-bounds (len {})", stringify!($args), array.len())) - ), - )* ) - } - } - }; -} - -impl_param_tuple!(); -impl_param_tuple!(arg0: P0); -impl_param_tuple!(arg0: P0, arg1: P1); -impl_param_tuple!(arg0: P0, arg1: P1, arg2: P2); - -// ---------------------------------------------------------------------------------------------------------------------------------------------- - #[doc(hidden)] pub enum ObjectRef<'a, C: GodotClass> { /// Helpful for emit: reuse `&self` from within the `impl` block, goes through `base()` re-borrowing and thus allows re-entrant calls @@ -167,62 +127,6 @@ impl<'a, C: WithBaseField, Ps: ParamTuple> TypedSignal<'a, C, Ps> { } } -// ---------------------------------------------------------------------------------------------------------------------------------------------- -/* Previous impl based on assumption, Signal would be used. Could maybe be combined within an enum. - -pub struct TypedSignal { - signal: Signal, - _signature: std::marker::PhantomData, -} - -impl TypedSignal { - pub(crate) fn from_untyped(signal: Signal) -> Self { - Self { - signal, - _signature: std::marker::PhantomData, - } - } - - pub fn emit(&self, params: Ps) { - self.signal.emit(¶ms.to_variant_array()); - } - - pub fn connect_untyped(&mut self, callable: &Callable, flags: i64) { - self.signal.connect(callable, flags); - } - - pub fn to_untyped(&self) -> Signal { - self.signal.clone() - } -}*/ - -// ---------------------------------------------------------------------------------------------------------------------------------------------- - -/* -pub struct TypedFunc { - godot_name: &'static str, - _return_type: std::marker::PhantomData, - _param_types: std::marker::PhantomData<(C, Ps)>, -} - -impl TypedFunc { - #[doc(hidden)] - pub fn from_godot_name(godot_name: &'static str) -> Self { - Self { - godot_name, - _return_type: std::marker::PhantomData, - _param_types: std::marker::PhantomData, - } - } - - pub fn with_object(obj: &Gd) {} - - pub fn godot_name(&self) -> &'static str { - self.godot_name - } -} -*/ - // ---------------------------------------------------------------------------------------------------------------------------------------------- /// Type-safe `#[func]` reference that is readily callable. diff --git a/godot-core/src/registry/functional/variadic.rs b/godot-core/src/registry/functional/variadic.rs new file mode 100644 index 000000000..a0f4d6cad --- /dev/null +++ b/godot-core/src/registry/functional/variadic.rs @@ -0,0 +1,85 @@ +/* + * Copyright (c) godot-rust; Bromeon and contributors. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +//! Emulates variadic argument lists (via tuples), related to functions and signals. + +// https://geo-ant.github.io/blog/2021/rust-traits-and-variadic-functions +// +// Could be generalized with R return type, and not special-casing `self`. But keep simple until actually needed. + +use crate::builtin::Variant; +use crate::meta; + +pub trait AsFunc { + fn call(&mut self, maybe_instance: I, params: Ps); +} + +macro_rules! impl_signal_recipient { + ($( $args:ident : $Ps:ident ),*) => { + // Global and associated functions. + impl AsFunc<(), ( $($Ps,)* )> for F + where F: FnMut( $($Ps,)* ) -> R + { + fn call(&mut self, _no_instance: (), ($($args,)*): ( $($Ps,)* )) { + self($($args,)*); + } + } + + // Methods. + impl AsFunc<&mut C, ( $($Ps,)* )> for F + where F: FnMut( &mut C, $($Ps,)* ) -> R + { + fn call(&mut self, instance: &mut C, ($($args,)*): ( $($Ps,)* )) { + self(instance, $($args,)*); + } + } + }; +} + +impl_signal_recipient!(); +impl_signal_recipient!(arg0: P0); +impl_signal_recipient!(arg0: P0, arg1: P1); +impl_signal_recipient!(arg0: P0, arg1: P1, arg2: P2); + +// ---------------------------------------------------------------------------------------------------------------------------------------------- + +pub trait ParamTuple { + fn to_variant_array(&self) -> Vec; + fn from_variant_array(array: &[&Variant]) -> Self; +} + +macro_rules! impl_param_tuple { + ($($args:ident : $Ps:ident),*) => { + impl<$($Ps),*> ParamTuple for ($($Ps,)*) + where + $($Ps: meta::ToGodot + meta::FromGodot),* + { + fn to_variant_array(&self) -> Vec { + let ($($args,)*) = self; + + vec![ + $( $args.to_variant(), )* + ] + } + + #[allow(unused_variables, unused_mut, clippy::unused_unit)] + fn from_variant_array(array: &[&Variant]) -> Self { + let mut iter = array.iter(); + ( $( + <$Ps>::from_variant( + iter.next().unwrap_or_else(|| panic!("ParamTuple: {} access out-of-bounds (len {})", stringify!($args), array.len())) + ), + )* ) + } + } + }; +} + +impl_param_tuple!(); +impl_param_tuple!(arg0: P0); +impl_param_tuple!(arg0: P0, arg1: P1); +impl_param_tuple!(arg0: P0, arg1: P1, arg2: P2); diff --git a/godot-core/src/registry/mod.rs b/godot-core/src/registry/mod.rs index 65678ee0d..26d43d198 100644 --- a/godot-core/src/registry/mod.rs +++ b/godot-core/src/registry/mod.rs @@ -16,9 +16,11 @@ pub mod plugin; pub mod property; #[cfg(since_api = "4.2")] -pub mod as_func; -#[cfg(since_api = "4.2")] -pub mod typed_signal; +pub mod functional; + +// Contents re-exported in `godot` crate; just keep empty. +#[cfg(before_api = "4.2")] +pub mod functional {} // RpcConfig uses MultiplayerPeer::TransferMode and MultiplayerApi::RpcMode, which are only enabled in `codegen-full` feature. #[cfg(feature = "codegen-full")] diff --git a/godot/src/lib.rs b/godot/src/lib.rs index 17854c3cd..5f8902571 100644 --- a/godot/src/lib.rs +++ b/godot/src/lib.rs @@ -183,17 +183,13 @@ pub mod init { /// Register/export Rust symbols to Godot: classes, methods, enums... pub mod register { + pub use godot_core::registry::functional::*; pub use godot_core::registry::property; pub use godot_macros::{godot_api, godot_dyn, Export, GodotClass, GodotConvert, Var}; #[cfg(feature = "__codegen-full")] pub use godot_core::registry::RpcConfig; - #[cfg(since_api = "4.2")] - pub use godot_core::registry::as_func::*; - #[cfg(since_api = "4.2")] - pub use godot_core::registry::typed_signal::*; - /// Re-exports used by proc-macro API. #[doc(hidden)] pub mod private { diff --git a/itest/rust/src/builtin_tests/containers/signal_test.rs b/itest/rust/src/builtin_tests/containers/signal_test.rs index d9a207f74..3812e91e0 100644 --- a/itest/rust/src/builtin_tests/containers/signal_test.rs +++ b/itest/rust/src/builtin_tests/containers/signal_test.rs @@ -124,12 +124,14 @@ impl Emitter { // "Internal" means connect/emit happens from within the class (via &mut self). + #[cfg(since_api = "4.2")] fn connect_signals_internal(&mut self) { let mut sig = self.signals().emitter_1(); sig.connect_self(Self::self_receive); sig.connect(Self::self_receive_static); } + #[cfg(since_api = "4.2")] fn emit_signals_internal(&mut self) { self.signals().emitter_1().emit(1234); }