Skip to content

Commit 695dae1

Browse files
committed
Add Callable::from_fn()
1 parent d4a41c6 commit 695dae1

File tree

2 files changed

+144
-35
lines changed

2 files changed

+144
-35
lines changed

godot-core/src/builtin/callable.rs

Lines changed: 113 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use godot_ffi as sys;
88

9-
use crate::builtin::{inner, GodotString, StringName, ToVariant, Variant, VariantArray};
9+
use crate::builtin::{inner, StringName, ToVariant, Variant, VariantArray};
1010
use crate::engine::Object;
1111
use crate::obj::mem::Memory;
1212
use crate::obj::{Gd, GodotClass, InstanceId};
@@ -51,6 +51,51 @@ impl Callable {
5151
}
5252
}
5353

54+
/// Create a callable from a Rust function or closure.
55+
///
56+
/// `name` is used for the string representation of the closure, which helps debugging.
57+
///
58+
/// Callables created through multiple `from_fn()` calls are never equal, even if they refer to the same function. If you want to use
59+
/// equality, either clone an existing `Callable` instance, or define your own `PartialEq` impl with [`Callable::from_custom`].
60+
///
61+
/// # Example
62+
/// ```no_run
63+
/// # use godot::prelude::*;
64+
/// let callable = Callable::from_fn("sum", |args: &[&Variant]| {
65+
/// let sum: i32 = args.iter().map(|arg| arg.to::<i32>()).sum();
66+
/// Ok(sum.to_variant())
67+
/// });
68+
/// ```
69+
#[cfg(since_api = "4.2")]
70+
pub fn from_fn<F, S>(name: S, rust_function: F) -> Self
71+
where
72+
F: 'static + Send + Sync + FnMut(&[&Variant]) -> Result<Variant, ()>,
73+
S: Into<crate::builtin::GodotString>,
74+
{
75+
let userdata = CallableUserdata {
76+
inner: FnWrapper {
77+
rust_function,
78+
name: name.into(),
79+
},
80+
};
81+
82+
let info = sys::GDExtensionCallableCustomInfo {
83+
callable_userdata: Box::into_raw(Box::new(userdata)) as *mut std::ffi::c_void,
84+
token: ptr::null_mut(),
85+
object: ptr::null_mut(),
86+
call_func: Some(rust_callable_call_fn::<F>),
87+
is_valid_func: None, // could be customized, but no real use case yet.
88+
free_func: Some(rust_callable_destroy::<FnWrapper<F>>),
89+
hash_func: None,
90+
equal_func: None,
91+
// Op < is only used in niche scenarios and default is usually good enough, see https://github.com/godotengine/godot/issues/81901.
92+
less_than_func: None,
93+
to_string_func: Some(rust_callable_to_string_named::<F>),
94+
};
95+
96+
Self::from_custom_info(info)
97+
}
98+
5499
/// Create a highly configurable callable from Rust.
55100
///
56101
/// See [`RustCallable`] for requirements on the type.
@@ -59,22 +104,28 @@ impl Callable {
59104
// Could theoretically use `dyn` but would need:
60105
// - double boxing
61106
// - a type-erased workaround for PartialEq supertrait (which has a `Self` type parameter and thus is not object-safe)
62-
let wrapper = CallableUserdata { callable };
107+
let userdata = CallableUserdata { inner: callable };
63108

64-
let mut info = sys::GDExtensionCallableCustomInfo {
65-
callable_userdata: Box::into_raw(Box::new(wrapper)) as *mut std::ffi::c_void,
109+
let info = sys::GDExtensionCallableCustomInfo {
110+
callable_userdata: Box::into_raw(Box::new(userdata)) as *mut std::ffi::c_void,
66111
token: ptr::null_mut(),
67112
object: ptr::null_mut(),
68-
call_func: Some(rust_callable_call::<C>),
113+
call_func: Some(rust_callable_call_custom::<C>),
69114
is_valid_func: None, // could be customized, but no real use case yet.
70115
free_func: Some(rust_callable_destroy::<C>),
71116
hash_func: Some(rust_callable_hash::<C>),
72117
equal_func: Some(rust_callable_equal::<C>),
73118
// Op < is only used in niche scenarios and default is usually good enough, see https://github.com/godotengine/godot/issues/81901.
74119
less_than_func: None,
75-
to_string_func: Some(rust_callable_to_string::<C>),
120+
to_string_func: Some(rust_callable_to_string_display::<C>),
76121
};
77122

123+
Self::from_custom_info(info)
124+
}
125+
126+
#[cfg(since_api = "4.2")]
127+
fn from_custom_info(mut info: sys::GDExtensionCallableCustomInfo) -> Callable {
128+
// SAFETY: callable_custom_create() is a valid way of creating callables.
78129
unsafe {
79130
Callable::from_sys_init(|type_ptr| {
80131
sys::interface_fn!(callable_custom_create)(type_ptr, ptr::addr_of_mut!(info))
@@ -208,8 +259,8 @@ impl_builtin_traits! {
208259

209260
// Equality for custom callables depend on the equality implementation of that custom callable.
210261
// So we cannot implement `Eq` here and be confident equality will be total for all future custom callables.
262+
// Godot does not define a less-than operator, so there is no `PartialOrd`.
211263
PartialEq => callable_operator_equal;
212-
// PartialOrd => callable_operator_less;
213264
Clone => callable_construct_copy;
214265
Drop => callable_destroy;
215266
}
@@ -258,21 +309,27 @@ pub use custom_callable::RustCallable;
258309
#[cfg(since_api = "4.2")]
259310
mod custom_callable {
260311
use super::*;
312+
use crate::builtin::GodotString;
261313
use std::hash::Hash;
262314

263-
pub struct CallableUserdata<C: RustCallable> {
264-
pub callable: C,
315+
pub struct CallableUserdata<T> {
316+
pub inner: T,
265317
}
266318

267-
impl<C: RustCallable> CallableUserdata<C> {
319+
impl<T> CallableUserdata<T> {
268320
/// # Safety
269321
/// Returns an unbounded reference. `void_ptr` must be a valid pointer to a `CallableUserdata`.
270-
unsafe fn inner_from_raw<'a>(void_ptr: *mut std::ffi::c_void) -> &'a mut C {
271-
let ptr = void_ptr as *mut CallableUserdata<C>;
272-
&mut (*ptr).callable
322+
unsafe fn inner_from_raw<'a>(void_ptr: *mut std::ffi::c_void) -> &'a mut T {
323+
let ptr = void_ptr as *mut CallableUserdata<T>;
324+
&mut (*ptr).inner
273325
}
274326
}
275327

328+
pub(crate) struct FnWrapper<F> {
329+
pub(crate) rust_function: F,
330+
pub(crate) name: GodotString,
331+
}
332+
276333
/// Represents a custom callable object defined in Rust.
277334
///
278335
/// This trait has a single method, `invoke`, which is called upon invocation.
@@ -288,7 +345,7 @@ mod custom_callable {
288345
fn invoke(&mut self, args: &[&Variant]) -> Result<Variant, ()>;
289346
}
290347

291-
pub unsafe extern "C" fn rust_callable_call<C: RustCallable>(
348+
pub unsafe extern "C" fn rust_callable_call_custom<C: RustCallable>(
292349
callable_userdata: *mut std::ffi::c_void,
293350
p_args: *const sys::GDExtensionConstVariantPtr,
294351
p_argument_count: sys::GDExtensionInt,
@@ -304,52 +361,73 @@ mod custom_callable {
304361
crate::builtin::meta::varcall_return_checked(result, r_return, r_error);
305362
}
306363

307-
pub unsafe extern "C" fn rust_callable_destroy<C: RustCallable>(
364+
pub unsafe extern "C" fn rust_callable_call_fn<F>(
308365
callable_userdata: *mut std::ffi::c_void,
309-
) {
310-
let rust_ptr = callable_userdata as *mut CallableUserdata<C>;
366+
p_args: *const sys::GDExtensionConstVariantPtr,
367+
p_argument_count: sys::GDExtensionInt,
368+
r_return: sys::GDExtensionVariantPtr,
369+
r_error: *mut sys::GDExtensionCallError,
370+
) where
371+
F: FnMut(&[&Variant]) -> Result<Variant, ()>,
372+
{
373+
let arg_refs: &[&Variant] =
374+
Variant::unbounded_refs_from_sys(p_args, p_argument_count as usize);
375+
376+
let w: &mut FnWrapper<F> = CallableUserdata::inner_from_raw(callable_userdata);
377+
378+
let result = (w.rust_function)(arg_refs);
379+
crate::builtin::meta::varcall_return_checked(result, r_return, r_error);
380+
}
381+
382+
pub unsafe extern "C" fn rust_callable_destroy<T>(callable_userdata: *mut std::ffi::c_void) {
383+
let rust_ptr = callable_userdata as *mut CallableUserdata<T>;
311384
let _drop = Box::from_raw(rust_ptr);
312385
}
313386

314-
pub unsafe extern "C" fn rust_callable_hash<C: RustCallable>(
387+
pub unsafe extern "C" fn rust_callable_hash<T: Hash>(
315388
callable_userdata: *mut std::ffi::c_void,
316389
) -> u32 {
317-
let c: &C = CallableUserdata::<C>::inner_from_raw(callable_userdata);
390+
let c: &T = CallableUserdata::<T>::inner_from_raw(callable_userdata);
318391

319392
// Just cut off top bits, not best-possible hash.
320393
sys::hash_value(c) as u32
321394
}
322395

323-
pub unsafe extern "C" fn rust_callable_equal<C: RustCallable>(
396+
pub unsafe extern "C" fn rust_callable_equal<T: PartialEq>(
324397
callable_userdata_a: *mut std::ffi::c_void,
325398
callable_userdata_b: *mut std::ffi::c_void,
326399
) -> sys::GDExtensionBool {
327-
let a: &C = CallableUserdata::inner_from_raw(callable_userdata_a);
328-
let b: &C = CallableUserdata::inner_from_raw(callable_userdata_b);
400+
let a: &T = CallableUserdata::inner_from_raw(callable_userdata_a);
401+
let b: &T = CallableUserdata::inner_from_raw(callable_userdata_b);
329402

330403
(a == b) as sys::GDExtensionBool
331404
}
332405

333-
// pub unsafe extern "C" fn rust_callable_less<C: RustCallable>(
334-
// callable_userdata_a: *mut std::ffi::c_void,
335-
// callable_userdata_b: *mut std::ffi::c_void,
336-
// ) -> sys::GDExtensionBool {
337-
// let a: &C = CallableUserdata::inner_from_raw(callable_userdata_a);
338-
// let b: &C = CallableUserdata::inner_from_raw(callable_userdata_b);
339-
//
340-
// (a < b) as sys::GDExtensionBool
341-
// }
342-
343-
pub unsafe extern "C" fn rust_callable_to_string<C: RustCallable>(
406+
pub unsafe extern "C" fn rust_callable_to_string_display<T: fmt::Display>(
344407
callable_userdata: *mut std::ffi::c_void,
345408
r_is_valid: *mut sys::GDExtensionBool,
346409
r_out: sys::GDExtensionStringPtr,
347410
) {
348-
let c: &C = CallableUserdata::inner_from_raw(callable_userdata);
349-
let s = GodotString::from(c.to_string());
411+
let c: &T = CallableUserdata::inner_from_raw(callable_userdata);
412+
let s = crate::builtin::GodotString::from(c.to_string());
350413

351414
s.move_string_ptr(r_out);
415+
*r_is_valid = true as sys::GDExtensionBool;
416+
}
352417

418+
pub unsafe extern "C" fn rust_callable_to_string_named<F>(
419+
callable_userdata: *mut std::ffi::c_void,
420+
r_is_valid: *mut sys::GDExtensionBool,
421+
r_out: sys::GDExtensionStringPtr,
422+
) {
423+
let w: &mut FnWrapper<F> = CallableUserdata::inner_from_raw(callable_userdata);
424+
425+
w.name.clone().move_string_ptr(r_out);
353426
*r_is_valid = true as sys::GDExtensionBool;
354427
}
428+
429+
pub fn unqualified_type_name<T>() -> &'static str {
430+
let type_name = std::any::type_name::<T>();
431+
type_name.split("::").last().unwrap()
432+
}
355433
}

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,37 @@ mod custom_callable {
137137
use std::hash::Hash;
138138
use std::sync::{Arc, Mutex};
139139

140+
#[itest]
141+
fn callable_from_fn() {
142+
let callable = Callable::from_fn("sum", sum);
143+
144+
assert!(callable.is_valid());
145+
assert!(!callable.is_null());
146+
assert!(callable.is_custom());
147+
assert!(callable.object().is_none());
148+
149+
let sum1 = callable.callv(varray![1, 2, 4, 8]);
150+
assert_eq!(sum1, 15.to_variant());
151+
152+
let sum2 = callable.callv(varray![5]);
153+
assert_eq!(sum2, 5.to_variant());
154+
}
155+
156+
#[itest]
157+
fn callable_from_fn_eq() {
158+
let a = Callable::from_fn("sum", sum);
159+
let b = a.clone();
160+
let c = Callable::from_fn("sum", sum);
161+
162+
assert_eq!(a, b, "same function, same instance -> equal");
163+
assert_ne!(a, c, "same function, different instance -> not equal");
164+
}
165+
166+
fn sum(args: &[&Variant]) -> Result<Variant, ()> {
167+
let sum: i32 = args.iter().map(|arg| arg.to::<i32>()).sum();
168+
Ok(sum.to_variant())
169+
}
170+
140171
#[itest]
141172
fn callable_custom_invoke() {
142173
let my_rust_callable = Adder::new(0);

0 commit comments

Comments
 (0)