Skip to content

Commit 21f95f7

Browse files
bors[bot]lilizoey
andauthored
Merge #231
231: Add implementation of Callable r=lilizoey a=lilizoey Add an initial implementation of callable, mostly just wrapping `InnerCallable`. `bind` and its relatives will crash if you attempt to use them. i experimented with a purely rust-based solution but it wouldn't work when passed to gdscript and if we're only using rust then we might as well use closures. `call` and `call_deferred` will also crash when called. Add a `Gd::callable` method to more easily make `Callables` from a `Gd<T>`, it just defers to `Callable::from_object_method`. ## New? We should probably have a `new` constructor, but what should it be? should it just create an empty callable, i.e just do `Self::default`, or should it call `from_object_method`? maybe a function like: ```rs pub fn new<T, S>(object: impl Into<Option<Gd<T>>>, method: S) -> Self where T: GodotClass, S: Into<StringName>, { let Some(object) = object.into() else { return Self::default() }; Self::from_object_method(object, method) } ``` ### Other Add vararg functions for builtin inner classes. initially i planned to use these for `call` and `call_deferred`, but it seems like these functions dont work at the moment. i believe for the same reason as #169 Add `length` and `is_empty` to `StringName`. `is_empty` is used in `Callable::method_name`, and it just felt weird to me to have `is_empty` but not `length`. Co-authored-by: Lili Zoey <[email protected]>
2 parents 4229ad5 + 7c786cd commit 21f95f7

File tree

10 files changed

+347
-61
lines changed

10 files changed

+347
-61
lines changed

