From d33bd47219314e825d1130e646bc34f52fe65600 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Sat, 21 Oct 2023 18:32:45 -0500 Subject: [PATCH] Add `CallableCustom` that devs can use in their GDExtensions --- binding_generator.py | 12 ++ include/godot_cpp/core/object.hpp | 24 +--- include/godot_cpp/core/object_id.hpp | 62 ++++++++++ include/godot_cpp/godot.hpp | 1 + include/godot_cpp/variant/callable_custom.hpp | 64 ++++++++++ .../variant/callable_method_pointer.hpp | 38 +++--- src/godot.cpp | 2 + src/variant/callable_custom.cpp | 113 ++++++++++++++++++ src/variant/callable_method_pointer.cpp | 16 +-- test/project/main.gd | 10 ++ test/src/example.cpp | 45 +++++++ test/src/example.h | 1 + 12 files changed, 335 insertions(+), 53 deletions(-) create mode 100644 include/godot_cpp/core/object_id.hpp create mode 100644 include/godot_cpp/variant/callable_custom.hpp create mode 100644 src/variant/callable_custom.cpp diff --git a/binding_generator.py b/binding_generator.py index eb201ff7..676ff748 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -110,6 +110,8 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False): for native_struct in api["native_structures"]: struct_name = native_struct["name"] + if struct_name == "ObjectID": + continue snake_struct_name = camel_to_snake(struct_name) header_filename = include_gen_folder / "classes" / (snake_struct_name + ".hpp") @@ -416,6 +418,9 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl if class_name == "Array": result.append("#include ") + if class_name == "Callable": + result.append("#include ") + for include in fully_used_classes: if include == "TypedArray": result.append("#include ") @@ -525,6 +530,9 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl result.append(f"\t{class_name}(const wchar_t *from);") result.append(f"\t{class_name}(const char16_t *from);") result.append(f"\t{class_name}(const char32_t *from);") + if class_name == "Callable": + result.append("\tCallable(CallableCustom *p_custom);") + result.append("\tCallableCustom *get_custom() const;") if "constants" in builtin_api: axis_constants_count = 0 @@ -1083,6 +1091,8 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): class_api["alias_for"] = "ClassDB" engine_classes[class_api["name"]] = class_api["is_refcounted"] for native_struct in api["native_structures"]: + if native_struct["name"] == "ObjectID": + continue engine_classes[native_struct["name"]] = False native_structures.append(native_struct["name"]) @@ -1210,6 +1220,8 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): for native_struct in api["native_structures"]: struct_name = native_struct["name"] + if struct_name == "ObjectID": + continue snake_struct_name = camel_to_snake(struct_name) header_filename = include_gen_folder / (snake_struct_name + ".hpp") diff --git a/include/godot_cpp/core/object.hpp b/include/godot_cpp/core/object.hpp index 4e85e4dc..79f8fbfe 100644 --- a/include/godot_cpp/core/object.hpp +++ b/include/godot_cpp/core/object.hpp @@ -33,6 +33,8 @@ #include +#include + #include #include @@ -106,28 +108,6 @@ MethodInfo::MethodInfo(const PropertyInfo &p_ret, StringName p_name, const Args arguments = { args... }; } -class ObjectID { - uint64_t id = 0; - -public: - _FORCE_INLINE_ bool is_ref_counted() const { return (id & (uint64_t(1) << 63)) != 0; } - _FORCE_INLINE_ bool is_valid() const { return id != 0; } - _FORCE_INLINE_ bool is_null() const { return id == 0; } - _FORCE_INLINE_ operator uint64_t() const { return id; } - _FORCE_INLINE_ operator int64_t() const { return id; } - - _FORCE_INLINE_ bool operator==(const ObjectID &p_id) const { return id == p_id.id; } - _FORCE_INLINE_ bool operator!=(const ObjectID &p_id) const { return id != p_id.id; } - _FORCE_INLINE_ bool operator<(const ObjectID &p_id) const { return id < p_id.id; } - - _FORCE_INLINE_ void operator=(int64_t p_int64) { id = p_int64; } - _FORCE_INLINE_ void operator=(uint64_t p_uint64) { id = p_uint64; } - - _FORCE_INLINE_ ObjectID() {} - _FORCE_INLINE_ explicit ObjectID(const uint64_t p_id) { id = p_id; } - _FORCE_INLINE_ explicit ObjectID(const int64_t p_id) { id = p_id; } -}; - class ObjectDB { public: static Object *get_instance(uint64_t p_object_id) { diff --git a/include/godot_cpp/core/object_id.hpp b/include/godot_cpp/core/object_id.hpp new file mode 100644 index 00000000..9f3bc96f --- /dev/null +++ b/include/godot_cpp/core/object_id.hpp @@ -0,0 +1,62 @@ +/**************************************************************************/ +/* object_id.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_OBJECT_ID_HPP +#define GODOT_OBJECT_ID_HPP + +#include + +namespace godot { + +class ObjectID { + uint64_t id = 0; + +public: + _FORCE_INLINE_ bool is_ref_counted() const { return (id & (uint64_t(1) << 63)) != 0; } + _FORCE_INLINE_ bool is_valid() const { return id != 0; } + _FORCE_INLINE_ bool is_null() const { return id == 0; } + _FORCE_INLINE_ operator uint64_t() const { return id; } + _FORCE_INLINE_ operator int64_t() const { return id; } + + _FORCE_INLINE_ bool operator==(const ObjectID &p_id) const { return id == p_id.id; } + _FORCE_INLINE_ bool operator!=(const ObjectID &p_id) const { return id != p_id.id; } + _FORCE_INLINE_ bool operator<(const ObjectID &p_id) const { return id < p_id.id; } + + _FORCE_INLINE_ void operator=(int64_t p_int64) { id = p_int64; } + _FORCE_INLINE_ void operator=(uint64_t p_uint64) { id = p_uint64; } + + _FORCE_INLINE_ ObjectID() {} + _FORCE_INLINE_ explicit ObjectID(const uint64_t p_id) { id = p_id; } + _FORCE_INLINE_ explicit ObjectID(const int64_t p_id) { id = p_id; } +}; + +} // namespace godot + +#endif // GODOT_OBJECT_ID_HPP diff --git a/include/godot_cpp/godot.hpp b/include/godot_cpp/godot.hpp index 46911dcc..d2184a4c 100644 --- a/include/godot_cpp/godot.hpp +++ b/include/godot_cpp/godot.hpp @@ -166,6 +166,7 @@ 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" GDExtensionInterfaceCallableCustomGetUserData gdextension_interface_callable_custom_get_userdata; 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_custom.hpp b/include/godot_cpp/variant/callable_custom.hpp new file mode 100644 index 00000000..34328f9c --- /dev/null +++ b/include/godot_cpp/variant/callable_custom.hpp @@ -0,0 +1,64 @@ +/**************************************************************************/ +/* callable_custom.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_CUSTOM_HPP +#define GODOT_CALLABLE_CUSTOM_HPP + +#include +#include + +namespace godot { + +class Object; + +class CallableCustomBase { +public: + virtual ObjectID get_object() const = 0; + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const = 0; + virtual ~CallableCustomBase() {} +}; + +class CallableCustom : public CallableCustomBase { +public: + typedef bool (*CompareEqualFunc)(const CallableCustom *p_a, const CallableCustom *p_b); + typedef bool (*CompareLessFunc)(const CallableCustom *p_a, const CallableCustom *p_b); + + virtual uint32_t hash() const = 0; + virtual String get_as_text() const = 0; + virtual CompareEqualFunc get_compare_equal_func() const = 0; + virtual CompareLessFunc get_compare_less_func() const = 0; + virtual bool is_valid() const; + virtual ObjectID 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 godot + +#endif // GODOT_CALLABLE_CUSTOM_HPP diff --git a/include/godot_cpp/variant/callable_method_pointer.hpp b/include/godot_cpp/variant/callable_method_pointer.hpp index d83bcba9..8559253d 100644 --- a/include/godot_cpp/variant/callable_method_pointer.hpp +++ b/include/godot_cpp/variant/callable_method_pointer.hpp @@ -36,16 +36,12 @@ 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; - virtual ~CallableCustomMethodPointerBase() {} +class CallableCustomMethodPointerBase : public CallableCustomBase { }; namespace internal { -Callable create_custom_callable(CallableCustomMethodPointerBase *p_callable_method_pointer); +Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_method_pointer); } // namespace internal @@ -59,8 +55,8 @@ class CallableCustomMethodPointer : public CallableCustomMethodPointerBase { void (T::*method)(P...); public: - virtual Object *get_object() const override { - return instance; + virtual ObjectID get_object() const override { + return ObjectID(instance->get_instance_id()); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { @@ -77,7 +73,7 @@ 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); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // @@ -91,8 +87,8 @@ class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase { (P...); public: - virtual Object *get_object() const override { - return instance; + virtual ObjectID get_object() const override { + return ObjectID(instance->get_instance_id()); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { @@ -109,7 +105,7 @@ 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); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // @@ -123,8 +119,8 @@ class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase { (P...) const; public: - virtual Object *get_object() const override { - return instance; + virtual ObjectID get_object() const override { + return ObjectID(instance->get_instance_id()); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { @@ -141,7 +137,7 @@ 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); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // @@ -153,8 +149,8 @@ class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase void (*method)(P...); public: - virtual Object *get_object() const override { - return nullptr; + virtual ObjectID get_object() const override { + return ObjectID(); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { @@ -171,7 +167,7 @@ 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); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // @@ -184,8 +180,8 @@ class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerB (P...); public: - virtual Object *get_object() const override { - return nullptr; + virtual ObjectID get_object() const override { + return ObjectID(); } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { @@ -201,7 +197,7 @@ 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); + return ::godot::internal::create_callable_from_ccmp(ccmp); } // diff --git a/src/godot.cpp b/src/godot.cpp index 7579cfde..70c5978c 100644 --- a/src/godot.cpp +++ b/src/godot.cpp @@ -172,6 +172,7 @@ 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; +GDExtensionInterfaceCallableCustomGetUserData gdextension_interface_callable_custom_get_userdata = nullptr; GDExtensionInterfaceRefGetObject gdextension_interface_ref_get_object = nullptr; GDExtensionInterfaceRefSetObject gdextension_interface_ref_set_object = nullptr; GDExtensionInterfaceScriptInstanceCreate2 gdextension_interface_script_instance_create2 = nullptr; @@ -390,6 +391,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge 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(callable_custom_get_userdata, GDExtensionInterfaceCallableCustomGetUserData); 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_custom.cpp b/src/variant/callable_custom.cpp new file mode 100644 index 00000000..e0540fa1 --- /dev/null +++ b/src/variant/callable_custom.cpp @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* callable_custom.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 +#include + +namespace godot { + +static void callable_custom_call(void *p_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + callable_custom->call((const Variant **)p_args, p_argument_count, *(Variant *)r_return, *r_error); +} + +static GDExtensionBool callable_custom_is_valid(void *p_userdata) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + return callable_custom->is_valid(); +} + +static void callable_custom_free(void *p_userdata) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + memdelete(callable_custom); +} + +static uint32_t callable_custom_hash(void *p_userdata) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + return callable_custom->hash(); +} + +static void callable_custom_to_string(void *p_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + *((String *)r_out) = callable_custom->get_as_text(); + *r_is_valid = true; +} + +static GDExtensionBool callable_custom_equal_func(void *p_a, void *p_b) { + CallableCustom *a = (CallableCustom *)p_a; + CallableCustom *b = (CallableCustom *)p_b; + CallableCustom::CompareEqualFunc func_a = a->get_compare_equal_func(); + CallableCustom::CompareEqualFunc func_b = b->get_compare_equal_func(); + if (func_a != func_b) { + return false; + } + return func_a(a, b); +} + +static GDExtensionBool callable_custom_less_than_func(void *p_a, void *p_b) { + CallableCustom *a = (CallableCustom *)p_a; + CallableCustom *b = (CallableCustom *)p_b; + CallableCustom::CompareEqualFunc func_a = a->get_compare_less_func(); + CallableCustom::CompareEqualFunc func_b = b->get_compare_less_func(); + if (func_a != func_b) { + // Just compare the addresses. + return p_a < p_b; + } + return func_a(a, b); +} + +bool CallableCustom::is_valid() const { + // The same default implementation as in Godot. + return ObjectDB::get_instance(get_object()); +} + +Callable::Callable(CallableCustom *p_callable_custom) { + GDExtensionCallableCustomInfo info = {}; + info.callable_userdata = p_callable_custom; + info.token = internal::token; + info.object_id = p_callable_custom->get_object(); + info.call_func = &callable_custom_call; + info.is_valid_func = &callable_custom_is_valid; + info.free_func = &callable_custom_free; + info.hash_func = &callable_custom_hash; + info.equal_func = &callable_custom_equal_func; + info.less_than_func = &callable_custom_less_than_func; + info.to_string_func = &callable_custom_to_string; + + ::godot::internal::gdextension_interface_callable_custom_create(_native_ptr(), &info); +} + +CallableCustom *Callable::get_custom() const { + CallableCustomBase *callable_custom = (CallableCustomBase *)::godot::internal::gdextension_interface_callable_custom_get_userdata(_native_ptr(), internal::token); + return dynamic_cast(callable_custom); +} + +} // namespace godot diff --git a/src/variant/callable_method_pointer.cpp b/src/variant/callable_method_pointer.cpp index ea43632b..10f29205 100644 --- a/src/variant/callable_method_pointer.cpp +++ b/src/variant/callable_method_pointer.cpp @@ -30,31 +30,27 @@ #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) { +static void custom_callable_mp_call(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) { +static void custom_callable_mp_free(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(); - +Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_method_pointer) { GDExtensionCallableCustomInfo info = {}; info.callable_userdata = p_callable_method_pointer; info.token = internal::token; - info.object_id = object ? object->get_instance_id() : 0; - info.call_func = &call_custom_callable; - info.free_func = &free_custom_callable; + info.object_id = p_callable_method_pointer->get_object(); + info.call_func = &custom_callable_mp_call; + info.free_func = &custom_callable_mp_free; Callable callable; ::godot::internal::gdextension_interface_callable_custom_create(callable._native_ptr(), &info); diff --git a/test/project/main.gd b/test/project/main.gd index 62bd19d5..74a11683 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -121,6 +121,16 @@ func _ready(): 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") + # CallableCustom. + var custom_callable: Callable = example.test_custom_callable(); + assert_equal(custom_callable.is_custom(), true); + assert_equal(custom_callable.is_valid(), true); + assert_equal(custom_callable.call(), "Hi") + assert_equal(custom_callable.hash(), 27); + assert_equal(custom_callable.get_object(), null); + assert_equal(custom_callable.get_method(), ""); + assert_equal(str(custom_callable), ""); + # PackedArray iterators assert_equal(example.test_vector_ops(), 105) diff --git a/test/src/example.cpp b/test/src/example.cpp index dd58f377..2b8bef63 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -15,6 +15,46 @@ using namespace godot; +class MyCallableCustom : public CallableCustom { +public: + virtual uint32_t hash() const { + return 27; + } + + virtual String get_as_text() const { + return ""; + } + + static bool compare_equal_func(const CallableCustom *p_a, const CallableCustom *p_b) { + return p_a == p_b; + } + + virtual CompareEqualFunc get_compare_equal_func() const { + return &MyCallableCustom::compare_equal_func; + } + + static bool compare_less_func(const CallableCustom *p_a, const CallableCustom *p_b) { + return (void *)p_a < (void *)p_b; + } + + virtual CompareLessFunc get_compare_less_func() const { + return &MyCallableCustom::compare_less_func; + } + + bool is_valid() const { + return true; + } + + virtual ObjectID get_object() const { + return ObjectID(); + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const { + r_return_value = "Hi"; + r_call_error.error = GDEXTENSION_CALL_OK; + } +}; + void ExampleRef::set_id(int p_id) { id = p_id; } @@ -168,6 +208,7 @@ void Example::_bind_methods() { 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_custom_callable"), &Example::test_custom_callable); ClassDB::bind_method(D_METHOD("test_bitfield", "flags"), &Example::test_bitfield); @@ -378,6 +419,10 @@ Callable Example::test_callable_mp_static_ret() const { return callable_mp_static(&Example::unbound_static_method2); } +Callable Example::test_custom_callable() const { + return Callable(memnew(MyCallableCustom)); +} + void Example::unbound_method1(Object *p_object, String p_string, int p_int) { String test = "unbound_method1: "; test += p_object->get_class(); diff --git a/test/src/example.h b/test/src/example.h index 200c970e..388cc8cc 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -143,6 +143,7 @@ public: Callable test_callable_mp_retc() const; Callable test_callable_mp_static() const; Callable test_callable_mp_static_ret() const; + Callable test_custom_callable() 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);