diff --git a/gdextension/gdextension_interface.h b/gdextension/gdextension_interface.h index eb4fdc55..48cb2883 100644 --- a/gdextension/gdextension_interface.h +++ b/gdextension/gdextension_interface.h @@ -379,6 +379,47 @@ typedef struct { GDExtensionVariantPtr *default_arguments; } GDExtensionClassMethodInfo; +typedef void (*GDExtensionCallableCustomCall)(void *callable_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); +typedef GDExtensionBool (*GDExtensionCallableCustomIsValid)(void *callable_userdata); +typedef void (*GDExtensionCallableCustomFree)(void *callable_userdata); + +typedef uint32_t (*GDExtensionCallableCustomHash)(void *callable_userdata); +typedef GDExtensionBool (*GDExtensionCallableCustomEqual)(void *callable_userdata_a, void *callable_userdata_b); +typedef GDExtensionBool (*GDExtensionCallableCustomLessThan)(void *callable_userdata_a, void *callable_userdata_b); + +typedef void (*GDExtensionCallableCustomToString)(void *callable_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out); + +typedef struct { + /* Only `call_func` and `token` are strictly required, however, `object` should be passed if its not a static method. + * + * `token` should point to an address that uniquely identifies the GDExtension (for example, the + * `GDExtensionClassLibraryPtr` passed to the entry symbol function. + * + * `hash_func`, `equal_func`, and `less_than_func` are optional. If not provided both `call_func` and + * `callable_userdata` together are used as the identity of the callable for hashing and comparison purposes. + * + * The hash returned by `hash_func` is cached, `hash_func` will not be called more than once per callable. + * + * `is_valid_func` is necessary if the validity of the callable can change before destruction. + * + * `free_func` is necessary if `callable_userdata` needs to be cleaned up when the callable is freed. + */ + void *callable_userdata; + void *token; + + GDExtensionObjectPtr object; + + GDExtensionCallableCustomCall call_func; + GDExtensionCallableCustomIsValid is_valid_func; + GDExtensionCallableCustomFree free_func; + + GDExtensionCallableCustomHash hash_func; + GDExtensionCallableCustomEqual equal_func; + GDExtensionCallableCustomLessThan less_than_func; + + GDExtensionCallableCustomToString to_string_func; +} GDExtensionCallableCustomInfo; + /* SCRIPT INSTANCE EXTENSION */ typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation. @@ -2262,6 +2303,34 @@ typedef void (*GDExtensionInterfacePlaceHolderScriptInstanceUpdate)(GDExtensionS */ typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object, GDExtensionObjectPtr p_language); +/* INTERFACE: Callable */ + +/** + * @name callable_custom_create + * @since 4.2 + * + * Creates a custom Callable object from a function pointer. + * + * Provided struct can be safely freed once the function returns. + * + * @param r_callable A pointer that will receive the new Callable. + * @param p_callable_custom_info The info required to construct a Callable. + */ +typedef void (*GDExtensionInterfaceCallableCustomCreate)(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_callable_custom_info); + +/** + * @name callable_custom_get_userdata + * @since 4.2 + * + * Retrieves the userdata pointer from a custom Callable. + * + * If the Callable is not a custom Callable or the token does not match the one provided to callable_custom_create() via GDExtensionCallableCustomInfo then NULL will be returned. + * + * @param p_callable A pointer to a Callable. + * @param p_token A pointer to an address that uniquely identifies the GDExtension. + */ +typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstTypePtr p_callable, void *p_token); + /* INTERFACE: ClassDB */ /** diff --git a/include/godot_cpp/core/binder_common.hpp b/include/godot_cpp/core/binder_common.hpp index 09da62b1..a51e15e6 100644 --- a/include/godot_cpp/core/binder_common.hpp +++ b/include/godot_cpp/core/binder_common.hpp @@ -276,6 +276,60 @@ void call_with_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) co (void)p_args; } +template +void call_with_variant_args(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, int p_argcount, GDExtensionCallError &r_error) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } + + if ((size_t)p_argcount < sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } +#endif + call_with_variant_args_helper(p_instance, p_method, p_args, r_error, BuildIndexSequence{}); +} + +template +void call_with_variant_args_ret(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } + + if ((size_t)p_argcount < sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } +#endif + call_with_variant_args_ret_helper(p_instance, p_method, p_args, r_ret, r_error, BuildIndexSequence{}); +} + +template +void call_with_variant_args_retc(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } + + if ((size_t)p_argcount < sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } +#endif + call_with_variant_args_retc_helper(p_instance, p_method, p_args, r_ret, r_error, BuildIndexSequence{}); +} + template void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const std::vector &default_values) { #ifdef DEBUG_ENABLED @@ -538,6 +592,42 @@ void call_with_ptr_args_static_method(void (*p_method)(P...), const GDExtensionC call_with_ptr_args_static_method_helper(p_method, p_args, BuildIndexSequence{}); } +template +void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } + + if ((size_t)p_argcount < sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } +#endif + call_with_variant_args_static_ret(p_method, p_args, r_ret, r_error, BuildIndexSequence{}); +} + +template +void call_with_variant_args_static_ret(void (*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error) { +#ifdef DEBUG_ENABLED + if ((size_t)p_argcount > sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } + + if ((size_t)p_argcount < sizeof...(P)) { + r_error.error = GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = (int32_t)sizeof...(P); + return; + } +#endif + call_with_variant_args_static(p_method, p_args, r_error, BuildIndexSequence{}); +} + template void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_args, Variant &r_ret, GDExtensionCallError &r_error, IndexSequence) { r_error.error = GDEXTENSION_CALL_OK; diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index af81037a..c974fa48 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -40,6 +40,10 @@ #include +// Makes callable_mp readily available in all classes connecting signals. +// Needs to come after method_bind and object have been included. +#include + #include #include #include diff --git a/include/godot_cpp/godot.hpp b/include/godot_cpp/godot.hpp index a3fae75f..4561a75f 100644 --- a/include/godot_cpp/godot.hpp +++ b/include/godot_cpp/godot.hpp @@ -164,6 +164,7 @@ extern "C" GDExtensionInterfaceObjectGetClassName gdextension_interface_object_g extern "C" GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to; extern "C" GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_object_get_instance_from_id; extern "C" GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id; +extern "C" GDExtensionInterfaceCallableCustomCreate gdextension_interface_callable_custom_create; extern "C" GDExtensionInterfaceRefGetObject gdextension_interface_ref_get_object; extern "C" GDExtensionInterfaceRefSetObject gdextension_interface_ref_set_object; extern "C" GDExtensionInterfaceScriptInstanceCreate2 gdextension_interface_script_instance_create2; diff --git a/include/godot_cpp/variant/callable_method_pointer.hpp b/include/godot_cpp/variant/callable_method_pointer.hpp new file mode 100644 index 00000000..bd31ea15 --- /dev/null +++ b/include/godot_cpp/variant/callable_method_pointer.hpp @@ -0,0 +1,215 @@ +/**************************************************************************/ +/* callable_method_pointer.hpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef GODOT_CALLABLE_METHOD_POINTER_HPP +#define GODOT_CALLABLE_METHOD_POINTER_HPP + +#include +#include + +namespace godot { + +class CallableCustomMethodPointerBase { +public: + virtual Object *get_object() const = 0; + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const = 0; +}; + +namespace internal { + +Callable create_custom_callable(CallableCustomMethodPointerBase *p_callable_method_pointer); + +} // namespace internal + +// +// No return value. +// + +template +class CallableCustomMethodPointer : public CallableCustomMethodPointerBase { + T *instance; + void (T::*method)(P...); + +public: + virtual Object *get_object() const override { + return instance; + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { + call_with_variant_args(instance, method, p_arguments, p_argcount, r_call_error); + } + + CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) { + instance = p_instance; + method = p_method; + } +}; + +template +Callable create_custom_callable_function_pointer(T *p_instance, void (T::*p_method)(P...)) { + typedef CallableCustomMethodPointer CCMP; + CCMP *ccmp = memnew(CCMP(p_instance, p_method)); + return ::godot::internal::create_custom_callable(ccmp); +} + +// +// With return value. +// + +template +class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase { + T *instance; + R(T::*method) + (P...); + +public: + virtual Object *get_object() const override { + return instance; + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { + call_with_variant_args_ret(instance, method, p_arguments, p_argcount, r_return_value, r_call_error); + } + + CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) { + instance = p_instance; + method = p_method; + } +}; + +template +Callable create_custom_callable_function_pointer(T *p_instance, R (T::*p_method)(P...)) { + typedef CallableCustomMethodPointerRet CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_instance, p_method)); + return ::godot::internal::create_custom_callable(ccmp); +} + +// +// Const with return value. +// + +template +class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase { + T *instance; + R(T::*method) + (P...) const; + +public: + virtual Object *get_object() const override { + return instance; + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { + call_with_variant_args_retc(instance, method, p_arguments, p_argcount, r_return_value, r_call_error); + } + + CallableCustomMethodPointerRetC(const T *p_instance, R (T::*p_method)(P...) const) { + instance = const_cast(p_instance); + method = p_method; + } +}; + +template +Callable create_custom_callable_function_pointer(const T *p_instance, R (T::*p_method)(P...) const) { + typedef CallableCustomMethodPointerRetC CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_instance, p_method)); + return ::godot::internal::create_custom_callable(ccmp); +} + +// +// Static method with no return value. +// + +template +class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase { + void (*method)(P...); + +public: + virtual Object *get_object() const override { + return nullptr; + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { + call_with_variant_args_static_ret(method, p_arguments, p_argcount, r_return_value, r_call_error); + r_return_value = Variant(); + } + + CallableCustomStaticMethodPointer(void (*p_method)(P...)) { + method = p_method; + } +}; + +template +Callable create_custom_callable_static_function_pointer(void (*p_method)(P...)) { + typedef CallableCustomStaticMethodPointer CCMP; + CCMP *ccmp = memnew(CCMP(p_method)); + return ::godot::internal::create_custom_callable(ccmp); +} + +// +// Static method with return value. +// + +template +class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase { + R(*method) + (P...); + +public: + virtual Object *get_object() const override { + return nullptr; + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { + call_with_variant_args_static_ret(method, p_arguments, p_argcount, r_return_value, r_call_error); + } + + CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) { + method = p_method; + } +}; + +template +Callable create_custom_callable_static_function_pointer(R (*p_method)(P...)) { + typedef CallableCustomStaticMethodPointerRet CCMP; + CCMP *ccmp = memnew(CCMP(p_method)); + return ::godot::internal::create_custom_callable(ccmp); +} + +// +// The API: +// + +#define callable_mp(I, M) ::godot::create_custom_callable_function_pointer(I, M) +#define callable_mp_static(M) ::godot::create_custom_callable_static_function_pointer(M) + +} // namespace godot + +#endif // GODOT_CALLABLE_METHOD_POINTER_HPP diff --git a/src/godot.cpp b/src/godot.cpp index 06712213..05413ff1 100644 --- a/src/godot.cpp +++ b/src/godot.cpp @@ -170,6 +170,7 @@ GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_na GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to = nullptr; GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_object_get_instance_from_id = nullptr; GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id = nullptr; +GDExtensionInterfaceCallableCustomCreate gdextension_interface_callable_custom_create = nullptr; GDExtensionInterfaceRefGetObject gdextension_interface_ref_get_object = nullptr; GDExtensionInterfaceRefSetObject gdextension_interface_ref_set_object = nullptr; GDExtensionInterfaceScriptInstanceCreate2 gdextension_interface_script_instance_create2 = nullptr; @@ -386,6 +387,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge LOAD_PROC_ADDRESS(object_cast_to, GDExtensionInterfaceObjectCastTo); LOAD_PROC_ADDRESS(object_get_instance_from_id, GDExtensionInterfaceObjectGetInstanceFromId); LOAD_PROC_ADDRESS(object_get_instance_id, GDExtensionInterfaceObjectGetInstanceId); + LOAD_PROC_ADDRESS(callable_custom_create, GDExtensionInterfaceCallableCustomCreate); LOAD_PROC_ADDRESS(ref_get_object, GDExtensionInterfaceRefGetObject); LOAD_PROC_ADDRESS(ref_set_object, GDExtensionInterfaceRefSetObject); LOAD_PROC_ADDRESS(script_instance_create2, GDExtensionInterfaceScriptInstanceCreate2); diff --git a/src/variant/callable_method_pointer.cpp b/src/variant/callable_method_pointer.cpp new file mode 100644 index 00000000..7934c730 --- /dev/null +++ b/src/variant/callable_method_pointer.cpp @@ -0,0 +1,66 @@ +/**************************************************************************/ +/* callable_method_pointer.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include + +//#include + +namespace godot { + +static void call_custom_callable(void *userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { + CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)userdata; + callable_method_pointer->call((const Variant **)p_args, p_argument_count, *(Variant *)r_return, *r_error); +} + +static void free_custom_callable(void *userdata) { + CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)userdata; + memdelete(callable_method_pointer); +} + +namespace internal { + +Callable create_custom_callable(CallableCustomMethodPointerBase *p_callable_method_pointer) { + Object *object = p_callable_method_pointer->get_object(); + + GDExtensionCallableCustomInfo info = {}; + info.callable_userdata = p_callable_method_pointer; + info.token = internal::token; + info.object = object != nullptr ? object->_owner : nullptr; + info.call_func = &call_custom_callable; + info.free_func = &free_custom_callable; + + Callable callable; + ::godot::internal::gdextension_interface_callable_custom_create(callable._native_ptr(), &info); + return callable; +} + +} // namespace internal + +} // namespace godot diff --git a/test/project/main.gd b/test/project/main.gd index dbc60f8e..f51ef3b6 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -96,6 +96,28 @@ func _ready(): # String::resize(). assert_equal(example.test_string_resize("What"), "What!?") + # mp_callable() with void method. + var mp_callable: Callable = example.test_callable_mp() + mp_callable.call(example, "void", 36) + assert_equal(custom_signal_emitted, ["unbound_method1: Example - void", 36]) + + # mp_callable() with return value. + var mp_callable_ret: Callable = example.test_callable_mp_ret() + assert_equal(mp_callable_ret.call(example, "test", 77), "unbound_method2: Example - test - 77") + + # mp_callable() with const method and return value. + var mp_callable_retc: Callable = example.test_callable_mp_retc() + assert_equal(mp_callable_retc.call(example, "const", 101), "unbound_method3: Example - const - 101") + + # mp_callable_static() with void method. + var mp_callable_static: Callable = example.test_callable_mp_static() + mp_callable_static.call(example, "static", 83) + assert_equal(custom_signal_emitted, ["unbound_static_method1: Example - static", 83]) + + # mp_callable_static() with return value. + var mp_callable_static_ret: Callable = example.test_callable_mp_static_ret() + assert_equal(mp_callable_static_ret.call(example, "static-ret", 84), "unbound_static_method2: Example - static-ret - 84") + # PackedArray iterators assert_equal(example.test_vector_ops(), 105) diff --git a/test/src/example.cpp b/test/src/example.cpp index 800f5ed0..759cc4f7 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -163,6 +163,12 @@ void Example::_bind_methods() { ClassDB::bind_method(D_METHOD("test_variant_call", "variant"), &Example::test_variant_call); + ClassDB::bind_method(D_METHOD("test_callable_mp"), &Example::test_callable_mp); + ClassDB::bind_method(D_METHOD("test_callable_mp_ret"), &Example::test_callable_mp_ret); + ClassDB::bind_method(D_METHOD("test_callable_mp_retc"), &Example::test_callable_mp_retc); + ClassDB::bind_method(D_METHOD("test_callable_mp_static"), &Example::test_callable_mp_static); + ClassDB::bind_method(D_METHOD("test_callable_mp_static_ret"), &Example::test_callable_mp_static_ret); + ClassDB::bind_method(D_METHOD("test_bitfield", "flags"), &Example::test_bitfield); ClassDB::bind_method(D_METHOD("test_rpc", "value"), &Example::test_rpc); @@ -349,6 +355,64 @@ int Example::test_vector_ops() const { return ret; } +Callable Example::test_callable_mp() { + return callable_mp(this, &Example::unbound_method1); +} + +Callable Example::test_callable_mp_ret() { + return callable_mp(this, &Example::unbound_method2); +} + +Callable Example::test_callable_mp_retc() const { + return callable_mp(this, &Example::unbound_method3); +} + +Callable Example::test_callable_mp_static() const { + return callable_mp_static(&Example::unbound_static_method1); +} + +Callable Example::test_callable_mp_static_ret() const { + return callable_mp_static(&Example::unbound_static_method2); +} + +void Example::unbound_method1(Object *p_object, String p_string, int p_int) { + String test = "unbound_method1: "; + test += p_object->get_class(); + test += " - " + p_string; + emit_custom_signal(test, p_int); +} + +String Example::unbound_method2(Object *p_object, String p_string, int p_int) { + String test = "unbound_method2: "; + test += p_object->get_class(); + test += " - " + p_string; + test += " - " + itos(p_int); + return test; +} + +String Example::unbound_method3(Object *p_object, String p_string, int p_int) const { + String test = "unbound_method3: "; + test += p_object->get_class(); + test += " - " + p_string; + test += " - " + itos(p_int); + return test; +} + +void Example::unbound_static_method1(Example *p_object, String p_string, int p_int) { + String test = "unbound_static_method1: "; + test += p_object->get_class(); + test += " - " + p_string; + p_object->emit_custom_signal(test, p_int); +} + +String Example::unbound_static_method2(Object *p_object, String p_string, int p_int) { + String test = "unbound_static_method2: "; + test += p_object->get_class(); + test += " - " + p_string; + test += " - " + itos(p_int); + return test; +} + int Example::test_tarray_arg(const TypedArray &p_array) { int sum = 0; for (int i = 0; i < p_array.size(); i++) { diff --git a/test/src/example.h b/test/src/example.h index 354b7ba0..4dfda23e 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -138,6 +138,18 @@ public: Variant test_variant_call(Variant p_variant); + Callable test_callable_mp(); + Callable test_callable_mp_ret(); + Callable test_callable_mp_retc() const; + Callable test_callable_mp_static() const; + Callable test_callable_mp_static_ret() const; + + void unbound_method1(Object *p_object, String p_string, int p_int); + String unbound_method2(Object *p_object, String p_string, int p_int); + String unbound_method3(Object *p_object, String p_string, int p_int) const; + static void unbound_static_method1(Example *p_object, String p_string, int p_int); + static String unbound_static_method2(Object *p_object, String p_string, int p_int); + BitField test_bitfield(BitField flags); // RPC