Skip to content

Commit 686918f

Browse files
committed
Generated emit() functions now take impl AsArg<T>
Allows types to be *mostly* passed like in other engine APIs, e.g. - `i32`, `Vector2` and other `Copy` types by value - `Gd`, `DynGd`, `Variant`, `Array`, `Dictionary` by reference - Strings by reference or "string" literals
1 parent 56db361 commit 686918f

File tree

8 files changed

+110
-7
lines changed

8 files changed

+110
-7
lines changed

godot-core/src/meta/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,10 @@ pub(crate) use traits::{
7272
use crate::registry::method::MethodParamOrReturnInfo;
7373

7474
pub(crate) use crate::{
75-
arg_into_owned, arg_into_ref, declare_arg_method, impl_asarg_by_ref, impl_asarg_by_value,
76-
impl_godot_as_self,
75+
arg_into_ref, declare_arg_method, impl_asarg_by_ref, impl_asarg_by_value, impl_godot_as_self,
7776
};
77+
// Public due to signals emit() needing it. Should be made pub(crate) again if that changes.
78+
pub use crate::arg_into_owned;
7879

7980
#[doc(hidden)]
8081
pub use signature::*;

godot-core/src/obj/dyn_gd.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,23 @@ where
543543
}
544544
}
545545

546+
/*
547+
// See `impl AsArg for Gd<T>` for why this isn't yet implemented.
548+
impl<'r, T, TBase, D> meta::AsArg<DynGd<TBase, D>> for &'r DynGd<T, D>
549+
where
550+
T: Inherits<TBase>,
551+
TBase: GodotClass,
552+
D: ?Sized + 'static,
553+
{
554+
fn into_arg<'cow>(self) -> meta::CowArg<'cow, DynGd<TBase, D>>
555+
where
556+
'r: 'cow,
557+
{
558+
meta::CowArg::Owned(self.clone().upcast::<TBase>())
559+
}
560+
}
561+
*/
562+
546563
impl<T, D> meta::ParamType for DynGd<T, D>
547564
where
548565
T: GodotClass,

godot-core/src/obj/gd.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,30 @@ impl<'r, T: GodotClass> AsArg<Gd<T>> for &'r Gd<T> {
852852
}
853853
}
854854

855+
/*
856+
// TODO find a way to generalize AsArg to derived->base conversions without breaking type inference in array![].
857+
// Possibly we could use a "canonical type" with unambiguous mapping (&Gd<T> -> &Gd<T>, not &Gd<T> -> &Gd<TBase>).
858+
// See also regression test in array_test.rs.
859+
860+
impl<'r, T, TBase> AsArg<Gd<TBase>> for &'r Gd<T>
861+
where
862+
T: Inherits<TBase>,
863+
TBase: GodotClass,
864+
{
865+
#[doc(hidden)] // Repeated despite already hidden in trait; some IDEs suggest this otherwise.
866+
fn into_arg<'cow>(self) -> CowArg<'cow, Gd<TBase>>
867+
where
868+
'r: 'cow, // Original reference must be valid for at least as long as the returned cow.
869+
{
870+
// Performance: clones unnecessarily, which has overhead for ref-counted objects.
871+
// A result of being generic over base objects and allowing T: Inherits<Base> rather than just T == Base.
872+
// Was previously `CowArg::Borrowed(self)`. Borrowed() can maybe be specialized for objects, or combined with AsObjectArg.
873+
874+
CowArg::Owned(self.clone().upcast::<TBase>())
875+
}
876+
}
877+
*/
878+
855879
impl<T: GodotClass> ParamType for Gd<T> {
856880
type Arg<'v> = CowArg<'v, Gd<T>>;
857881

godot-macros/src/class/data_models/signal.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,35 @@ impl SignalCollection {
320320
}
321321
}
322322

