diff --git a/include/godot_cpp/classes/ref.hpp b/include/godot_cpp/classes/ref.hpp index d6c77a8bf..6e0d874b6 100644 --- a/include/godot_cpp/classes/ref.hpp +++ b/include/godot_cpp/classes/ref.hpp @@ -191,7 +191,8 @@ class Ref { template void instantiate(VarArgs... p_params) { - ref(memnew(T(p_params...))); + Ref ref = memnew(T(p_params...)); + SWAP(reference, ref.reference); } uint32_t hash() const { return HashMapHasherDefault::hash(reference); } @@ -211,6 +212,25 @@ class Ref { } }; +template +struct memnew_result>> { +#if GODOT_VERSION_MINOR >= 7 + using class_name = Ref; + _ALWAYS_INLINE_ static class_name capture(T *p_obj) { + // Godot will have already incremented the refcount, so we can create the Ref without incrementing it again. + return Ref::_gde_internal_constructor(p_obj); + } +#else + using class_name = T *; + _ALWAYS_INLINE_ static class_name capture(class_name p_obj) { return p_obj; } +#endif +}; + +template +void postinitialize_handler(Ref &p_object) { + postinitialize_handler(p_object.ptr()); +} + template struct PtrToArg> { _FORCE_INLINE_ static Ref convert(const void *p_ptr) { diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index 749e47504..c2fcb08cf 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -255,7 +255,9 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed, bool p_runtime) { class_register_order.push_back(cl.name); // Register this class with Godot -#if GODOT_VERSION_MINOR >= 5 +#if GODOT_VERSION_MINOR >= 7 + GDExtensionClassCreationInfo6 class_info = { +#elif GODOT_VERSION_MINOR >= 5 GDExtensionClassCreationInfo5 class_info = { #elif GODOT_VERSION_MINOR >= 4 GDExtensionClassCreationInfo4 class_info = { @@ -292,7 +294,9 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed, bool p_runtime) { (void *)&T::get_class_static(), // void *class_userdata; }; -#if GODOT_VERSION_MINOR >= 5 +#if GODOT_VERSION_MINOR >= 7 + ::godot::gdextension_interface::classdb_register_extension_class6(::godot::gdextension_interface::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info); +#elif GODOT_VERSION_MINOR >= 5 ::godot::gdextension_interface::classdb_register_extension_class5(::godot::gdextension_interface::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info); #elif GODOT_VERSION_MINOR >= 4 ::godot::gdextension_interface::classdb_register_extension_class4(::godot::gdextension_interface::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info); diff --git a/include/godot_cpp/core/memory.hpp b/include/godot_cpp/core/memory.hpp index dd987378c..9b367d9d8 100644 --- a/include/godot_cpp/core/memory.hpp +++ b/include/godot_cpp/core/memory.hpp @@ -84,12 +84,22 @@ class Memory { template ::value, bool> = true> _ALWAYS_INLINE_ void _pre_initialize() {} +template +struct memnew_result { + using class_name = T *; + _ALWAYS_INLINE_ static class_name capture(T *p_obj) { return p_obj; } +}; + +template +using memnew_result_t = typename memnew_result::class_name; + _ALWAYS_INLINE_ void postinitialize_handler(void *) {} template -_ALWAYS_INLINE_ T *_post_initialize(T *p_obj) { +_ALWAYS_INLINE_ memnew_result_t _post_initialize(T *p_obj) { + memnew_result_t result(memnew_result::capture(p_obj)); postinitialize_handler(p_obj); - return p_obj; + return result; } #define memalloc(m_size) ::godot::Memory::alloc_static(m_size) diff --git a/src/classes/wrapped.cpp b/src/classes/wrapped.cpp index 38b485de4..bc9c956a5 100644 --- a/src/classes/wrapped.cpp +++ b/src/classes/wrapped.cpp @@ -81,7 +81,9 @@ Wrapped::Wrapped(const StringName &p_godot_class) { } else #endif { -#if GODOT_VERSION_MINOR >= 4 +#if GODOT_VERSION_MINOR >= 7 + _owner = ::godot::gdextension_interface::classdb_construct_object3(reinterpret_cast(p_godot_class._native_ptr())); +#elif GODOT_VERSION_MINOR >= 4 _owner = ::godot::gdextension_interface::classdb_construct_object2(reinterpret_cast(p_godot_class._native_ptr())); #else _owner = ::godot::gdextension_interface::classdb_construct_object(reinterpret_cast(p_godot_class._native_ptr())); diff --git a/test/project/main.gd b/test/project/main.gd index 064876217..52d7c0f13 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -44,6 +44,7 @@ func _ready(): # Pass custom reference. assert_equal(example.custom_ref_func(null), -1) var ref1 = ExampleRef.new() + assert_equal(ref1.get_reference_count(), 1) ref1.id = 27 assert_equal(example.custom_ref_func(ref1), 27) ref1.id += 1; @@ -63,6 +64,7 @@ func _ready(): assert_equal(null_ref, null) var ret_ref = example.return_extended_ref() assert_not_equal(ret_ref.get_instance_id(), 0) + assert_equal(ret_ref.get_reference_count(), 1) assert_equal(ret_ref.get_id(), 0) assert_equal(example.get_v4(), Vector4(1.2, 3.4, 5.6, 7.8)) assert_equal(example.test_node_argument(example), example) diff --git a/test/src/example.cpp b/test/src/example.cpp index f1bc687e5..43f781c4e 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -379,10 +379,7 @@ Ref Example::return_empty_ref() const { return ref; } -ExampleRef *Example::return_extended_ref() const { - // You can instance and return a refcounted object like this, but keep in mind that refcounting starts with the returned object - // and it will be destroyed when all references are destroyed. If you store this pointer you run the risk of having a pointer - // to a destroyed object. +Ref Example::return_extended_ref() const { return memnew(ExampleRef()); } diff --git a/test/src/example.h b/test/src/example.h index 4e6208361..d75abbd8f 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -124,7 +124,7 @@ class Example : public Control { Viewport *return_something_const() const; Ref return_ref() const; Ref return_empty_ref() const; - ExampleRef *return_extended_ref() const; + Ref return_extended_ref() const; Ref extended_ref_checks(Ref p_ref) const; Variant varargs_func(const Variant **args, GDExtensionInt arg_count, GDExtensionCallError &error); int varargs_func_nv(const Variant **args, GDExtensionInt arg_count, GDExtensionCallError &error);