Last active
July 28, 2020 11:18
-
-
Save muit/c0bfadb832d285ebd356ea1576c86707 to your computer and use it in GitHub Desktop.
Smart pointer based on ownership. 1 owner, many weaks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015-2020 Piperift - All rights reserved | |
#pragma once | |
#include "Platform/Platform.h" | |
#include <atomic> | |
namespace VCLang | |
{ | |
namespace Impl | |
{ | |
template <typename T> | |
struct PtrBuilder | |
{ | |
template <typename... Args> | |
static T* New(Args&&... args) | |
{ | |
return new T(std::forward<Args>(args)...); | |
} | |
static T* NewArray(size_t size) | |
{ | |
using Elem = std::remove_extent_t<T>; | |
return new Elem[size](); | |
} | |
static void Delete(void* ptr) | |
{ | |
delete static_cast<T*>(ptr); | |
} | |
}; | |
// Container that lives from when an owner is created to when the last weak has been reset | |
struct PtrWeakCounter | |
{ | |
// Owner and weak references | |
std::atomic<bool> bIsSet = true; | |
std::atomic<u32> weaks = 1; | |
}; | |
struct Ptr; | |
struct PtrOwner | |
{ | |
friend Ptr; | |
protected: | |
void* value = nullptr; | |
PtrWeakCounter* counter = nullptr; | |
public: | |
bool IsValid() const | |
{ | |
return counter != nullptr; | |
} | |
operator bool() const | |
{ | |
return IsValid(); | |
}; | |
protected: | |
PtrOwner(void* value) : value{value}, counter{new PtrWeakCounter{value}} {} | |
void MoveFrom(PtrOwner&& other) | |
{ | |
value = other.value; | |
counter = other.counter; | |
other.value = nullptr; | |
other.counter = nullptr; | |
} | |
}; | |
struct Ptr | |
{ | |
protected: | |
void* value; | |
PtrWeakCounter* counter = nullptr; | |
protected: | |
Ptr(const PtrOwner& owner) | |
{ | |
value = owner.value; | |
counter = owner.counter; | |
++counter->weaks; | |
} | |
Ptr(const Ptr& other) | |
{ | |
value = other.value; | |
counter = other.counter; | |
++counter->weaks; | |
} | |
Ptr(Ptr&& other) | |
{ | |
value = other.value; | |
counter = other.counter; | |
other.counter = nullptr; | |
} | |
Ptr& operator=(const Ptr& other) | |
{ | |
if (counter != other.counter) | |
{ | |
Reset(); | |
value = other.value; | |
counter = other.counter; | |
++counter->weaks; | |
} | |
return *this; | |
} | |
Ptr& operator=(Ptr&& other) | |
{ | |
if (counter != other.counter) | |
{ | |
Reset(); | |
value = other.value; | |
counter = other.counter; | |
other.counter = nullptr; | |
} | |
else | |
{ | |
other.Reset(); | |
} | |
return *this; | |
} | |
public: | |
~Ptr() | |
{ | |
Reset(); | |
} | |
void Reset() | |
{ | |
if (counter) | |
{ | |
value = nullptr; | |
if (--counter->weaks <= 0 && !counter->bIsSet) | |
{ | |
delete counter; | |
} | |
counter = nullptr; | |
} | |
} | |
bool IsValid() const | |
{ | |
return counter && counter->bIsSet; | |
} | |
operator bool() const | |
{ | |
return IsValid(); | |
}; | |
}; | |
} // namespace Impl | |
template <typename T> | |
struct Ptr; | |
template <typename T, typename TBuilder = Impl::PtrBuilder<T>> | |
struct PtrOwner : public Impl::PtrOwner | |
{ | |
using Super = Impl::PtrOwner; | |
template <typename T2, typename TBuilder2> | |
friend struct PtrOwner; | |
public: | |
PtrOwner(T* value) : Super(value) {} | |
PtrOwner(PtrOwner&& other) noexcept | |
{ | |
MoveFrom(MoveTemp(other)); | |
} | |
template <typename T2> | |
PtrOwner(PtrOwner<T2, TBuilder>&& other) | |
{ | |
static_assert(std::is_same_v<T2, T> || std::is_convertible_v<T2, T>, | |
"Type is not down-castable!"); | |
MoveFrom(MoveTemp(other)); | |
} | |
PtrOwner& operator=(PtrOwner&& other) | |
{ | |
MoveFrom(MoveTemp(other)); | |
return *this; | |
} | |
template <typename T2> | |
PtrOwner& operator=(PtrOwner<T2, TBuilder>&& other) | |
{ | |
static_assert(std::is_same_v<T2, T> || std::is_convertible_v<T2, T>, | |
"Type is not down-castable!"); | |
MoveFrom(MoveTemp(other)); | |
return *this; | |
} | |
/** Cast a global pointer into another type. Will invalidate previous owner on success */ | |
template <typename T2> | |
PtrOwner<T2, TBuilder> Cast() | |
{ | |
static_assert(std::is_same_v<T2, T>, "Don't try to Cast using the same type"); | |
// If can be casted statically or dynamically | |
if (IsValid() && (std::is_convertible_v<T2, T> || dynamic_cast<T2*>(**this) != nullptr)) | |
{ | |
PtrOwner<T2, TBuilder> newPtr{}; | |
newPtr.MoveFrom(MoveTemp(*this)); | |
return newPtr; | |
} | |
return {}; | |
} | |
/** Cast a global pointer into another type. Will invalidate previous owner on success */ | |
template <typename T2> | |
Ptr<T2> Cast() | |
{ | |
// If can be casted statically or dynamically | |
if (IsValid()) | |
{ | |
Ptr<T> ptr{*this}; | |
return ptr.Cast<T2>(); | |
} | |
return {}; | |
} | |
void Release() | |
{ | |
if (counter) | |
{ | |
TBuilder::Delete(value); | |
value = nullptr; | |
counter->bIsSet = false; | |
if (counter->weaks <= 0) | |
{ | |
delete counter; | |
} | |
counter = nullptr; | |
} | |
} | |
T* operator*() const | |
{ | |
return static_cast<T*>(value); | |
} | |
T* operator->() const | |
{ | |
return static_cast<T*>(value); | |
} | |
template <typename T2> | |
bool operator==(T2* other) const | |
{ | |
return **this == other; | |
} | |
template <typename T2> | |
bool operator==(const PtrOwner<T2, TBuilder>& other) const | |
{ | |
return operator==(*other); | |
} | |
template <typename T2> | |
bool operator==(const Ptr<T2>& other) const | |
{ | |
return operator==(*other); | |
} | |
}; | |
template <typename T> | |
struct Ptr : public Impl::Ptr | |
{ | |
using Super = Impl::Ptr; | |
template <typename T2, typename TBuilder> | |
Ptr(const PtrOwner<T2, TBuilder>& owner) : Super(owner) | |
{ | |
static_assert(std::is_same_v<T2, T> || std::is_convertible_v<T2, T>, | |
"Type is not down-castable!"); | |
} | |
template <typename T2> | |
Ptr(const Ptr<T2>& other) : Super(other) | |
{ | |
static_assert(std::is_same_v<T2, T> || std::is_convertible_v<T2, T>, | |
"Type is not down-castable!"); | |
} | |
template <typename T2> | |
Ptr(Ptr<T2>&& other) : Super(MoveTemp(other)) | |
{ | |
static_assert(std::is_same_v<T2, T> || std::is_convertible_v<T2, T>, | |
"Type is not down-castable!"); | |
} | |
template <typename T2> | |
Ptr& operator=(const Ptr<T2>& other) | |
{ | |
static_assert(std::is_same_v<T2, T> || std::is_convertible_v<T2, T>, | |
"Type is not down-castable!"); | |
return Super::operator==(other); | |
} | |
template <typename T2> | |
Ptr& operator=(Ptr<T2>&& other) | |
{ | |
static_assert(std::is_same_v<T2, T> || std::is_convertible_v<T2, T>, | |
"Type is not down-castable!"); | |
return Super::operator==(MoveTemp(other)); | |
} | |
T* operator*() const | |
{ | |
return static_cast<T*>(value); | |
} | |
T* operator->() const | |
{ | |
return static_cast<T*>(value); | |
} | |
template <typename T2> | |
Ptr<T2> Cast() const | |
{ | |
if (IsValid() && (std::is_convertible_v<T2, T> || dynamic_cast<T2*>(**this) == nullptr)) | |
{ | |
Ptr<T2> ptr{}; | |
ptr.Set(GetGlobal()); | |
return ptr; | |
} | |
return {}; | |
} | |
template <typename T2> | |
bool operator==(T2* other) const | |
{ | |
return **this == other; | |
} | |
template <typename T2, typename TBuilder> | |
bool operator==(const PtrOwner<T2, TBuilder>& other) const | |
{ | |
return operator==(*other); | |
} | |
template <typename T2> | |
bool operator==(const Ptr<T2>& other) const | |
{ | |
return operator==(*other); | |
} | |
}; | |
template <typename T, typename TBuilder = Impl::PtrBuilder<T>, typename... Args, | |
EnableIfT<!std::is_array_v<T>, i32> = 0> | |
PtrOwner<T, TBuilder> MakeOwned(Args&&... args) | |
{ | |
return {TBuilder::New(std::forward<Args>(args)...)}; | |
} | |
template <typename T, typename TBuilder = Impl::PtrBuilder<T>, | |
EnableIfT<std::is_array_v<T> && std::extent_v<T> == 0, i32> = 0> | |
PtrOwner<T, TBuilder> MakeOwned(size_t size) | |
{ | |
using Elem = std::remove_extent_t<T>; | |
return {TBuilder::NewArray(size)}; | |
} | |
template <typename T, typename TBuilder = Impl::PtrBuilder<T>, typename... Args, | |
EnableIfT<std::extent_v<T> != 0, i32> = 0> | |
void MakeOwned(Args&&...) = delete; | |
} // namespace VCLang |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment