From de0b3d07156b3deaea7b660923e86acaa439f3a2 Mon Sep 17 00:00:00 2001 From: Daylily-Zeleen Date: Wed, 24 Apr 2024 22:14:33 +0800 Subject: [PATCH] Set instance and instance binding in Wrapped constructor. --- include/godot_cpp/classes/wrapped.hpp | 46 +++++++++++++++++---------- include/godot_cpp/core/memory.hpp | 9 ++++-- src/classes/wrapped.cpp | 22 +++++++++---- test/project/main.gd | 3 ++ test/src/example.cpp | 18 ++++++++++- test/src/example.h | 5 +++ test/src/register_types.cpp | 17 ++++++++++ 7 files changed, 92 insertions(+), 28 deletions(-) diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp index 8702fb130f..7dc6775ecf 100644 --- a/include/godot_cpp/classes/wrapped.hpp +++ b/include/godot_cpp/classes/wrapped.hpp @@ -41,18 +41,33 @@ #include namespace godot { - class ClassDB; typedef void GodotObject; +template ::value, bool> = true> +_ALWAYS_INLINE_ void _pre_initialize(); + // Base for all engine classes, to contain the pointer to the engine instance. class Wrapped { friend class GDExtensionBinding; friend class ClassDB; friend void postinitialize_handler(Wrapped *); + template ::value, bool>> + friend _ALWAYS_INLINE_ void _pre_initialize(); + + thread_local static const StringName *_constructing_extension_class_name; + thread_local static const GDExtensionInstanceBindingCallbacks *_constructing_class_binding_callbacks; + + _ALWAYS_INLINE_ static void _set_construct_info(const StringName *p_extension_class_name, const GDExtensionInstanceBindingCallbacks *p_binding_callbacks) { + _constructing_extension_class_name = p_extension_class_name; + _constructing_class_binding_callbacks = p_binding_callbacks; + } + protected: + virtual bool _is_extension_class() const { return false; } + #ifdef HOT_RELOAD_ENABLED struct RecreateInstance { GDExtensionClassInstancePtr wrapper; @@ -62,9 +77,6 @@ class Wrapped { inline static RecreateInstance *recreate_instance = nullptr; #endif - virtual const StringName *_get_extension_class_name() const; // This is needed to retrieve the class name before the godot object has its _extension and _extension_instance members assigned. - virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const = 0; - void _notification(int p_what) {} bool _set(const StringName &p_name, const Variant &p_property) { return false; } bool _get(const StringName &p_name, Variant &r_property) const { return false; } @@ -96,6 +108,8 @@ class Wrapped { virtual ~Wrapped() {} public: + static const StringName *_get_extension_class_name(); // This is needed to retrieve the class name before the godot object has its _extension and _extension_instance members assigned. + static const StringName &get_class_static() { static const StringName string_name = StringName("Wrapped"); return string_name; @@ -109,6 +123,11 @@ class Wrapped { GodotObject *_owner = nullptr; }; +template ::value, bool> = true> +_ALWAYS_INLINE_ void _pre_initialize() { + Wrapped::_set_construct_info(T::_get_extension_class_name(), &T::_gde_binding_callbacks); +} + _FORCE_INLINE_ void snarray_add_str(Vector &arr) { } @@ -163,15 +182,7 @@ private: friend class ::godot::ClassDB; \ \ protected: \ - virtual const ::godot::StringName *_get_extension_class_name() const override { \ - static ::godot::StringName string_name = get_class_static(); \ - return &string_name; \ - } \ - \ - virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \ - return &_gde_binding_callbacks; \ - } \ - \ + virtual bool _is_extension_class() const override { return true; } \ static void (*_get_bind_methods())() { \ return &m_class::_bind_methods; \ } \ @@ -214,6 +225,11 @@ protected: } \ \ public: \ + static const ::godot::StringName *_get_extension_class_name() { \ + const ::godot::StringName &string_name = get_class_static(); \ + return &string_name; \ + } \ + \ typedef m_class self_type; \ typedef m_inherits parent_type; \ \ @@ -390,10 +406,6 @@ private: friend class ::godot::ClassDB; \ \ protected: \ - virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \ - return &_gde_binding_callbacks; \ - } \ - \ m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \ m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \ \ diff --git a/include/godot_cpp/core/memory.hpp b/include/godot_cpp/core/memory.hpp index 3c98c194c3..1934ee458b 100644 --- a/include/godot_cpp/core/memory.hpp +++ b/include/godot_cpp/core/memory.hpp @@ -82,6 +82,9 @@ class Memory { static void free_static(void *p_ptr, bool p_pad_align = false); }; +template ::value, bool> = true> +_ALWAYS_INLINE_ void _pre_initialize() {} + _ALWAYS_INLINE_ void postinitialize_handler(void *) {} template @@ -94,10 +97,10 @@ _ALWAYS_INLINE_ T *_post_initialize(T *p_obj) { #define memrealloc(m_mem, m_size) ::godot::Memory::realloc_static(m_mem, m_size) #define memfree(m_mem) ::godot::Memory::free_static(m_mem) -#define memnew(m_class) ::godot::_post_initialize(new ("", "") m_class) +#define memnew(m_class) (::godot::_pre_initialize>(), ::godot::_post_initialize(new ("", "") m_class)) -#define memnew_allocator(m_class, m_allocator) ::godot::_post_initialize(new ("", m_allocator::alloc) m_class) -#define memnew_placement(m_placement, m_class) ::godot::_post_initialize(new ("", m_placement, sizeof(m_class), "") m_class) +#define memnew_allocator(m_class, m_allocator) (::godot::_pre_initialize>(), ::godot::_post_initialize(new ("", m_allocator::alloc) m_class)) +#define memnew_placement(m_placement, m_class) (::godot::_pre_initialize>(), ::godot::_post_initialize(new ("", m_placement, sizeof(m_class), "") m_class)) // Generic comparator used in Map, List, etc. template diff --git a/src/classes/wrapped.cpp b/src/classes/wrapped.cpp index 594cfefffd..bdcf4d6aac 100644 --- a/src/classes/wrapped.cpp +++ b/src/classes/wrapped.cpp @@ -39,18 +39,16 @@ #include namespace godot { +thread_local const StringName *Wrapped::_constructing_extension_class_name = nullptr; +thread_local const GDExtensionInstanceBindingCallbacks *Wrapped::_constructing_class_binding_callbacks = nullptr; -const StringName *Wrapped::_get_extension_class_name() const { +const StringName *Wrapped::_get_extension_class_name() { return nullptr; } void Wrapped::_postinitialize() { - const StringName *extension_class = _get_extension_class_name(); - if (extension_class) { - godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast(extension_class), this); - } - godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks()); - if (extension_class) { + // Only send NOTIFICATION_POSTINITIALIZE for extension classes. + if (_is_extension_class()) { _notificationv(Object::NOTIFICATION_POSTINITIALIZE); } } @@ -76,6 +74,16 @@ Wrapped::Wrapped(const StringName p_godot_class) { } #endif _owner = godot::internal::gdextension_interface_classdb_construct_object(reinterpret_cast(p_godot_class._native_ptr())); + + if (_constructing_extension_class_name) { + godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast(_constructing_extension_class_name), this); + _constructing_extension_class_name = nullptr; + } + + if (_constructing_class_binding_callbacks) { + godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _constructing_class_binding_callbacks); + _constructing_class_binding_callbacks = nullptr; + } } Wrapped::Wrapped(GodotObject *p_godot_object) { diff --git a/test/project/main.gd b/test/project/main.gd index 665582fd5c..d02a86bdab 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -9,6 +9,9 @@ class TestClass: func _ready(): var example: Example = $Example + # Timing of set instance binding. + assert_equal(example.is_object_binding_set_by_parent_constructor(), true) + # Signal. example.emit_custom_signal("Button", 42) assert_equal(custom_signal_emitted, ["Button", 42]) diff --git a/test/src/example.cpp b/test/src/example.cpp index 78d706225f..52d2bd34e5 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -193,6 +193,8 @@ void Example::_bind_methods() { ClassDB::bind_method(D_METHOD("return_extended_ref"), &Example::return_extended_ref); ClassDB::bind_method(D_METHOD("extended_ref_checks", "ref"), &Example::extended_ref_checks); + ClassDB::bind_method(D_METHOD("is_object_binding_set_by_parent_constructor"), &Example::is_object_binding_set_by_parent_constructor); + ClassDB::bind_method(D_METHOD("test_array"), &Example::test_array); ClassDB::bind_method(D_METHOD("test_tarray_arg", "array"), &Example::test_tarray_arg); ClassDB::bind_method(D_METHOD("test_tarray"), &Example::test_tarray); @@ -290,7 +292,17 @@ void Example::_bind_methods() { BIND_ENUM_CONSTANT(OUTSIDE_OF_CLASS); } -Example::Example() { +bool Example::has_object_instance_binding() const { + return internal::gdextension_interface_object_get_instance_binding(_owner, internal::token, nullptr); +} + +Example::Example() : + object_instance_binding_set_by_parent_constructor(has_object_instance_binding()) { + // Test conversion, to ensure users can use all parent calss functions at this time. + // It would crash if instance binding still not be initialized. + Variant v = Variant(this); + Object *o = (Object *)v; + //UtilityFunctions::print("Constructor."); } @@ -370,6 +382,10 @@ void Example::emit_custom_signal(const String &name, int value) { emit_signal("custom_signal", name, value); } +bool Example::is_object_binding_set_by_parent_constructor() const { + return object_instance_binding_set_by_parent_constructor; +} + Array Example::test_array() const { Array arr; diff --git a/test/src/example.h b/test/src/example.h index 1af4e5faaa..3d178c4804 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -82,6 +82,9 @@ class Example : public Control { Vector2 dprop[3]; int last_rpc_arg = 0; + const bool object_instance_binding_set_by_parent_constructor; + bool has_object_instance_binding() const; + public: // Constants. enum Constants { @@ -120,6 +123,8 @@ class Example : public Control { void emit_custom_signal(const String &name, int value); int def_args(int p_a = 100, int p_b = 200); + bool is_object_binding_set_by_parent_constructor() const; + Array test_array() const; int test_tarray_arg(const TypedArray &p_array); TypedArray test_tarray() const; diff --git a/test/src/register_types.cpp b/test/src/register_types.cpp index 7cfe689e0e..31a055109a 100644 --- a/test/src/register_types.cpp +++ b/test/src/register_types.cpp @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -16,10 +17,26 @@ using namespace godot; +class MyLineEdit : public LineEdit { + GDCLASS(MyLineEdit, LineEdit) +protected: + static void _bind_methods() {} + +public: + void _on_text_submitted(const String &p_test) { + UtilityFunctions::print("Submitted: ", p_test); + } + + MyLineEdit() { + connect("text_submitted", callable_mp(this, &MyLineEdit::_on_text_submitted)); + } +}; + void initialize_example_module(ModuleInitializationLevel p_level) { if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { return; } + GDREGISTER_CLASS(MyLineEdit); GDREGISTER_CLASS(ExampleRef); GDREGISTER_CLASS(ExampleMin);