godot-codegen/src/class_generator.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -879,11 +879,6 @@ fn make_builtin_method_definition(
879879
type_info: &BuiltinTypeInfo,
880880
ctx: &mut Context,
881881
) -> TokenStream {
882-
// TODO implement varcalls
883-
if method.is_vararg {
884-
return TokenStream::new();
885-
}
886-
887882
let method_name_str = &method.name;
888883

889884
let (receiver, receiver_arg) =

godot-core/src/builtin/callable.rs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
7+
use godot_ffi as sys;
8+
9+
use crate::builtin::{inner, ToVariant, Variant};
10+
use crate::engine::Object;
11+
use crate::obj::mem::Memory;
12+
use crate::obj::{Gd, GodotClass, InstanceId};
13+
use std::fmt;
14+
use sys::{ffi_methods, GodotFfi};
15+
16+
use super::{StringName, VariantArray};
17+
18+
/// A `Callable` represents a function in Godot.
19+
///
20+
/// Usually a callable is a reference to an `Object` and a method name, this is a standard callable. But can
21+
/// also be a custom callable, which is usually created from `bind`, `unbind`, or a GDScript lambda. See
22+
/// [`Callable::is_custom`].
23+
///
24+
/// Currently it is impossible to use `bind` and `unbind` in GDExtension, see [godot-cpp#802].
25+
///
26+
/// [godot-cpp#802]: https://github.com/godotengine/godot-cpp/issues/802
27+
#[repr(C, align(8))]
28+
pub struct Callable {
29+
opaque: sys::types::OpaqueCallable,
30+
}
31+
32+
impl Callable {
33+
fn from_opaque(opaque: sys::types::OpaqueCallable) -> Self {
34+
Self { opaque }
35+
}
36+
37+
/// Create a callable for the method `object::method_name`.
38+
///
39+
/// _Godot equivalent: `Callable(Object object, StringName method)`_
40+
pub fn from_object_method<T, S>(object: Gd<T>, method_name: S) -> Self
41+
where
42+
T: GodotClass, // + Inherits<Object>,
43+
S: Into<StringName>,
44+
{
45+
// upcast not needed
46+
let method = method_name.into();
47+
unsafe {
48+
Self::from_sys_init_default(|self_ptr| {
49+
let ctor = sys::builtin_fn!(callable_from_object_method);
50+
let args = [object.sys_const(), method.sys_const()];
51+
ctor(self_ptr, args.as_ptr());
52+
})
53+
}
54+
}
55+
56+
/// Calls the method represented by this callable.
57+
///
58+
/// Arguments passed should match the method's signature.
59+
///
60+
/// - If called with more arguments than expected by the method, the extra arguments will be ignored and
61+
/// the call continues as normal.
62+
/// - If called with fewer arguments than expected it will crash Godot, without triggering UB.
63+
/// - If called with arguments of the wrong type then an error will be printed and the call will return
64+
/// `NIL`.
65+
/// - If called on an invalid Callable then no error is printed, and `NIL` is returned.
66+
///
67+
/// _Godot equivalent: `callv`_
68+
pub fn callv(&self, arguments: VariantArray) -> Variant {
69+
self.as_inner().callv(arguments)
70+
}
71+
72+
/// Returns the name of the method represented by this callable. If the callable is a lambda function,
73+
/// returns the function's name.
74+
///
75+
/// ## Known Bugs
76+
///
77+
/// Getting the name of a lambda errors instead of returning its name, see [godot#73052].
78+
///
79+
/// _Godot equivalent: `get_method`_
80+
///
81+
/// [godot#73052]: https://github.com/godotengine/godot/issues/73052
82+
pub fn method_name(&self) -> Option<StringName> {
83+
let method_name = self.as_inner().get_method();
84+
if method_name.is_empty() {
85+
None
86+
} else {
87+
Some(method_name)
88+
}
89+
}
90+
91+
/// Returns the object on which this callable is called.
92+
///
93+
/// Returns `None` when this callable doesn't have any target object to call a method on, regardless of
94+
/// if the method exists for that target or not.
95+
///
96+
/// _Godot equivalent: `get_object`_
97+
pub fn object(&self) -> Option<Gd<Object>> {
98+
// Increment refcount because we're getting a reference, and `InnerCallable::get_object` doesn't
99+
// increment the refcount.
100+
self.as_inner().get_object().map(|object| {
101+
<Object as GodotClass>::Mem::maybe_inc_ref(&object);
102+
object
103+
})
104+
}
105+
106+
/// Returns the ID of this callable's object, see also [`Gd::instance_id`].
107+
///
108+
/// Returns `None` when this callable doesn't have any target to call a method on.
109+
///
110+
/// _Godot equivalent: `get_object_id`_
111+
pub fn object_id(&self) -> Option<InstanceId> {
112+
let id = self.as_inner().get_object_id();
113+
InstanceId::try_from_i64(id)
114+
}
115+
116+
/// Returns the 32-bit hash value of this callable's object.
117+
///
118+
/// _Godot equivalent: `hash`_
119+
pub fn hash(&self) -> u32 {
120+
self.as_inner().hash().try_into().unwrap()
121+
}
122+
123+
/// Returns true if this callable is a custom callable.
124+
///
125+
/// Custom callables are mainly created from bind or unbind. In GDScript, lambda functions are also
126+
/// custom callables.
127+
///
128+
/// If a callable is not a custom callable, then it is considered a standard callable, this function is
129+
/// the opposite of [`Callable.is_standard`].
130+
///
131+
/// _Godot equivalent: `is_custom`_
132+
///
133+
/// [`Callable.is_standard`]: https://docs.godotengine.org/en/stable/classes/class_callable.html#class-callable-method-is-standard
134+
#[doc(alias = "is_standard")]
135+
pub fn is_custom(&self) -> bool {
136+
self.as_inner().is_custom()
137+
}
138+
139+
/// Returns true if this callable has no target to call the method on.
140+
///
141+
/// This is not the negated form of [`is_valid`], as `is_valid` will return `false` if the callable has a
142+
/// target but the method does not exist.
143+
///
144+
/// _Godot equivalent: `is_null`_
145+
pub fn is_null(&self) -> bool {
146+
self.as_inner().is_null()
147+
}
148+
149+
/// Returns true if the callable's object exists and has a valid method name assigned, or is a custom
150+
/// callable.
151+
///
152+
/// _Godot equivalent: `is_valid`_
153+
pub fn is_valid(&self) -> bool {
154+
self.as_inner().is_valid()
155+
}
156+
157+
#[doc(hidden)]
158+
pub fn as_inner(&self) -> inner::InnerCallable {
159+
inner::InnerCallable::from_outer(self)
160+
}
161+
}
162+
163+
impl_builtin_traits! {
164+
for Callable {
165+
Default => callable_construct_default;
166+
// Equality for custom callables depend on the equality implementation of that custom callable. This
167+
// is from what i can tell currently implemented as total equality in all cases, but i dont believe
168+
// there are any guarantees that all implementations of equality for custom callables will be.
169+
//
170+
// So we cannot implement `Eq` here and be confident equality will be total for all future custom
171+
// callables.
172+
PartialEq => callable_operator_equal;
173+
Clone => callable_construct_copy;
174+
Drop => callable_destroy;
175+
}
176+
}
177+
178+
// SAFETY:
179+
// The `opaque` in `Callable` is just a pair of pointers, and requires no special initialization or cleanup
180+
// beyond what is done in `from_opaque` and `drop`. So using `*mut Opaque` is safe.
181+
unsafe impl GodotFfi for Callable {
182+
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. }
183+
}
184+
185+
impl std::fmt::Debug for Callable {
186+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187+
let method = self.method_name();
188+
let object = self.object();
189+
190+
f.debug_struct("Callable")
191+
.field("method", &method)
192+
.field("object", &object)
193+
.finish()
194+
}
195+
}
196+
197+
impl std::fmt::Display for Callable {
198+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199+
write!(f, "{}", self.to_variant())
200+
}
201+
}

