Skip to content

Commit 2776cc3

Browse files
committed
Add Callable::from_custom()
1 parent 9e33ef1 commit 2776cc3

File tree

6 files changed

+241
-17
lines changed

6 files changed

+241
-17
lines changed

godot-core/src/builtin/array.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ impl<T: VariantMetadata> Array<T> {
178178
interface_fn!(array_operator_index_const)(self.sys(), index)
179179
};
180180

181+
// Signature is wrong in GDExtension, semantically this is a const ptr
182+
let variant_ptr = sys::to_const_ptr(variant_ptr);
181183
Variant::ptr_from_sys(variant_ptr)
182184
}
183185

godot-core/src/builtin/callable.rs

Lines changed: 138 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66

77
use godot_ffi as sys;
88

9-
use crate::builtin::{inner, ToVariant, Variant};
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};
13-
use std::fmt;
13+
use std::{fmt, ptr};
1414
use sys::{ffi_methods, GodotFfi};
1515

16-
use super::{StringName, VariantArray};
17-
1816
/// A `Callable` represents a function in Godot.
1917
///
2018
/// Usually a callable is a reference to an `Object` and a method name, this is a standard callable. But can
@@ -53,14 +51,47 @@ impl Callable {
5351
}
5452
}
5553

54+
/// Create a callable representing a Rust function.
55+
// #[cfg(since_api = "4.2")]
56+
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+
}
63+
64+
// #[cfg(since_api = "4.2")]
65+
fn from_custom_inner(callable: Box<CallableUserdata>) -> Self {
66+
let mut info = sys::GDExtensionCallableCustomInfo {
67+
callable_userdata: Box::into_raw(callable) as *mut std::ffi::c_void,
68+
token: ptr::null_mut(),
69+
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.
76+
less_than_func: None,
77+
to_string_func: Some(rust_callable_to_string),
78+
};
79+
80+
unsafe {
81+
Callable::from_sys_init(|type_ptr| {
82+
sys::interface_fn!(callable_custom_create)(type_ptr, ptr::addr_of_mut!(info))
83+
})
84+
}
85+
}
86+
5687
/// Creates an invalid/empty object that is not able to be called.
5788
///
5889
/// _Godot equivalent: `Callable()`_
5990
pub fn invalid() -> Self {
6091
unsafe {
6192
Self::from_sys_init(|self_ptr| {
62-
let ctor = ::godot_ffi::builtin_fn!(callable_construct_default);
63-
ctor(self_ptr, std::ptr::null_mut())
93+
let ctor = sys::builtin_fn!(callable_construct_default);
94+
ctor(self_ptr, ptr::null_mut())
6495
})
6596
}
6697
}
@@ -177,13 +208,10 @@ impl_builtin_traits! {
177208
// Currently no Default::default() to encourage explicit valid initialization.
178209
//Default => callable_construct_default;
179210

180-
// Equality for custom callables depend on the equality implementation of that custom callable. This
181-
// is from what i can tell currently implemented as total equality in all cases, but i dont believe
182-
// there are any guarantees that all implementations of equality for custom callables will be.
183-
//
184-
// So we cannot implement `Eq` here and be confident equality will be total for all future custom
185-
// callables.
211+
// Equality for custom callables depend on the equality implementation of that custom callable.
212+
// So we cannot implement `Eq` here and be confident equality will be total for all future custom callables.
186213
PartialEq => callable_operator_equal;
214+
// PartialOrd => callable_operator_less;
187215
Clone => callable_construct_copy;
188216
Drop => callable_destroy;
189217
}
@@ -202,7 +230,7 @@ unsafe impl GodotFfi for Callable {
202230
}
203231
}
204232

