Skip to content

Commit d4a41c6

Browse files
committed
Custom callables no longer use dyn; elaborate tests for equality/hash
1 parent 2776cc3 commit d4a41c6

File tree

4 files changed

+202
-75
lines changed

4 files changed

+202
-75
lines changed

godot-core/src/builtin/callable.rs

Lines changed: 66 additions & 56 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, StringName, ToVariant, Variant, VariantArray};
9+
use crate::builtin::{inner, GodotString, StringName, ToVariant, Variant, VariantArray};
1010
use crate::engine::Object;
1111
use crate::obj::mem::Memory;
1212
use crate::obj::{Gd, GodotClass, InstanceId};
@@ -51,30 +51,28 @@ impl Callable {
5151
}
5252
}
5353

54-
/// Create a callable representing a Rust function.
55-
// #[cfg(since_api = "4.2")]
54+
/// Create a highly configurable callable from Rust.
55+
///
56+
/// See [`RustCallable`] for requirements on the type.
57+
#[cfg(since_api = "4.2")]
5658
pub fn from_custom<C: RustCallable>(callable: C) -> Self {
57-
// Double box could be avoided if we propagate C through all functions.
58-
let wrapper = CallableUserdata {
59-
callable: Box::new(callable),
60-
};
61-
Self::from_custom_inner(Box::new(wrapper))
62-
}
59+
// Could theoretically use `dyn` but would need:
60+
// - double boxing
61+
// - a type-erased workaround for PartialEq supertrait (which has a `Self` type parameter and thus is not object-safe)
62+
let wrapper = CallableUserdata { callable };
6363

