@@ -68,54 +68,29 @@ impl Callable {
68
68
///
69
69
/// Allows you to call static functions through `Callable`.
70
70
///
71
- /// Note that due to varying support across different engine versions, the resulting `Callable` has unspecified behavior for
72
- /// methods such as [`method_name()`][Self::method_name], [`object()`][Self::object], [`object_id()`][Self::object_id] or
73
- /// [`get_argument_count()`][Self::arg_len] among others. It is recommended to only use this for calling the function.
74
- ///
75
71
/// # Compatibility
76
- /// Up until and including Godot 4.3, this method has some limitations:
77
- /// - [`is_valid()`][Self::is_valid] will return `false`, even though the call itself succeeds .
78
- /// - You cannot use statics to connect signals to such callables. Use the new typed signal API instead.
72
+ /// Not available before Godot 4.4. Library versions <0.3 used to provide this, however the polyfill used to emulate it was half-broken
73
+ /// (not supporting signals, bind(), method_name(), is_valid(), etc) .
74
+ # [ cfg ( since_api = "4.4" ) ]
79
75
pub fn from_local_static (
80
76
class_name : impl meta:: AsArg < StringName > ,
81
77
function_name : impl meta:: AsArg < StringName > ,
82
78
) -> Self {
83
79
meta:: arg_into_owned!( class_name) ;
84
80
meta:: arg_into_owned!( function_name) ;
85
81
86
- // Modern implementation: use ClassDb::class_call_static().
87
- #[ cfg( since_api = "4.4" ) ]
88
- {
89
- let callable_name = format ! ( "{class_name}.{function_name}" ) ;
90
-
91
- Self :: from_local_fn ( & callable_name, move |args| {
92
- let args = args. iter ( ) . cloned ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
93
-
94
- let result: Variant = classes:: ClassDb :: singleton ( ) . class_call_static (
95
- & class_name,
96
- & function_name,
97
- args. as_slice ( ) ,
98
- ) ;
99
- Ok ( result)
100
- } )
101
- }
82
+ let callable_name = format ! ( "{class_name}.{function_name}" ) ;
102
83
103
- // Polyfill for <= Godot 4.3: use GDScript expressions.
104
- #[ cfg( before_api = "4.4" ) ]
105
- {
106
- use crate :: obj:: NewGd ;
84
+ Self :: from_local_fn ( & callable_name, move |args| {
85
+ let args = args. iter ( ) . cloned ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
107
86
108
- let code = format ! (
109
- "static func __callable():\n \t return Callable({class_name}, \" {function_name}\" )"
87
+ let result: Variant = classes:: ClassDb :: singleton ( ) . class_call_static (
88
+ & class_name,
89
+ & function_name,
90
+ args. as_slice ( ) ,
110
91
) ;
111
-
112
- let mut script = classes:: GDScript :: new_gd ( ) ;
113
- script. set_source_code ( & code) ;
114
- script. reload ( ) ;
115
-
116
- let callable = script. call ( "__callable" , & [ ] ) ;
117
- callable. to ( )
118
- }
92
+ Ok ( result)
93
+ } )
119
94
}
120
95
121
96
#[ cfg( since_api = "4.2" ) ]
@@ -125,7 +100,7 @@ impl Callable {
125
100
token : ptr:: null_mut ( ) ,
126
101
object_id : 0 ,
127
102
call_func : None ,
128
- is_valid_func : None , // could be customized, but no real use case yet .
103
+ is_valid_func : None , // overwritten later .
129
104
free_func : None ,
130
105
hash_func : None ,
131
106
equal_func : None ,
@@ -219,12 +194,14 @@ impl Callable {
219
194
let userdata = CallableUserdata { inner : callable } ;
220
195
221
196
let info = CallableCustomInfo {
197
+ // We could technically associate an object_id with the custom callable. is_valid_func would then check that for validity.
222
198
callable_userdata : Box :: into_raw ( Box :: new ( userdata) ) as * mut std:: ffi:: c_void ,
223
199
call_func : Some ( rust_callable_call_custom :: < C > ) ,
224
200
free_func : Some ( rust_callable_destroy :: < C > ) ,
225
201
hash_func : Some ( rust_callable_hash :: < C > ) ,
226
202
equal_func : Some ( rust_callable_equal :: < C > ) ,
227
203
to_string_func : Some ( rust_callable_to_string_display :: < C > ) ,
204
+ is_valid_func : Some ( rust_callable_is_valid_custom :: < C > ) ,
228
205
..Self :: default_callable_custom_info ( )
229
206
} ;
230
207
@@ -243,6 +220,7 @@ impl Callable {
243
220
call_func : Some ( rust_callable_call_fn :: < F > ) ,
244
221
free_func : Some ( rust_callable_destroy :: < FnWrapper < F > > ) ,
245
222
to_string_func : Some ( rust_callable_to_string_named :: < F > ) ,
223
+ is_valid_func : Some ( rust_callable_is_valid) ,
246
224
..Self :: default_callable_custom_info ( )
247
225
} ;
248
226
@@ -530,6 +508,17 @@ mod custom_callable {
530
508
/// Error handling is mostly needed in case argument number or types mismatch.
531
509
#[ allow( clippy:: result_unit_err) ] // TODO remove once there's a clear error type here.
532
510
fn invoke ( & mut self , args : & [ & Variant ] ) -> Result < Variant , ( ) > ;
511
+
512
+ // TODO(v0.3): add object_id().
513
+
514
+ /// Returns whether the callable is considered valid.
515
+ ///
516
+ /// True by default.
517
+ ///
518
+ /// If this Callable stores an object, this method should return whether that object is alive.
519
+ fn is_valid ( & self ) -> bool {
520
+ true
521
+ }
533
522
}
534
523
535
524
pub unsafe extern "C" fn rust_callable_call_custom < C : RustCallable > (
@@ -641,4 +630,23 @@ mod custom_callable {
641
630
w. name . clone ( ) . move_into_string_ptr ( r_out) ;
642
631
* r_is_valid = sys:: conv:: SYS_TRUE ;
643
632
}
633
+
634
+ // Implementing this is necessary because the default (nullptr) may consider custom callables as invalid in some cases.
635
+ pub unsafe extern "C" fn rust_callable_is_valid_custom < C : RustCallable > (
636
+ callable_userdata : * mut std:: ffi:: c_void ,
637
+ ) -> sys:: GDExtensionBool {
638
+ let w: & mut C = CallableUserdata :: inner_from_raw ( callable_userdata) ;
639
+ let valid = w. is_valid ( ) ;
640
+
641
+ sys:: conv:: bool_to_sys ( valid)
642
+ }
643
+
644
+ // Implementing this is necessary because the default (nullptr) may consider custom callables as invalid in some cases.
645
+ pub unsafe extern "C" fn rust_callable_is_valid (
646
+ _callable_userdata : * mut std:: ffi:: c_void ,
647
+ ) -> sys:: GDExtensionBool {
648
+ // If we had an object (CallableCustomInfo::object_id field), we could check whether that object is alive.
649
+ // But since we just take a Rust function/closure, not knowing what happens inside, we assume always valid.
650
+ sys:: conv:: SYS_TRUE
651
+ }
644
652
}
0 commit comments