godot-core/src/builtin/macros.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -107,25 +107,6 @@ macro_rules! impl_builtin_traits_inner {
107107
}
108108
}
109109
};
110-
111-
// TODO remove; use godot-core/src/builtin/variant/impls.rs instead (this one is only used for Callable)
112-
( FromVariant for $Type:ty => $gd_method:ident ) => {
113-
impl $crate::builtin::variant::FromVariant for $Type {
114-
fn try_from_variant(variant: &$crate::builtin::Variant) -> Result<Self, $crate::builtin::variant::VariantConversionError> {
115-
if variant.get_type() != <Self as $crate::builtin::meta::VariantMetadata>::variant_type() {
116-
return Err($crate::builtin::variant::VariantConversionError)
117-
}
118-
let result = unsafe {
119-
Self::from_sys_init_default(|self_ptr| {
120-
let converter = sys::builtin_fn!($gd_method);
121-
converter(self_ptr, variant.var_sys());
122-
})
123-
};
124-
125-
Ok(result)
126-
}
127-
}
128-
};
129110
}
130111

131112
macro_rules! impl_builtin_traits {

godot-core/src/builtin/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub use crate::{array, dict, varray};
3838
pub use aabb::*;
3939
pub use array_inner::{Array, VariantArray};
4040
pub use basis::*;
41+
pub use callable::*;
4142
pub use color::*;
4243
pub use dictionary_inner::Dictionary;
4344
pub use math::*;
@@ -90,6 +91,7 @@ mod dictionary_inner;
9091

9192
mod aabb;
9293
mod basis;
94+
mod callable;
9395
mod color;
9496
mod glam_helpers;
9597
mod math;
@@ -403,7 +405,10 @@ mod export {
403405
impl_export_by_clone!(Vector3i);
404406
impl_export_by_clone!(Vector4);
405407

406-
// TODO investigate whether these should impl Export at all, and if so, how
407-
// impl_export_by_clone!(Callable);
408+
// Callables can be exported, however you can't do anything with them in the editor.
409+
// But we do need to be able to export them since we can't make something a property without exporting.
410+
// And it should be possible to access Callables by property from for instance GDScript.
411+
impl_export_by_clone!(Callable);
412+
// TODO investigate whether Signal should impl Export at all, and if so, how
408413
// impl_export_by_clone!(Signal);
409414
}

godot-core/src/builtin/others.rs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,9 @@
66

77
// Stub for various other built-in classes, which are currently incomplete, but whose types
88
// are required for codegen
9-
use crate::builtin::{inner, StringName};
10-
use crate::obj::{Gd, GodotClass};
119
use godot_ffi as sys;
1210
use sys::{ffi_methods, GodotFfi};
1311

1412
// TODO: Swap more inner math types with glam types
1513
// Note: ordered by enum ord in extension JSON
16-
impl_builtin_stub!(Callable, OpaqueCallable);
1714
impl_builtin_stub!(Signal, OpaqueSignal);
18-
19-
impl Callable {
20-
pub fn from_object_method<T, S>(object: Gd<T>, method: S) -> Self
21-
where
22-
T: GodotClass, // + Inherits<Object>,
23-
S: Into<StringName>,
24-
{
25-
// upcast not needed
26-
let method = method.into();
27-
unsafe {
28-
Self::from_sys_init_default(|self_ptr| {
29-
let ctor = sys::builtin_fn!(callable_from_object_method);
30-
let args = [object.sys_const(), method.sys_const()];
31-
ctor(self_ptr, args.as_ptr());
32-
})
33-
}
34-
}
35-
36-
#[doc(hidden)]
37-
pub fn as_inner(&self) -> inner::InnerCallable {
38-
inner::InnerCallable::from_outer(self)
39-
}
40-
}
41-
42-
impl_builtin_traits! {
43-
for Callable {
44-
// Default => callable_construct_default;
45-
FromVariant => callable_from_variant;
46-
}
47-
}

godot-core/src/builtin/string_name.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use sys::{ffi_methods, GodotFfi};
1111
use std::fmt;
1212
use std::hash::{Hash, Hasher};
1313

14+
use super::inner;
15+
1416
#[repr(C)]
1517
pub struct StringName {
1618
opaque: sys::types::OpaqueStringName,
@@ -21,6 +23,25 @@ impl StringName {
2123
Self { opaque }
2224
}
2325

26+
/// Returns the number of characters in the string.
27+
///
28+
/// _Godot equivalent: `length`_
29+
pub fn len(&self) -> usize {
30+
self.as_inner().length() as usize
31+
}
32+
33+
/// Returns `true` if this is the empty string.
34+
///
35+
/// _Godot equivalent: `is_empty`_
36+
pub fn is_empty(&self) -> bool {
37+
self.as_inner().is_empty()
38+
}
39+
40+
#[doc(hidden)]
41+
pub fn as_inner(&self) -> inner::InnerStringName {
42+
inner::InnerStringName::from_outer(self)
43+
}
44+
2445
ffi_methods! {
2546
type sys::GDExtensionStringNamePtr = *mut Opaque;
2647

godot-core/src/builtin/variant/impls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ mod impls {
146146
impl_variant_traits!(Aabb, aabb_to_variant, aabb_from_variant, Aabb);
147147
impl_variant_traits!(bool, bool_to_variant, bool_from_variant, Bool);
148148
impl_variant_traits!(Basis, basis_to_variant, basis_from_variant, Basis);
149+
impl_variant_traits!(Callable, callable_to_variant, callable_from_variant, Callable);
149150
impl_variant_traits!(Vector2, vector2_to_variant, vector2_from_variant, Vector2);
150151
impl_variant_traits!(Vector3, vector3_to_variant, vector3_from_variant, Vector3);
151152
impl_variant_traits!(Vector4, vector4_to_variant, vector4_from_variant, Vector4);
@@ -157,7 +158,6 @@ mod impls {
157158
impl_variant_traits!(StringName, string_name_to_variant, string_name_from_variant, StringName);
158159
impl_variant_traits!(NodePath, node_path_to_variant, node_path_from_variant, NodePath);
159160
// TODO use impl_variant_traits!, as soon as `Default` is available. Also consider auto-generating.
160-
impl_variant_metadata!(Callable, /* callable_to_variant, callable_from_variant, */ Callable);
161161
impl_variant_metadata!(Signal, /* signal_to_variant, signal_from_variant, */ Signal);
162162
impl_variant_traits!(PackedByteArray, packed_byte_array_to_variant, packed_byte_array_from_variant, PackedByteArray);
163163
impl_variant_traits!(PackedInt32Array, packed_int32_array_to_variant, packed_int32_array_from_variant, PackedInt32Array);

godot-core/src/obj/gd.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ use sys::types::OpaqueObject;
1515
use sys::{ffi_methods, interface_fn, static_assert_eq_size, GodotFfi, PtrcallType};
1616

1717
use crate::builtin::meta::{ClassName, VariantMetadata};
18-
use crate::builtin::{FromVariant, ToVariant, Variant, VariantConversionError};
18+
use crate::builtin::{
19+
Callable, FromVariant, StringName, ToVariant, Variant, VariantConversionError,
20+
};
1921
use crate::obj::dom::Domain as _;
2022
use crate::obj::mem::Memory as _;
2123
use crate::obj::{cap, dom, mem, Export, GodotClass, Inherits, Share};
@@ -416,6 +418,11 @@ impl<T: GodotClass> Gd<T> {
416418
T::Mem::maybe_init_ref(&self);
417419
self
418420
}
421+
422+
/// Returns a callable referencing a method from this object named `method_name`.
423+
pub fn callable<S: Into<StringName>>(&self, method_name: S) -> Callable {
424+
Callable::from_object_method(self.share(), method_name)
425+
}
419426
}
420427

421428
/// _The methods in this impl block are only available for objects `T` that are manually managed,

0 commit comments

Comments
 (0)