Skip to content

Instantly share code, notes, and snippets.

@muit
Last active July 28, 2020 11:18
Show Gist options
  • Save muit/c0bfadb832d285ebd356ea1576c86707 to your computer and use it in GitHub Desktop.
Save muit/c0bfadb832d285ebd356ea1576c86707 to your computer and use it in GitHub Desktop.
Smart pointer based on ownership. 1 owner, many weaks
// 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