Skip to content

Commit

Permalink
Own module for typed signals + variadic args
Browse files Browse the repository at this point in the history
  • Loading branch information
Bromeon committed Jan 6, 2025
1 parent 340a165 commit 73f520f
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 152 deletions.
46 changes: 0 additions & 46 deletions godot-core/src/registry/as_func.rs

This file was deleted.

12 changes: 12 additions & 0 deletions godot-core/src/registry/functional/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Original file line number Diff line number Diff line change
Expand Up @@ -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<Variant>;
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<Variant> {
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
Expand Down Expand Up @@ -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<Ps> {
signal: Signal,
_signature: std::marker::PhantomData<Ps>,
}
impl<Ps: ParamTuple> TypedSignal<Ps> {
pub(crate) fn from_untyped(signal: Signal) -> Self {
Self {
signal,
_signature: std::marker::PhantomData,
}
}
pub fn emit(&self, params: Ps) {
self.signal.emit(&params.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<C, R, Ps> {
godot_name: &'static str,
_return_type: std::marker::PhantomData<R>,
_param_types: std::marker::PhantomData<(C, Ps)>,
}
impl<C: GodotClass, R, Ps> TypedFunc<C, R, Ps> {
#[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<T: GodotClass>(obj: &Gd<T>) {}
pub fn godot_name(&self) -> &'static str {
self.godot_name
}
}
*/

// ----------------------------------------------------------------------------------------------------------------------------------------------

/// Type-safe `#[func]` reference that is readily callable.
Expand Down
85 changes: 85 additions & 0 deletions godot-core/src/registry/functional/variadic.rs
Original file line number Diff line number Diff line change
@@ -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<I, Ps> {
fn call(&mut self, maybe_instance: I, params: Ps);
}

macro_rules! impl_signal_recipient {
($( $args:ident : $Ps:ident ),*) => {
// Global and associated functions.
impl<F, R, $($Ps,)*> AsFunc<(), ( $($Ps,)* )> for F
where F: FnMut( $($Ps,)* ) -> R
{
fn call(&mut self, _no_instance: (), ($($args,)*): ( $($Ps,)* )) {
self($($args,)*);
}
}

// Methods.
impl<F, R, C, $($Ps,)*> 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<Variant>;
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<Variant> {
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);
8 changes: 5 additions & 3 deletions godot-core/src/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
6 changes: 1 addition & 5 deletions godot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions itest/rust/src/builtin_tests/containers/signal_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit 73f520f

Please sign in to comment.