323+
fn make_asarg_params(params: &venial::Punctuated<venial::FnParam>) -> TokenStream {
324+
// Could be specialized by trying to parse types, but won't be 100% accurate due to lack of semantics (AsArg could be a safe fallback). E.g.:
325+
// if ty.tokens.iter().any(|tk| matches!(tk, TokenTree::Ident(ident) if ident == "Gd")) {
326+
// quote! { impl ::godot::meta::AsObjectArg<#some_inner_ty> }
327+
// }
328+
329+
let mut tokens = TokenStream::new();
330+
331+
for (param, _punct) in params.iter() {
332+
match param {
333+
venial::FnParam::Typed(param) => {
334+
let param_name = &param.name;
335+
let param_type = &param.ty;
336+
337+
tokens.extend(quote! {
338+
#param_name: impl ::godot::meta::AsArg<#param_type>,
339+
});
340+
}
341+
venial::FnParam::Receiver(_) => {
342+
unreachable!("signals have no receivers; already checked")
343+
}
344+
};
345+
}
346+
347+
tokens
348+
}
349+
323350
fn make_signal_individual_struct(details: &SignalDetails) -> TokenStream {
324-
let emit_params = &details.fn_signature.params;
351+
let emit_params = make_asarg_params(&details.fn_signature.params);
325352

326353
let SignalDetails {
327354
// class_name,
@@ -360,6 +387,12 @@ fn make_signal_individual_struct(details: &SignalDetails) -> TokenStream {
360387
#(#signal_cfg_attrs)*
361388
impl<C: ::godot::obj::WithSignals> #individual_struct_name<'_, C> {
362389
pub fn emit(&mut self, #emit_params) {
390+
use ::godot::meta::AsArg;
391+
#(
392+
::godot::meta::arg_into_owned!(infer #param_names);
393+
//let #param_names = #param_names.into_arg();
394+
)*
395+
363396
self.__typed.emit_tuple((#( #param_names, )*));
364397
}
365398
}

itest/rust/src/builtin_tests/containers/signal_test.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,28 @@ fn signal_symbols_external() {
122122
emitter.free();
123123
}
124124

125+
// "External" means connect/emit happens from outside the class, via Gd::signals().
126+
#[cfg(since_api = "4.2")]
127+
#[itest]
128+
fn signal_symbols_complex_emit() {
129+
let mut emitter = Emitter::new_alloc();
130+
let arg = emitter.clone();
131+
let mut sig = emitter.signals().signal_obj();
132+
133+
let tracker = Rc::new(RefCell::new(None));
134+
{
135+
let tracker = tracker.clone();
136+
sig.connect(move |obj: Gd<Object>, name: GString| {
137+
*tracker.borrow_mut() = Some((obj, name));
138+
});
139+
}
140+
141+
// Forward compat: .upcast() here becomes a breaking change if we generalize AsArg to include derived->base conversions.
142+
sig.emit(&arg.upcast(), "hello");
143+
144+
emitter.free();
145+
}
146+
125147
// "External" means connect/emit happens from outside the class, via Gd::signals().
126148
#[cfg(since_api = "4.2")]
127149
#[itest]
@@ -396,7 +418,7 @@ mod emitter {
396418
pub fn signal_int(arg1: i64);
397419

398420
#[signal]
399-
fn signal_obj(arg1: Gd<Object>, arg2: GString);
421+
pub(super) fn signal_obj(arg1: Gd<Object>, arg2: GString);
400422

401423
#[func]
402424
pub fn self_receive(&mut self, arg1: i64) {

itest/rust/src/engine_tests/async_test.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,10 @@ fn async_typed_signal_with_array() -> TaskHandle {
229229
assert_eq!(result, array![1, 2, 3]);
230230
});
231231

232-
object.signals().custom_signal_array().emit(array![1, 2, 3]);
232+
object
233+
.signals()
234+
.custom_signal_array()
235+
.emit(&array![1, 2, 3]);
233236

234237
task_handle
235238
}

itest/rust/src/object_tests/dyn_gd_test.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ fn dyn_gd_store_in_godot_array() {
347347
let a = Gd::from_object(RefcHealth { hp: 33 }).into_dyn();
348348
let b = foreign::NodeHealth::new_alloc().into_dyn();
349349

350+
// Forward compat: .upcast() here becomes a breaking change if we generalize AsArg to include derived->base conversions.
350351
let array: Array<DynGd<Object, _>> = array![&a.upcast(), &b.upcast()];
351352

352353
assert_eq!(array.at(0).dyn_bind().get_hitpoints(), 33);
@@ -355,8 +356,9 @@ fn dyn_gd_store_in_godot_array() {
355356
array.at(1).free();
356357

357358
// Tests also type inference of array![]. Independent variable c.
358-
let c = Gd::from_object(RefcHealth { hp: 33 }).into_dyn();
359-
let array_inferred = array![&c];
359+
let c: DynGd<RefcHealth, dyn Health> = Gd::from_object(RefcHealth { hp: 33 }).into_dyn();
360+
let c = c.upcast::<RefCounted>();
361+
let array_inferred /*: Array<DynGd<RefCounted, _>>*/ = array![&c];
360362
assert_eq!(array_inferred.at(0).dyn_bind().get_hitpoints(), 33);
361363
}
362364

itest/rust/src/object_tests/object_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@ pub mod object_test_gd {
10401040

10411041
#[func]
10421042
fn return_nested_self() -> Array<Gd<<Self as GodotClass>::Base>> {
1043+
// Forward compat: .upcast() here becomes a breaking change if we generalize AsArg to include derived->base conversions.
10431044
array![&Self::return_self().upcast()]
10441045
}
10451046
}

0 commit comments

Comments
 (0)