AronaCore/core/misc/ref_counting.h
2024-02-19 01:33:18 +08:00

173 lines
4.6 KiB
C++

#pragma once
#include <assert.h>
/**
* A smart pointer to an object which implements AddRef/Release.
*/
template<typename ReferencedType>
class ref_count_ptr {
typedef ReferencedType* reference_type;
public:
ref_count_ptr(): reference_(nullptr) {
}
explicit ref_count_ptr(ReferencedType* in_reference, bool bAddRef = true) {
reference_ = in_reference;
if (reference_ && bAddRef) {
reference_->AddRef();
}
}
ref_count_ptr(const ref_count_ptr& copy) {
reference_ = copy.reference_;
if (reference_) {
reference_->AddRef();
}
}
template<typename CopyReferencedType>
explicit ref_count_ptr(const ref_count_ptr<CopyReferencedType>& copy) {
reference_ = static_cast<ReferencedType*>(copy.get_reference());
if (reference_) {
reference_->AddRef();
}
}
ref_count_ptr(ref_count_ptr&& move) noexcept {
reference_ = move.reference_;
move.reference_ = nullptr;
}
template<typename MoveReferencedType>
explicit ref_count_ptr(ref_count_ptr<MoveReferencedType>&& move) {
reference_ = static_cast<ReferencedType*>(move.get_reference());
move.reference_ = nullptr;
}
~ref_count_ptr() {
if (reference_) {
reference_->Release();
}
}
ref_count_ptr& operator=(ReferencedType* in_reference) {
if (reference_ != in_reference) {
// Call AddRef before Release, in case the new reference is the same as the old reference.
ReferencedType* old_reference = reference_;
reference_ = in_reference;
if (reference_) {
reference_->AddRef();
}
if (old_reference) {
old_reference->Release();
}
}
return *this;
}
ref_count_ptr& operator=(const ref_count_ptr& in_ptr) {
return *this = in_ptr.reference_;
}
template<typename CopyReferencedType>
ref_count_ptr& operator=(const ref_count_ptr<CopyReferencedType>& in_ptr) {
return *this = in_ptr.GetReference();
}
ref_count_ptr& operator=(ref_count_ptr&& in_ptr) noexcept {
if (this != &in_ptr) {
ReferencedType* old_reference = reference_;
reference_ = in_ptr.reference_;
in_ptr.reference_ = nullptr;
if (old_reference) {
old_reference->Release();
}
}
return *this;
}
template<typename MoveReferencedType>
ref_count_ptr& operator=(ref_count_ptr<MoveReferencedType>&& in_ptr) {
// InPtr is a different type (or we would have called the other operator), so we need not test &InPtr != this
ReferencedType* old_reference = reference_;
reference_ = in_ptr.reference_;
in_ptr.reference_ = nullptr;
if (old_reference) {
old_reference->Release();
}
return *this;
}
ReferencedType* operator->() const {
return reference_;
}
operator reference_type() const {
return reference_;
}
ReferencedType** get_init_reference() {
*this = nullptr;
return &reference_;
}
ReferencedType* get_reference() const {
return reference_;
}
friend bool is_valid_ref(const ref_count_ptr& in_reference) {
return in_reference.reference_ != nullptr;
}
bool is_valid() const {
return reference_ != nullptr;
}
void safe_release() {
*this = nullptr;
}
unsigned int get_ref_count() {
unsigned int result = 0;
if (reference_) {
result = reference_->GetRefCount();
assert(result > 0);
// you should never have a zero ref count if there is a live ref counted pointer (*this is live)
}
return result;
}
void swap(ref_count_ptr& in_ptr) noexcept // this does not change the reference count, and so is faster
{
ReferencedType* old_reference = reference_;
reference_ = in_ptr.reference_;
in_ptr.reference_ = old_reference;
}
// void Serialize(FArchive& Ar)
// {
// reference_type PtrReference = Reference;
// Ar << PtrReference;
// if(Ar.IsLoading())
// {
// *this = PtrReference;
// }
// }
private:
ReferencedType* reference_;
template<typename OtherType>
friend class ref_count_ptr;
public:
bool operator==(const ref_count_ptr& b) const {
return get_reference() == b.get_reference();
}
bool operator==(ReferencedType* b) const {
return get_reference() == b;
}
};