64-
// #[cfg(since_api = "4.2")]
65-
fn from_custom_inner(callable: Box<CallableUserdata>) -> Self {
6664
let mut info = sys::GDExtensionCallableCustomInfo {
67-
callable_userdata: Box::into_raw(callable) as *mut std::ffi::c_void,
65+
callable_userdata: Box::into_raw(Box::new(wrapper)) as *mut std::ffi::c_void,
6866
token: ptr::null_mut(),
6967
object: ptr::null_mut(),
70-
call_func: Some(rust_callable_call),
71-
is_valid_func: None,
72-
free_func: Some(rust_callable_destroy),
73-
hash_func: Some(rust_callable_hash),
74-
equal_func: Some(rust_callable_equal),
75-
// < is only used in niche scenarios and default is usually good enough, see https://github.com/godotengine/godot/issues/81901.
68+
call_func: Some(rust_callable_call::<C>),
69+
is_valid_func: None, // could be customized, but no real use case yet.
70+
free_func: Some(rust_callable_destroy::<C>),
71+
hash_func: Some(rust_callable_hash::<C>),
72+
equal_func: Some(rust_callable_equal::<C>),
73+
// Op < is only used in niche scenarios and default is usually good enough, see https://github.com/godotengine/godot/issues/81901.
7674
less_than_func: None,
77-
to_string_func: Some(rust_callable_to_string),
75+
to_string_func: Some(rust_callable_to_string::<C>),
7876
};
7977

8078
unsafe {
@@ -251,38 +249,46 @@ impl fmt::Display for Callable {
251249
// ----------------------------------------------------------------------------------------------------------------------------------------------
252250
// Callbacks for custom implementations
253251

254-
// #[cfg(since_api = "4.2")]
252+
#[cfg(since_api = "4.2")]
255253
use custom_callable::*;
256254

257-
// #[cfg(since_api = "4.2")]
255+
#[cfg(since_api = "4.2")]
258256
pub use custom_callable::RustCallable;
259257

260-
// #[cfg(since_api = "4.2")]
258+
#[cfg(since_api = "4.2")]
261259
mod custom_callable {
262260
use super::*;
263-
use crate::builtin::GodotString;
264261
use std::hash::Hash;
265262

266-
pub struct CallableUserdata {
267-
pub callable: Box<dyn RustCallable>,
263+
pub struct CallableUserdata<C: RustCallable> {
264+
pub callable: C,
268265
}
269266

270-
impl CallableUserdata {
271-
unsafe fn inner_from_raw<'a>(void_ptr: *mut std::ffi::c_void) -> &'a dyn RustCallable {
272-
let ptr = void_ptr as *mut CallableUserdata;
273-
&(*ptr).callable
267+
impl<C: RustCallable> CallableUserdata<C> {
268+
/// # Safety
269+
/// 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
274273
}
275274
}
276275

277-
pub trait RustCallable:
278-
'static + Sized + PartialEq + Hash + fmt::Display + Send + Sync
279-
where
280-
Self: Sized,
281-
{
276+
/// Represents a custom callable object defined in Rust.
277+
///
278+
/// This trait has a single method, `invoke`, which is called upon invocation.
279+
///
280+
/// Since callables can be invoked from anywhere, they must be self-contained (`'static`) and thread-safe (`Send + Sync`).
281+
/// They also should implement `Display` for the Godot string representation.
282+
/// Furthermore, `PartialEq` and `Hash` are required for equality checks and usage as a key in a `Dictionary`.
283+
pub trait RustCallable: 'static + PartialEq + Hash + fmt::Display + Send + Sync {
284+
/// Invokes the callable with the given arguments as `Variant` references.
285+
///
286+
/// Return `Ok(...)` if the call succeeded, and `Err(())` otherwise.
287+
/// Error handling is mostly needed in case argument number or types mismatch.
282288
fn invoke(&mut self, args: &[&Variant]) -> Result<Variant, ()>;
283289
}
284290

285-
pub unsafe extern "C" fn rust_callable_call(
291+
pub unsafe extern "C" fn rust_callable_call<C: RustCallable>(
286292
callable_userdata: *mut std::ffi::c_void,
287293
p_args: *const sys::GDExtensionConstVariantPtr,
288294
p_argument_count: sys::GDExtensionInt,
@@ -292,51 +298,55 @@ mod custom_callable {
292298
let arg_refs: &[&Variant] =
293299
Variant::unbounded_refs_from_sys(p_args, p_argument_count as usize);
294300

295-
let mut c = CallableUserdata::inner_from_raw(callable_userdata);
301+
let c: &mut C = CallableUserdata::inner_from_raw(callable_userdata);
296302

297303
let result = c.invoke(arg_refs);
298304
crate::builtin::meta::varcall_return_checked(result, r_return, r_error);
299305
}
300306

301-
pub unsafe extern "C" fn rust_callable_destroy(callable_userdata: *mut std::ffi::c_void) {
302-
let rust_ptr = callable_userdata as *mut CallableUserdata;
307+
pub unsafe extern "C" fn rust_callable_destroy<C: RustCallable>(
308+
callable_userdata: *mut std::ffi::c_void,
309+
) {
310+
let rust_ptr = callable_userdata as *mut CallableUserdata<C>;
303311
let _drop = Box::from_raw(rust_ptr);
304312
}
305313

306-
pub unsafe extern "C" fn rust_callable_hash(callable_userdata: *mut std::ffi::c_void) -> u32 {
307-
let c = CallableUserdata::inner_from_raw(callable_userdata);
314+
pub unsafe extern "C" fn rust_callable_hash<C: RustCallable>(
315+
callable_userdata: *mut std::ffi::c_void,
316+
) -> u32 {
317+
let c: &C = CallableUserdata::<C>::inner_from_raw(callable_userdata);
308318

309319
// Just cut off top bits, not best-possible hash.
310320
sys::hash_value(c) as u32
311321
}
312322

313-
pub unsafe extern "C" fn rust_callable_equal(
323+
pub unsafe extern "C" fn rust_callable_equal<C: RustCallable>(
314324
callable_userdata_a: *mut std::ffi::c_void,
315325
callable_userdata_b: *mut std::ffi::c_void,
316326
) -> sys::GDExtensionBool {
317-
let a = CallableUserdata::inner_from_raw(callable_userdata_a);
318-
let b = CallableUserdata::inner_from_raw(callable_userdata_b);
327+
let a: &C = CallableUserdata::inner_from_raw(callable_userdata_a);
328+
let b: &C = CallableUserdata::inner_from_raw(callable_userdata_b);
319329

320330
(a == b) as sys::GDExtensionBool
321331
}
322332

323-
pub unsafe extern "C" fn rust_callable_less(
324-
callable_userdata_a: *mut std::ffi::c_void,
325-
callable_userdata_b: *mut std::ffi::c_void,
326-
) -> sys::GDExtensionBool {
327-
let a = CallableUserdata::inner_from_raw(callable_userdata_a);
328-
let b = CallableUserdata::inner_from_raw(callable_userdata_b);
329-
330-
(a < b) as sys::GDExtensionBool
331-
}
332-
333-
pub unsafe extern "C" fn rust_callable_to_string(
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>(
334344
callable_userdata: *mut std::ffi::c_void,
335345
r_is_valid: *mut sys::GDExtensionBool,
336346
r_out: sys::GDExtensionStringPtr,
337347
) {
338-
let c = CallableUserdata::inner_from_raw(callable_userdata);
339-
let s = StringName::from(c.to_string());
348+
let c: &C = CallableUserdata::inner_from_raw(callable_userdata);
349+
let s = GodotString::from(c.to_string());
340350

341351
s.move_string_ptr(r_out);
342352

godot-core/src/builtin/meta/signature.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ unsafe fn varcall_return<R: ToVariant>(
393393
///
394394
/// # Safety
395395
/// See [`varcall_return`].
396+
#[cfg(since_api = "4.2")] // unused before
396397
pub(crate) unsafe fn varcall_return_checked<R: ToVariant>(
397398
ret_val: Result<R, ()>, // TODO Err should be custom CallError enum
398399
ret: sys::GDExtensionVariantPtr,

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ impl Variant {
234234
}
235235

236236
/// # Safety
237-
///
237+
/// `variant_ptr_array` must be a valid pointer to an array of `length` variant pointers.
238+
/// The caller is responsible of keeping the backing storage alive while the unbounded references exist.
239+
#[cfg(since_api = "4.2")] // unused before
238240
pub(crate) unsafe fn unbounded_refs_from_sys<'a>(
239241
variant_ptr_array: *const sys::GDExtensionConstVariantPtr,
240242
length: usize,

0 commit comments

Comments
 (0)