Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions binding_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,8 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
used_classes = list(used_classes)
used_classes.sort()
fully_used_classes = list(fully_used_classes)
if class_api["name"] == "RefCounted":
fully_used_classes.remove("Ref") # Special case; Ref should include RefCounted but not vice versa.
fully_used_classes.sort()

with header_filename.open("w+", encoding="utf-8") as header_file:
Expand Down
142 changes: 62 additions & 80 deletions include/godot_cpp/classes/ref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,30 +42,34 @@ namespace godot {

// Helper class for RefCounted objects, same as Godot one.

class RefCounted;

template <typename T>
class Ref {
T *reference = nullptr;

void ref(const Ref &p_from) {
if (p_from.reference == reference) {
_FORCE_INLINE_ void ref(const Ref &p_from) {
ref_pointer<false>(p_from.reference);
}

template <bool Init>
_FORCE_INLINE_ void ref_pointer(T *p_refcounted) {
if (p_refcounted == reference) {
return;
}

unref();

reference = p_from.reference;
// This will go out of scope and get unref'd.
Ref cleanup_ref;
cleanup_ref.reference = reference;
reference = p_refcounted;
if (reference) {
reference->reference();
}
}

void ref_pointer(T *p_ref) {
ERR_FAIL_NULL(p_ref);

if (p_ref->init_ref()) {
reference = p_ref;
if constexpr (Init) {
if (!reference->init_ref()) {
reference = nullptr;
}
} else {
if (!reference->reference()) {
reference = nullptr;
}
}
}
}

Expand Down Expand Up @@ -111,110 +115,88 @@ class Ref {
ref(p_from);
}

template <typename T_Other>
void operator=(const Ref<T_Other> &p_from) {
RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr()));
if (!refb) {
unref();
void operator=(Ref &&p_from) {
if (reference == p_from.reference) {
return;
}
unref();
reference = p_from.reference;
p_from.reference = nullptr;
}

Ref r;
r.reference = Object::cast_to<T>(refb);
ref(r);
r.reference = nullptr;
template <typename T_Other>
void operator=(const Ref<T_Other> &p_from) {
ref_pointer<false>(Object::cast_to<T>(p_from.ptr()));
}

void operator=(const Variant &p_variant) {
// Needs testing, Variant has a cast to Object * here.
void operator=(T *p_from) {
ref_pointer<true>(p_from);
}

// Object *object = p_variant.get_validated_object();
Object *object = p_variant;
void operator=(const Variant &p_variant) {
Object *object = p_variant.get_validated_object();

if (object == reference) {
return;
}

unref();

if (!object) {
return;
}

T *r = Object::cast_to<T>(object);
if (r && r->reference()) {
reference = r;
}
ref_pointer<false>(Object::cast_to<T>(object));
}

template <typename T_Other>
void reference_ptr(T_Other *p_ptr) {
if (reference == p_ptr) {
return;
}
unref();

T *r = Object::cast_to<T>(p_ptr);
if (r) {
ref_pointer(r);
}
ref_pointer<true>(Object::cast_to<T>(p_ptr));
}

Ref(const Ref &p_from) {
ref(p_from);
this->operator=(p_from);
}

Ref(Ref &&p_from) {
reference = p_from.reference;
p_from.reference = nullptr;
}

template <typename T_Other>
Ref(const Ref<T_Other> &p_from) {
RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr()));
if (!refb) {
unref();
return;
}

Ref r;
r.reference = Object::cast_to<T>(refb);
ref(r);
r.reference = nullptr;
this->operator=(p_from);
}

Ref(T *p_reference) {
if (p_reference) {
ref_pointer(p_reference);
}
Ref(T *p_from) {
this->operator=(p_from);
}

Ref(const Variant &p_variant) {
// Needs testing, Variant has a cast to Object * here.

// Object *object = p_variant.get_validated_object();
Object *object = p_variant;

if (!object) {
return;
}

T *r = Object::cast_to<T>(object);
if (r && r->reference()) {
reference = r;
}
Ref(const Variant &p_from) {
this->operator=(p_from);
}

inline bool is_valid() const { return reference != nullptr; }
inline bool is_null() const { return reference == nullptr; }

void unref() {
if (reference && reference->unreference()) {
memdelete(reference);
if (reference) {
// NOTE: `reinterpret_cast` is "safe" here because we know `T` has simple linear
// inheritance to `RefCounted`. This guarantees that `T * == `RefCounted *`, which
// allows us to declare `Ref<T>` with forward declared `T` types.
if (reinterpret_cast<RefCounted *>(reference)->unreference()) {
memdelete(reinterpret_cast<RefCounted *>(reference));
}
reference = nullptr;
}
reference = nullptr;
}

void instantiate() {
ref(memnew(T()));
template <typename... VarArgs>
void instantiate(VarArgs... p_params) {
ref(memnew(T(p_params...)));
}

Ref() {}
uint32_t hash() const { return HashMapHasherDefault::hash(reference); }

Ref() = default;

~Ref() {
unref();
Expand All @@ -224,7 +206,7 @@ class Ref {
// without adding to the refcount.
inline static Ref<T> _gde_internal_constructor(Object *obj) {
Ref<T> r;
r.reference = (T *)obj;
r.reference = reinterpret_cast<T *>(obj);
return r;
}
};
Expand Down
Loading