From 8f2da0a9225e5b0e8fccbc1d4f21a7c964e3f4f8 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 5 Jan 2025 21:10:17 +0100 Subject: [PATCH] Document + test limitations of Callable::from_local_static() --- godot-core/src/builtin/callable.rs | 18 +++++++++++------- .../builtin_tests/containers/callable_test.rs | 9 +++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/godot-core/src/builtin/callable.rs b/godot-core/src/builtin/callable.rs index 72e8ade70..a39ee60ae 100644 --- a/godot-core/src/builtin/callable.rs +++ b/godot-core/src/builtin/callable.rs @@ -23,13 +23,12 @@ type CallableCustomInfo = sys::GDExtensionCallableCustomInfo2; /// A `Callable` represents a function in Godot. /// -/// Usually a callable is a reference to an `Object` and a method name, this is a standard callable. But can -/// also be a custom callable, which is usually created from `bind`, `unbind`, or a GDScript lambda. See -/// [`Callable::is_custom`]. -/// -/// Currently, it is impossible to use `bind` and `unbind` in GDExtension, see [godot-cpp#802]. -/// -/// [godot-cpp#802]: https://github.com/godotengine/godot-cpp/issues/802 +/// Callables can be created in many ways: +/// - From an `Object` and a (non-static) method name. This is a _standard_ callable. +/// - From a GDScript class name and a static function name. (This typically works because classes are instances of `GDScript`). +/// - From a GDScript lambda function. +/// - By modifying an existing `Callable` with [`bind()`][Self::bind] or [`unbind()`][Self::unbind]. +/// - By creating a custom callable from Rust. /// /// # Godot docs /// @@ -72,6 +71,11 @@ impl Callable { /// Note that due to varying support across different engine versions, the resulting `Callable` has unspecified behavior for /// methods such as [`method_name()`][Self::method_name], [`object()`][Self::object], [`object_id()`][Self::object_id] or /// [`get_argument_count()`][Self::arg_len] among others. It is recommended to only use this for calling the function. + /// + /// # Compatibility + /// Up until and including Godot 4.3, this method has some limitations: + /// - [`is_valid()`][Self::is_valid] will return `false`, even though the call itself succeeds. + /// - You cannot use statics to connect signals to such callables. Use the new typed signal API instead. pub fn from_local_static( class_name: impl meta::AsArg, function_name: impl meta::AsArg, diff --git a/itest/rust/src/builtin_tests/containers/callable_test.rs b/itest/rust/src/builtin_tests/containers/callable_test.rs index 25d63e452..c53d94ac3 100644 --- a/itest/rust/src/builtin_tests/containers/callable_test.rs +++ b/itest/rust/src/builtin_tests/containers/callable_test.rs @@ -106,13 +106,22 @@ fn callable_static() { assert_eq!(callable.object(), None); assert_eq!(callable.object_id(), None); assert_eq!(callable.method_name(), None); + assert!(callable.is_custom()); + assert!(callable.is_valid()); } else { assert!(callable.object().is_some()); assert!(callable.object_id().is_some()); assert_eq!(callable.method_name(), Some("concat_array".into())); assert_eq!(callable.to_string(), "GDScriptNativeClass::concat_array"); + assert!(!callable.is_custom()); + + // Surprisingly false, but call still works (see test below). + // What DOESN'T work is connecting 4.3 static methods to signals via this approach. + assert!(!callable.is_valid()); } + assert!(!callable.is_null()); + // Calling works consistently everywhere. let result = callable.callv(&varray![ 10,