205-
impl std::fmt::Debug for Callable {
233+
impl fmt::Debug for Callable {
206234
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207235
let method = self.method_name();
208236
let object = self.object();
@@ -214,8 +242,104 @@ impl std::fmt::Debug for Callable {
214242
}
215243
}
216244

217-
impl std::fmt::Display for Callable {
245+
impl fmt::Display for Callable {
218246
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219247
write!(f, "{}", self.to_variant())
220248
}
221249
}
250+
251+
// ----------------------------------------------------------------------------------------------------------------------------------------------
252+
// Callbacks for custom implementations
253+
254+
// #[cfg(since_api = "4.2")]
255+
use custom_callable::*;
256+
257+
// #[cfg(since_api = "4.2")]
258+
pub use custom_callable::RustCallable;
259+
260+
// #[cfg(since_api = "4.2")]
261+
mod custom_callable {
262+
use super::*;
263+
use crate::builtin::GodotString;
264+
use std::hash::Hash;
265+
266+
pub struct CallableUserdata {
267+
pub callable: Box<dyn RustCallable>,
268+
}
269+
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
274+
}
275+
}
276+
277+
pub trait RustCallable:
278+
'static + Sized + PartialEq + Hash + fmt::Display + Send + Sync
279+
where
280+
Self: Sized,
281+
{
282+
fn invoke(&mut self, args: &[&Variant]) -> Result<Variant, ()>;
283+
}
284+
285+
pub unsafe extern "C" fn rust_callable_call(
286+
callable_userdata: *mut std::ffi::c_void,
287+
p_args: *const sys::GDExtensionConstVariantPtr,
288+
p_argument_count: sys::GDExtensionInt,
289+
r_return: sys::GDExtensionVariantPtr,
290+
r_error: *mut sys::GDExtensionCallError,
291+
) {
292+
let arg_refs: &[&Variant] =
293+
Variant::unbounded_refs_from_sys(p_args, p_argument_count as usize);
294+
295+
let mut c = CallableUserdata::inner_from_raw(callable_userdata);
296+
297+
let result = c.invoke(arg_refs);
298+
crate::builtin::meta::varcall_return_checked(result, r_return, r_error);
299+
}
300+
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;
303+
let _drop = Box::from_raw(rust_ptr);
304+
}
305+
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);
308+
309+
// Just cut off top bits, not best-possible hash.
310+
sys::hash_value(c) as u32
311+
}
312+
313+
pub unsafe extern "C" fn rust_callable_equal(
314+
callable_userdata_a: *mut std::ffi::c_void,
315+
callable_userdata_b: *mut std::ffi::c_void,
316+
) -> sys::GDExtensionBool {
317+
let a = CallableUserdata::inner_from_raw(callable_userdata_a);
318+
let b = CallableUserdata::inner_from_raw(callable_userdata_b);
319+
320+
(a == b) as sys::GDExtensionBool
321+
}
322+
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(
334+
callable_userdata: *mut std::ffi::c_void,
335+
r_is_valid: *mut sys::GDExtensionBool,
336+
r_out: sys::GDExtensionStringPtr,
337+
) {
338+
let c = CallableUserdata::inner_from_raw(callable_userdata);
339+
let s = StringName::from(c.to_string());
340+
341+
s.move_string_ptr(r_out);
342+
343+
*r_is_valid = true as sys::GDExtensionBool;
344+
}
345+
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,23 @@ unsafe fn varcall_return<R: ToVariant>(
389389
(*err).error = sys::GDEXTENSION_CALL_OK;
390390
}
391391

392+
/// Moves `ret_val` into `ret`, if it is `Ok(...)`. Otherwise sets an error.
393+
///
394+
/// # Safety
395+
/// See [`varcall_return`].
396+
pub(crate) unsafe fn varcall_return_checked<R: ToVariant>(
397+
ret_val: Result<R, ()>, // TODO Err should be custom CallError enum
398+
ret: sys::GDExtensionVariantPtr,
399+
err: *mut sys::GDExtensionCallError,
400+
) {
401+
if let Ok(ret_val) = ret_val {
402+
varcall_return(ret_val, ret, err);
403+
} else {
404+
*err = sys::default_call_error();
405+
(*err).error = sys::GDEXTENSION_CALL_ERROR_INVALID_ARGUMENT;
406+
}
407+
}
408+
392409
/// Convert the `N`th argument of `args_ptr` into a value of type `P`.
393410
///
394411
/// # Safety

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,24 @@ impl Variant {
229229
}
230230

