Browse Source

Merge pull request #1446 from Daylily-Zeleen/daylily-zeleen/set_instance_and_instance_biding_in_Wrapped_constructor

Set instance and instance binding in `Wrapped` constructor.
David Snopek 1 year ago
parent
commit
5d8f80bc55

+ 29 - 13
include/godot_cpp/classes/wrapped.hpp

@@ -46,13 +46,31 @@ class ClassDB;
 
 
 typedef void GodotObject;
 typedef void GodotObject;
 
 
+template <typename T, std::enable_if_t<std::is_base_of<::godot::Wrapped, T>::value, bool> = true>
+_ALWAYS_INLINE_ void _pre_initialize();
+
 // Base for all engine classes, to contain the pointer to the engine instance.
 // Base for all engine classes, to contain the pointer to the engine instance.
 class Wrapped {
 class Wrapped {
 	friend class GDExtensionBinding;
 	friend class GDExtensionBinding;
 	friend class ClassDB;
 	friend class ClassDB;
 	friend void postinitialize_handler(Wrapped *);
 	friend void postinitialize_handler(Wrapped *);
 
 
+	template <typename T, std::enable_if_t<std::is_base_of<::godot::Wrapped, T>::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;
+
+	template <typename T>
+	_ALWAYS_INLINE_ static void _set_construct_info() {
+		_constructing_extension_class_name = T::_get_extension_class_name();
+		_constructing_class_binding_callbacks = &T::_gde_binding_callbacks;
+	}
+
 protected:
 protected:
+	virtual bool _is_extension_class() const { return false; }
+	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.
+
 #ifdef HOT_RELOAD_ENABLED
 #ifdef HOT_RELOAD_ENABLED
 	struct RecreateInstance {
 	struct RecreateInstance {
 		GDExtensionClassInstancePtr wrapper;
 		GDExtensionClassInstancePtr wrapper;
@@ -62,9 +80,6 @@ protected:
 	inline static RecreateInstance *recreate_instance = nullptr;
 	inline static RecreateInstance *recreate_instance = nullptr;
 #endif
 #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) {}
 	void _notification(int p_what) {}
 	bool _set(const StringName &p_name, const Variant &p_property) { return false; }
 	bool _set(const StringName &p_name, const Variant &p_property) { return false; }
 	bool _get(const StringName &p_name, Variant &r_property) const { return false; }
 	bool _get(const StringName &p_name, Variant &r_property) const { return false; }
@@ -109,6 +124,11 @@ public:
 	GodotObject *_owner = nullptr;
 	GodotObject *_owner = nullptr;
 };
 };
 
 
+template <typename T, std::enable_if_t<std::is_base_of<::godot::Wrapped, T>::value, bool>>
+_ALWAYS_INLINE_ void _pre_initialize() {
+	Wrapped::_set_construct_info<T>();
+}
+
 _FORCE_INLINE_ void snarray_add_str(Vector<StringName> &arr) {
 _FORCE_INLINE_ void snarray_add_str(Vector<StringName> &arr) {
 }
 }
 
 
@@ -161,15 +181,14 @@ struct EngineClassRegistration {
 private:                                                                                                                                                                               \
 private:                                                                                                                                                                               \
 	void operator=(const m_class &p_rval) {}                                                                                                                                           \
 	void operator=(const m_class &p_rval) {}                                                                                                                                           \
 	friend class ::godot::ClassDB;                                                                                                                                                     \
 	friend class ::godot::ClassDB;                                                                                                                                                     \
+	friend class ::godot::Wrapped;                                                                                                                                                     \
                                                                                                                                                                                        \
                                                                                                                                                                                        \
 protected:                                                                                                                                                                             \
 protected:                                                                                                                                                                             \
-	virtual const ::godot::StringName *_get_extension_class_name() const override {                                                                                                    \
-		static ::godot::StringName string_name = get_class_static();                                                                                                                   \
-		return &string_name;                                                                                                                                                           \
-	}                                                                                                                                                                                  \
+	virtual bool _is_extension_class() const override { return true; }                                                                                                                 \
                                                                                                                                                                                        \
                                                                                                                                                                                        \
-	virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override {                                                                                      \
-		return &_gde_binding_callbacks;                                                                                                                                                \
+	static const ::godot::StringName *_get_extension_class_name() {                                                                                                                    \
+		const ::godot::StringName &string_name = get_class_static();                                                                                                                   \
+		return &string_name;                                                                                                                                                           \
 	}                                                                                                                                                                                  \
 	}                                                                                                                                                                                  \
                                                                                                                                                                                        \
                                                                                                                                                                                        \
 	static void (*_get_bind_methods())() {                                                                                                                                             \
 	static void (*_get_bind_methods())() {                                                                                                                                             \
@@ -388,12 +407,9 @@ private:
 	inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper;                                                                           \
 	inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper;                                                                           \
 	void operator=(const m_class &p_rval) {}                                                                                                                                           \
 	void operator=(const m_class &p_rval) {}                                                                                                                                           \
 	friend class ::godot::ClassDB;                                                                                                                                                     \
 	friend class ::godot::ClassDB;                                                                                                                                                     \
+	friend class ::godot::Wrapped;                                                                                                                                                     \
                                                                                                                                                                                        \
                                                                                                                                                                                        \
 protected:                                                                                                                                                                             \
 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(const char *p_godot_class) : m_inherits(p_godot_class) {}                                                                                                                  \
 	m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {}                                                                                                               \
 	m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {}                                                                                                               \
                                                                                                                                                                                        \
                                                                                                                                                                                        \

+ 6 - 3
include/godot_cpp/core/memory.hpp

@@ -82,6 +82,9 @@ public:
 	static void free_static(void *p_ptr, bool p_pad_align = false);
 	static void free_static(void *p_ptr, bool p_pad_align = false);
 };
 };
 
 
+template <typename T, std::enable_if_t<!std::is_base_of<::godot::Wrapped, T>::value, bool> = true>
+_ALWAYS_INLINE_ void _pre_initialize() {}
+
 _ALWAYS_INLINE_ void postinitialize_handler(void *) {}
 _ALWAYS_INLINE_ void postinitialize_handler(void *) {}
 
 
 template <typename T>
 template <typename T>
@@ -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 memrealloc(m_mem, m_size) ::godot::Memory::realloc_static(m_mem, m_size)
 #define memfree(m_mem) ::godot::Memory::free_static(m_mem)
 #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<std::remove_pointer_t<decltype(new ("", "") m_class)>>(), ::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<std::remove_pointer_t<decltype(new ("", "") m_class)>>(), ::godot::_post_initialize(new ("", m_allocator::alloc) m_class))
+#define memnew_placement(m_placement, m_class) (::godot::_pre_initialize<std::remove_pointer_t<decltype(new ("", "") m_class)>>(), ::godot::_post_initialize(new ("", m_placement, sizeof(m_class), "") m_class))
 
 
 // Generic comparator used in Map, List, etc.
 // Generic comparator used in Map, List, etc.
 template <typename T>
 template <typename T>

+ 18 - 7
src/classes/wrapped.cpp

@@ -39,18 +39,16 @@
 #include <godot_cpp/core/class_db.hpp>
 #include <godot_cpp/core/class_db.hpp>
 
 
 namespace godot {
 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;
 	return nullptr;
 }
 }
 
 
 void Wrapped::_postinitialize() {
 void Wrapped::_postinitialize() {
-	const StringName *extension_class = _get_extension_class_name();
-	if (extension_class) {
-		godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast<GDExtensionConstStringNamePtr>(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);
 		_notificationv(Object::NOTIFICATION_POSTINITIALIZE);
 	}
 	}
 }
 }
@@ -76,6 +74,19 @@ Wrapped::Wrapped(const StringName p_godot_class) {
 	}
 	}
 #endif
 #endif
 	_owner = godot::internal::gdextension_interface_classdb_construct_object(reinterpret_cast<GDExtensionConstStringNamePtr>(p_godot_class._native_ptr()));
 	_owner = godot::internal::gdextension_interface_classdb_construct_object(reinterpret_cast<GDExtensionConstStringNamePtr>(p_godot_class._native_ptr()));
