Skip to content

Commit b4ce22a

Browse files
authored
Merge pull request #1973 from dsnopek/memnew-refcounted
Make `memnew(RefCounted)` return `Ref<T>` for improved ownership safety
2 parents 5e4200d + 1115359 commit b4ce22a

7 files changed

Lines changed: 46 additions & 11 deletions

File tree

include/godot_cpp/classes/ref.hpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ class Ref {
191191

192192
template <typename... VarArgs>
193193
void instantiate(VarArgs... p_params) {
194-
ref(memnew(T(p_params...)));
194+
Ref<T> ref = memnew(T(p_params...));
195+
SWAP(reference, ref.reference);
195196
}
196197

197198
uint32_t hash() const { return HashMapHasherDefault::hash(reference); }
@@ -211,6 +212,25 @@ class Ref {
211212
}
212213
};
213214

215+
template <typename T>
216+
struct memnew_result<T, std::enable_if_t<std::is_base_of_v<RefCounted, T>>> {
217+
#if GODOT_VERSION_MINOR >= 7
218+
using class_name = Ref<T>;
219+
_ALWAYS_INLINE_ static class_name capture(T *p_obj) {
220+
// Godot will have already incremented the refcount, so we can create the Ref<T> without incrementing it again.
221+
return Ref<T>::_gde_internal_constructor(p_obj);
222+
}
223+
#else
224+
using class_name = T *;
225+
_ALWAYS_INLINE_ static class_name capture(class_name p_obj) { return p_obj; }
226+
#endif
227+
};
228+
229+
template <typename T>
230+
void postinitialize_handler(Ref<T> &p_object) {
231+
postinitialize_handler(p_object.ptr());
232+
}
233+
214234
template <typename T>
215235
struct PtrToArg<Ref<T>> {
216236
_FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {

include/godot_cpp/core/class_db.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,9 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed, bool p_runtime) {
255255
class_register_order.push_back(cl.name);
256256

257257
// Register this class with Godot
258-
#if GODOT_VERSION_MINOR >= 5
258+
#if GODOT_VERSION_MINOR >= 7
259+
GDExtensionClassCreationInfo6 class_info = {
260+
#elif GODOT_VERSION_MINOR >= 5
259261
GDExtensionClassCreationInfo5 class_info = {
260262
#elif GODOT_VERSION_MINOR >= 4
261263
GDExtensionClassCreationInfo4 class_info = {
@@ -292,7 +294,9 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed, bool p_runtime) {
292294
(void *)&T::get_class_static(), // void *class_userdata;
293295
};
294296

295-
#if GODOT_VERSION_MINOR >= 5
297+
#if GODOT_VERSION_MINOR >= 7
298+
::godot::gdextension_interface::classdb_register_extension_class6(::godot::gdextension_interface::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info);
299+
#elif GODOT_VERSION_MINOR >= 5
296300
::godot::gdextension_interface::classdb_register_extension_class5(::godot::gdextension_interface::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info);
297301
#elif GODOT_VERSION_MINOR >= 4
298302
::godot::gdextension_interface::classdb_register_extension_class4(::godot::gdextension_interface::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info);

include/godot_cpp/core/memory.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,22 @@ class Memory {
8484
template <typename T, std::enable_if_t<!std::is_base_of<::godot::Wrapped, T>::value, bool> = true>
8585
_ALWAYS_INLINE_ void _pre_initialize() {}
8686

87+
template <typename T, typename Enable = void>
88+
struct memnew_result {
89+
using class_name = T *;
90+
_ALWAYS_INLINE_ static class_name capture(T *p_obj) { return p_obj; }
91+
};
92+
93+
template <typename T>
94+
using memnew_result_t = typename memnew_result<T>::class_name;
95+
8796
_ALWAYS_INLINE_ void postinitialize_handler(void *) {}
8897

8998
template <typename T>
90-
_ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
99+
_ALWAYS_INLINE_ memnew_result_t<T> _post_initialize(T *p_obj) {
100+
memnew_result_t<T> result(memnew_result<T>::capture(p_obj));
91101
postinitialize_handler(p_obj);
92-
return p_obj;
102+
return result;
93103
}
94104

95105
#define memalloc(m_size) ::godot::Memory::alloc_static(m_size)

src/classes/wrapped.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ Wrapped::Wrapped(const StringName &p_godot_class) {
8181
} else
8282
#endif
8383
{
84-
#if GODOT_VERSION_MINOR >= 4
84+
#if GODOT_VERSION_MINOR >= 7
85+
_owner = ::godot::gdextension_interface::classdb_construct_object3(reinterpret_cast<GDExtensionConstStringNamePtr>(p_godot_class._native_ptr()));
86+
#elif GODOT_VERSION_MINOR >= 4
8587
_owner = ::godot::gdextension_interface::classdb_construct_object2(reinterpret_cast<GDExtensionConstStringNamePtr>(p_godot_class._native_ptr()));
8688
#else
8789
_owner = ::godot::gdextension_interface::classdb_construct_object(reinterpret_cast<GDExtensionConstStringNamePtr>(p_godot_class._native_ptr()));

test/project/main.gd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func _ready():
4444
# Pass custom reference.
4545
assert_equal(example.custom_ref_func(null), -1)
4646
var ref1 = ExampleRef.new()
47+
assert_equal(ref1.get_reference_count(), 1)
4748
ref1.id = 27
4849
assert_equal(example.custom_ref_func(ref1), 27)
4950
ref1.id += 1;
@@ -63,6 +64,7 @@ func _ready():
6364
assert_equal(null_ref, null)
6465
var ret_ref = example.return_extended_ref()
6566
assert_not_equal(ret_ref.get_instance_id(), 0)
67+
assert_equal(ret_ref.get_reference_count(), 1)
6668
assert_equal(ret_ref.get_id(), 0)
6769
assert_equal(example.get_v4(), Vector4(1.2, 3.4, 5.6, 7.8))
6870
assert_equal(example.test_node_argument(example), example)

test/src/example.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,7 @@ Ref<ExampleRef> Example::return_empty_ref() const {
379379
return ref;
380380
}
381381

382-
ExampleRef *Example::return_extended_ref() const {
383-
// You can instance and return a refcounted object like this, but keep in mind that refcounting starts with the returned object
384-
// and it will be destroyed when all references are destroyed. If you store this pointer you run the risk of having a pointer
385-
// to a destroyed object.
382+
Ref<ExampleRef> Example::return_extended_ref() const {
386383
return memnew(ExampleRef());
387384
}
388385

test/src/example.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class Example : public Control {
124124
Viewport *return_something_const() const;
125125
Ref<ExampleRef> return_ref() const;
126126
Ref<ExampleRef> return_empty_ref() const;
127-
ExampleRef *return_extended_ref() const;
127+
Ref<ExampleRef> return_extended_ref() const;
128128
Ref<ExampleRef> extended_ref_checks(Ref<ExampleRef> p_ref) const;
129129
Variant varargs_func(const Variant **args, GDExtensionInt arg_count, GDExtensionCallError &error);
130130
int varargs_func_nv(const Variant **args, GDExtensionInt arg_count, GDExtensionCallError &error);

0 commit comments

Comments
 (0)