diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 55f687bdf9c9..6bad6f5a5bb2 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -30,10 +30,11 @@
#include "callable.h"
-#include "callable_bind.h"
#include "core/object/object.h"
#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
+#include "core/variant/callable_bind.h"
+#include "core/variant/variant_callable.h"
void Callable::call_deferredp(const Variant **p_arguments, int p_argcount) const {
MessageQueue::get_singleton()->push_callablep(*this, p_arguments, p_argcount, true);
@@ -327,14 +328,27 @@ Callable::operator String() const {
}
}
+Callable Callable::create(const Variant &p_variant, const StringName &p_method) {
+ ERR_FAIL_COND_V_MSG(p_method == StringName(), Callable(), "Method argument to Callable::create method must be a non-empty string.");
+
+ switch (p_variant.get_type()) {
+ case Variant::NIL:
+ return Callable(ObjectID(), p_method);
+ case Variant::OBJECT:
+ return Callable(p_variant.operator ObjectID(), p_method);
+ default:
+ return Callable(memnew(VariantCallable(p_variant, p_method)));
+ }
+}
+
Callable::Callable(const Object *p_object, const StringName &p_method) {
- if (p_method == StringName()) {
+ if (unlikely(p_method == StringName())) {
object = 0;
- ERR_FAIL_MSG("Method argument to Callable constructor must be a non-empty string");
+ ERR_FAIL_MSG("Method argument to Callable constructor must be a non-empty string.");
}
- if (p_object == nullptr) {
+ if (unlikely(p_object == nullptr)) {
object = 0;
- ERR_FAIL_MSG("Object argument to Callable constructor must be non-null");
+ ERR_FAIL_MSG("Object argument to Callable constructor must be non-null.");
}
object = p_object->get_instance_id();
@@ -342,9 +356,9 @@ Callable::Callable(const Object *p_object, const StringName &p_method) {
}
Callable::Callable(ObjectID p_object, const StringName &p_method) {
- if (p_method == StringName()) {
+ if (unlikely(p_method == StringName())) {
object = 0;
- ERR_FAIL_MSG("Method argument to Callable constructor must be a non-empty string");
+ ERR_FAIL_MSG("Method argument to Callable constructor must be a non-empty string.");
}
object = p_object;
@@ -352,9 +366,9 @@ Callable::Callable(ObjectID p_object, const StringName &p_method) {
}
Callable::Callable(CallableCustom *p_custom) {
- if (p_custom->referenced) {
+ if (unlikely(p_custom->referenced)) {
object = 0;
- ERR_FAIL_MSG("Callable custom is already referenced");
+ ERR_FAIL_MSG("Callable custom is already referenced.");
}
p_custom->referenced = true;
object = 0; //ensure object is all zero, since pointer may be 32 bits
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 38872b71ef2d..bba69d453e7a 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -125,6 +125,8 @@ class Callable {
operator String() const;
+ static Callable create(const Variant &p_variant, const StringName &p_method);
+
Callable(const Object *p_object, const StringName &p_method);
Callable(ObjectID p_object, const StringName &p_method);
Callable(CallableCustom *p_custom);
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 503254d1d3a0..543ee1135f40 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -2039,6 +2039,7 @@ static void _register_variant_builtin_methods() {
/* Callable */
+ bind_static_method(Callable, create, sarray("variant", "method"), varray());
bind_method(Callable, callv, sarray("arguments"), varray());
bind_method(Callable, is_null, sarray(), varray());
bind_method(Callable, is_custom, sarray(), varray());
diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml
index 8dd0cfa1357b..bd56d6ab8063 100644
--- a/doc/classes/Callable.xml
+++ b/doc/classes/Callable.xml
@@ -4,7 +4,7 @@
A built-in type representing a method or a standalone function.
- [Callable] is a built-in [Variant] type that represents a function. It can either be a method within an [Object] instance, or a standalone function not related to any object, like a lambda function. Like all [Variant] types, it can be stored in variables and passed to other functions. It is most commonly used for signal callbacks.
+ [Callable] is a built-in [Variant] type that represents a function. It can either be a method within an [Object] instance, or a custom callable used for different purposes (see [method is_custom]). Like all [Variant] types, it can be stored in variables and passed to other functions. It is most commonly used for signal callbacks.
[b]Example:[/b]
[codeblocks]
[gdscript]
@@ -46,6 +46,22 @@
# Prints "Attack!", when the button_pressed signal is emitted.
button_pressed.connect(func(): print("Attack!"))
[/codeblock]
+ In GDScript, you can access methods and global functions as [Callable]s:
+ [codeblock]
+ tween.tween_callback(node.queue_free) # Object methods.
+ tween.tween_callback(array.clear) # Methods of built-in types.
+ tween.tween_callback(print.bind("Test")) # Global functions.
+ [/codeblock]
+ [b]Note:[/b] [Dictionary] does not support the above due to ambiguity with keys.
+ [codeblock]
+ var dictionary = {"hello": "world"}
+
+ # This will not work, `clear` is treated as a key.
+ tween.tween_callback(dictionary.clear)
+
+ # This will work.
+ tween.tween_callback(Callable.create(dictionary, "clear"))
+ [/codeblock]
@@ -69,6 +85,7 @@
Creates a new [Callable] for the method named [param method] in the specified [param object].
+ [b]Note:[/b] For methods of built-in [Variant] types, use [method create] instead.
@@ -120,6 +137,15 @@
Calls the method represented by this [Callable]. Unlike [method call], this method expects all arguments to be contained inside the [param arguments] [Array].
+
+
+
+
+
+ Creates a new [Callable] for the method named [param method] in the specified [param variant]. To represent a method of a built-in [Variant] type, a custom callable is used (see [method is_custom]). If [param variant] is [Object], then a standard callable will be created instead.
+ [b]Note:[/b] This method is always necessary for the [Dictionary] type, as property syntax is used to access its entries. You may also use this method when [param variant]'s type is not known in advance (for polymorphism).
+
+
@@ -160,7 +186,11 @@
- Returns [code]true[/code] if this [Callable] is a custom callable. Custom callables are created from [method bind] or [method unbind]. In GDScript, lambda functions are also custom callables.
+ Returns [code]true[/code] if this [Callable] is a custom callable. Custom callables are used:
+ - for binding/unbinding arguments (see [method bind] and [method unbind]);
+ - for representing methods of built-in [Variant] types (see [method create]);
+ - for representing global, lambda, and RPC functions in GDScript;
+ - for other purposes in the core, GDExtension, and C#.
diff --git a/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.gd b/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.gd
index e4016c011988..cb5ea827f6a1 100644
--- a/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.gd
@@ -1,6 +1,13 @@
func test():
var array: Array = [1, 2, 3]
print(array)
- var callable: Callable = array.clear
- callable.call()
+ var array_clear: Callable = array.clear
+ array_clear.call()
print(array)
+
+ var dictionary: Dictionary = {1: 2, 3: 4}
+ print(dictionary)
+ # `dictionary.clear` is treated as a key.
+ var dictionary_clear := Callable.create(dictionary, &"clear")
+ dictionary_clear.call()
+ print(dictionary)
diff --git a/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.out b/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.out
index c4182b38e975..c12984ca3757 100644
--- a/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.out
+++ b/modules/gdscript/tests/scripts/runtime/features/builtin_method_as_callable.out
@@ -1,3 +1,5 @@
GDTEST_OK
[1, 2, 3]
[]
+{ 1: 2, 3: 4 }
+{ }