231231
/// Converts to variant pointer; can be a null pointer.
232-
pub(crate) fn ptr_from_sys(variant_ptr: sys::GDExtensionVariantPtr) -> *const Variant {
232+
pub(crate) fn ptr_from_sys(variant_ptr: sys::GDExtensionConstVariantPtr) -> *const Variant {
233233
variant_ptr as *const Variant
234234
}
235235

236+
/// # Safety
237+
///
238+
pub(crate) unsafe fn unbounded_refs_from_sys<'a>(
239+
variant_ptr_array: *const sys::GDExtensionConstVariantPtr,
240+
length: usize,
241+
) -> &'a [&'a Variant] {
242+
let variant_ptr_array: &'a [sys::GDExtensionConstVariantPtr] =
243+
std::slice::from_raw_parts(variant_ptr_array, length);
244+
245+
// SAFETY: raw pointers and references have the same memory layout.
246+
// See https://doc.rust-lang.org/reference/type-layout.html#pointers-and-references-layout.
247+
unsafe { std::mem::transmute(variant_ptr_array) }
248+
}
249+
236250
/// Converts to variant mut pointer; can be a null pointer.
237251
pub(crate) fn ptr_from_sys_mut(variant_ptr: sys::GDExtensionVariantPtr) -> *mut Variant {
238252
variant_ptr as *mut Variant

godot-core/src/registry.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ pub mod callbacks {
397397
storage.on_dec_ref();
398398
}
399399

400+
// ----------------------------------------------------------------------------------------------------------------------------------------------
400401
// Safe, higher-level methods
401402

402403
/// Abstracts the `GodotInit` away, for contexts where this trait bound is not statically available
@@ -434,7 +435,9 @@ pub mod callbacks {
434435
}
435436
}
436437

437-
// Substitute for Default impl
438+
// ----------------------------------------------------------------------------------------------------------------------------------------------
439+
// Substitutes for Default impl
440+
438441
// Yes, bindgen can implement Default, but only for _all_ types (with single exceptions).
439442
// For FFI types, it's better to have explicit initialization in the general case though.
440443
fn default_registration_info(class_name: ClassName) -> ClassRegistrationInfo {

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

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
use godot::bind::{godot_api, GodotClass};
88
use godot::builtin::inner::InnerCallable;
9-
use godot::builtin::{varray, Callable, GodotString, StringName, ToVariant, Variant};
9+
use godot::builtin::{
10+
varray, Callable, GodotString, StringName, ToVariant, Variant, VariantOperator,
11+
};
1012
use godot::engine::{Node2D, Object};
1113
use godot::obj::Gd;
1214

@@ -125,3 +127,65 @@ fn callable_call_engine() {
125127

126128
obj.free();
127129
}
130+
131+
// #[cfg(since_api = "4.2")]
132+
mod custom_callable {
133+
use super::*;
134+
use godot::builtin::Dictionary;
135+
136+
#[itest]
137+
fn callable_custom_invoke() {
138+
let my_rust_callable = Adder { sum: 0 };
139+
let callable = Callable::from_custom(my_rust_callable);
140+
141+
assert!(callable.is_valid());
142+
assert!(!callable.is_null());
143+
assert!(callable.is_custom());
144+
assert!(callable.object().is_none());
145+
146+
let sum1 = callable.callv(varray![3, 9, 2, 1]);
147+
assert_eq!(sum1, 15.to_variant());
148+
149+
let sum2 = callable.callv(varray![4]);
150+
assert_eq!(sum2, 19.to_variant());
151+
}
152+
153+
#[itest]
154+
fn callable_custom_to_string() {
155+
let my_rust_callable = Adder { sum: 0 };
156+
let callable = Callable::from_custom(my_rust_callable);
157+
158+
println!("to_string: {}", callable);
159+
println!("equal: {}", callable == callable);
160+
println!("equal2: {}", callable.to_variant() == callable.to_variant());
161+
println!("hash: {}", callable.hash());
162+
}
163+
164+
#[itest]
165+
fn callable_custom_equal() {
166+
let a = Callable::from_custom(Adder { sum: 0 });
167+
let b = Callable::from_custom(Adder { sum: 0 });
168+
println!("equal: {}", a == b);
169+
println!("equal2: {}", a.to_variant() == b.to_variant());
170+
171+
let mut dict = Dictionary::new();
172+
173+
dict.insert(a, "hello");
174+
dict.insert(b, "hi");
175+
}
176+
177+
178+
struct Adder {
179+
sum: i32,
180+
}
181+
182+
impl godot::builtin::RustCallable for Adder {
183+
fn invoke(&mut self, args: &[&Variant]) -> Result<Variant, ()> {
184+
for arg in args {
185+
self.sum += arg.to::<i32>();
186+
}
187+
188+
Ok(self.sum.to_variant())
189+
}
190+
}
191+
}

0 commit comments

Comments
 (0)