+
+	if (_constructing_extension_class_name) {
+		godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast<GDExtensionConstStringNamePtr>(_constructing_extension_class_name), this);
+		_constructing_extension_class_name = nullptr;
+	}
+
+	if (likely(_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;
+	} else {
+		ERR_PRINT("BUG: create a Godot Object without binding callbacks.");
+		CRASH_NOW_MSG("BUG: create a Godot Object without binding callbacks.");
+	}
 }
 }
 
 
 Wrapped::Wrapped(GodotObject *p_godot_object) {
 Wrapped::Wrapped(GodotObject *p_godot_object) {

+ 3 - 0
test/project/main.gd

@@ -9,6 +9,9 @@ class TestClass:
 func _ready():
 func _ready():
 	var example: Example = $Example
 	var example: Example = $Example
 
 
+	# Timing of set instance binding.
+	assert_equal(example.is_object_binding_set_by_parent_constructor(), true)
+
 	# Signal.
 	# Signal.
 	example.emit_custom_signal("Button", 42)
 	example.emit_custom_signal("Button", 42)
 	assert_equal(custom_signal_emitted, ["Button", 42])
 	assert_equal(custom_signal_emitted, ["Button", 42])

+ 17 - 1
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("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("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_array"), &Example::test_array);
 	ClassDB::bind_method(D_METHOD("test_tarray_arg", "array"), &Example::test_tarray_arg);
 	ClassDB::bind_method(D_METHOD("test_tarray_arg", "array"), &Example::test_tarray_arg);
 	ClassDB::bind_method(D_METHOD("test_tarray"), &Example::test_tarray);
 	ClassDB::bind_method(D_METHOD("test_tarray"), &Example::test_tarray);
@@ -291,7 +293,17 @@ void Example::_bind_methods() {
 	BIND_ENUM_CONSTANT(OUTSIDE_OF_CLASS);
 	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.");
 	//UtilityFunctions::print("Constructor.");
 }
 }
 
 
@@ -371,6 +383,10 @@ void Example::emit_custom_signal(const String &name, int value) {
 	emit_signal("custom_signal", name, 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 Example::test_array() const {
 	Array arr;
 	Array arr;
 
 

+ 5 - 0
test/src/example.h

@@ -82,6 +82,9 @@ private:
 	Vector2 dprop[3];
 	Vector2 dprop[3];
 	int last_rpc_arg = 0;
 	int last_rpc_arg = 0;
 
 
+	const bool object_instance_binding_set_by_parent_constructor;
+	bool has_object_instance_binding() const;
+
 public:
 public:
 	// Constants.
 	// Constants.
 	enum Constants {
 	enum Constants {
@@ -120,6 +123,8 @@ public:
 	void emit_custom_signal(const String &name, int value);
 	void emit_custom_signal(const String &name, int value);
 	int def_args(int p_a = 100, int p_b = 200);
 	int def_args(int p_a = 100, int p_b = 200);
 
 
+	bool is_object_binding_set_by_parent_constructor() const;
+
 	Array test_array() const;
 	Array test_array() const;
 	int test_tarray_arg(const TypedArray<int64_t> &p_array);
 	int test_tarray_arg(const TypedArray<int64_t> &p_array);
 	TypedArray<Vector2> test_tarray() const;
 	TypedArray<Vector2> test_tarray() const;