mirror of
https://github.com/hyprwm/hyprutils.git
synced 2025-01-27 05:59:48 +01:00
memory: implement a unique pointer
this unique pointer differs from the STL, because it allows weak pointers to it, which cannot be lock()ed. under the hood, it's just a SP that cannot be referenced.
This commit is contained in:
parent
59414c4cee
commit
423c69d697
5 changed files with 308 additions and 115 deletions
119
include/hyprutils/memory/ImplBase.hpp
Normal file
119
include/hyprutils/memory/ImplBase.hpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Hyprutils {
|
||||||
|
namespace Memory {
|
||||||
|
namespace Impl_ {
|
||||||
|
class impl_base {
|
||||||
|
public:
|
||||||
|
virtual ~impl_base() {};
|
||||||
|
|
||||||
|
virtual void inc() noexcept = 0;
|
||||||
|
virtual void dec() noexcept = 0;
|
||||||
|
virtual void incWeak() noexcept = 0;
|
||||||
|
virtual void decWeak() noexcept = 0;
|
||||||
|
virtual unsigned int ref() noexcept = 0;
|
||||||
|
virtual unsigned int wref() noexcept = 0;
|
||||||
|
virtual void destroy() noexcept = 0;
|
||||||
|
virtual bool destroying() noexcept = 0;
|
||||||
|
virtual bool dataNonNull() noexcept = 0;
|
||||||
|
virtual bool lockable() noexcept = 0;
|
||||||
|
virtual void* getData() noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class impl : public impl_base {
|
||||||
|
public:
|
||||||
|
impl(T* data, bool lock = true) noexcept : _data(data), _lockable(lock) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strong refcount */
|
||||||
|
unsigned int _ref = 0;
|
||||||
|
/* weak refcount */
|
||||||
|
unsigned int _weak = 0;
|
||||||
|
/* if this is lockable (shared) */
|
||||||
|
bool _lockable = true;
|
||||||
|
|
||||||
|
T* _data = nullptr;
|
||||||
|
|
||||||
|
friend void swap(impl*& a, impl*& b) {
|
||||||
|
impl* tmp = a;
|
||||||
|
a = b;
|
||||||
|
b = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if the destructor was called,
|
||||||
|
creating shared_ptrs is no longer valid */
|
||||||
|
bool _destroying = false;
|
||||||
|
|
||||||
|
void _destroy() {
|
||||||
|
if (!_data || _destroying)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// first, we destroy the data, but keep the pointer.
|
||||||
|
// this way, weak pointers will still be able to
|
||||||
|
// reference and use, but no longer create shared ones.
|
||||||
|
_destroying = true;
|
||||||
|
__deleter(_data);
|
||||||
|
// now, we can reset the data and call it a day.
|
||||||
|
_data = nullptr;
|
||||||
|
_destroying = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::default_delete<T> __deleter{};
|
||||||
|
|
||||||
|
//
|
||||||
|
virtual void inc() noexcept {
|
||||||
|
_ref++;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void dec() noexcept {
|
||||||
|
_ref--;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void incWeak() noexcept {
|
||||||
|
_weak++;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void decWeak() noexcept {
|
||||||
|
_weak--;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual unsigned int ref() noexcept {
|
||||||
|
return _ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual unsigned int wref() noexcept {
|
||||||
|
return _weak;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void destroy() noexcept {
|
||||||
|
_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool destroying() noexcept {
|
||||||
|
return _destroying;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool lockable() noexcept {
|
||||||
|
return _lockable;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool dataNonNull() noexcept {
|
||||||
|
return _data != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void* getData() noexcept {
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~impl() {
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include "ImplBase.hpp"
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is a custom impl of std::shared_ptr.
|
This is a custom impl of std::shared_ptr.
|
||||||
|
@ -17,109 +15,6 @@
|
||||||
|
|
||||||
namespace Hyprutils {
|
namespace Hyprutils {
|
||||||
namespace Memory {
|
namespace Memory {
|
||||||
namespace CSharedPointer_ {
|
|
||||||
class impl_base {
|
|
||||||
public:
|
|
||||||
virtual ~impl_base() {};
|
|
||||||
|
|
||||||
virtual void inc() noexcept = 0;
|
|
||||||
virtual void dec() noexcept = 0;
|
|
||||||
virtual void incWeak() noexcept = 0;
|
|
||||||
virtual void decWeak() noexcept = 0;
|
|
||||||
virtual unsigned int ref() noexcept = 0;
|
|
||||||
virtual unsigned int wref() noexcept = 0;
|
|
||||||
virtual void destroy() noexcept = 0;
|
|
||||||
virtual bool destroying() noexcept = 0;
|
|
||||||
virtual bool dataNonNull() noexcept = 0;
|
|
||||||
virtual void* getData() noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class impl : public impl_base {
|
|
||||||
public:
|
|
||||||
impl(T* data) noexcept : _data(data) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* strong refcount */
|
|
||||||
unsigned int _ref = 0;
|
|
||||||
/* weak refcount */
|
|
||||||
unsigned int _weak = 0;
|
|
||||||
|
|
||||||
T* _data = nullptr;
|
|
||||||
|
|
||||||
friend void swap(impl*& a, impl*& b) {
|
|
||||||
impl* tmp = a;
|
|
||||||
a = b;
|
|
||||||
b = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if the destructor was called,
|
|
||||||
creating shared_ptrs is no longer valid */
|
|
||||||
bool _destroying = false;
|
|
||||||
|
|
||||||
void _destroy() {
|
|
||||||
if (!_data || _destroying)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// first, we destroy the data, but keep the pointer.
|
|
||||||
// this way, weak pointers will still be able to
|
|
||||||
// reference and use, but no longer create shared ones.
|
|
||||||
_destroying = true;
|
|
||||||
__deleter(_data);
|
|
||||||
// now, we can reset the data and call it a day.
|
|
||||||
_data = nullptr;
|
|
||||||
_destroying = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::default_delete<T> __deleter{};
|
|
||||||
|
|
||||||
//
|
|
||||||
virtual void inc() noexcept {
|
|
||||||
_ref++;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void dec() noexcept {
|
|
||||||
_ref--;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void incWeak() noexcept {
|
|
||||||
_weak++;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void decWeak() noexcept {
|
|
||||||
_weak--;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual unsigned int ref() noexcept {
|
|
||||||
return _ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual unsigned int wref() noexcept {
|
|
||||||
return _weak;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void destroy() noexcept {
|
|
||||||
_destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool destroying() noexcept {
|
|
||||||
return _destroying;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool dataNonNull() noexcept {
|
|
||||||
return _data != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void* getData() noexcept {
|
|
||||||
return _data;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~impl() {
|
|
||||||
destroy();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class CSharedPointer {
|
class CSharedPointer {
|
||||||
|
@ -132,7 +27,7 @@ namespace Hyprutils {
|
||||||
/* creates a new shared pointer managing a resource
|
/* creates a new shared pointer managing a resource
|
||||||
avoid calling. Could duplicate ownership. Prefer makeShared */
|
avoid calling. Could duplicate ownership. Prefer makeShared */
|
||||||
explicit CSharedPointer(T* object) noexcept {
|
explicit CSharedPointer(T* object) noexcept {
|
||||||
impl_ = new CSharedPointer_::impl<T>(object);
|
impl_ = new Impl_::impl<T>(object);
|
||||||
increment();
|
increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +53,7 @@ namespace Hyprutils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allows weakPointer to create from an impl */
|
/* allows weakPointer to create from an impl */
|
||||||
CSharedPointer(CSharedPointer_::impl_base* implementation) noexcept {
|
CSharedPointer(Impl_::impl_base* implementation) noexcept {
|
||||||
impl_ = implementation;
|
impl_ = implementation;
|
||||||
increment();
|
increment();
|
||||||
}
|
}
|
||||||
|
@ -246,7 +141,7 @@ namespace Hyprutils {
|
||||||
return impl_ ? impl_->ref() : 0;
|
return impl_ ? impl_->ref() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSharedPointer_::impl_base* impl_ = nullptr;
|
Impl_::impl_base* impl_ = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
|
|
149
include/hyprutils/memory/UniquePtr.hpp
Normal file
149
include/hyprutils/memory/UniquePtr.hpp
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImplBase.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is a custom impl of std::unique_ptr.
|
||||||
|
In contrast to the STL one, it allows for
|
||||||
|
creation of a weak_ptr, that will then be unable
|
||||||
|
to be locked.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Hyprutils {
|
||||||
|
namespace Memory {
|
||||||
|
template <typename T>
|
||||||
|
class CUniquePointer {
|
||||||
|
public:
|
||||||
|
template <typename X>
|
||||||
|
using validHierarchy = typename std::enable_if<std::is_assignable<CUniquePointer<T>&, X>::value, CUniquePointer&>::type;
|
||||||
|
template <typename X>
|
||||||
|
using isConstructible = typename std::enable_if<std::is_constructible<T&, X&>::value>::type;
|
||||||
|
|
||||||
|
/* creates a new unique pointer managing a resource
|
||||||
|
avoid calling. Could duplicate ownership. Prefer makeUnique */
|
||||||
|
explicit CUniquePointer(T* object) noexcept {
|
||||||
|
impl_ = new Impl_::impl<T>(object, false);
|
||||||
|
increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates a shared pointer from a reference */
|
||||||
|
template <typename U, typename = isConstructible<U>>
|
||||||
|
CUniquePointer(const CUniquePointer<U>& ref) = delete;
|
||||||
|
CUniquePointer(const CUniquePointer& ref) = delete;
|
||||||
|
|
||||||
|
template <typename U, typename = isConstructible<U>>
|
||||||
|
CUniquePointer(CUniquePointer<U>&& ref) noexcept {
|
||||||
|
std::swap(impl_, ref.impl_);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUniquePointer(CUniquePointer&& ref) noexcept {
|
||||||
|
std::swap(impl_, ref.impl_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates an empty unique pointer with no implementation */
|
||||||
|
CUniquePointer() noexcept {
|
||||||
|
; // empty
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates an empty unique pointer with no implementation */
|
||||||
|
CUniquePointer(std::nullptr_t) noexcept {
|
||||||
|
; // empty
|
||||||
|
}
|
||||||
|
|
||||||
|
~CUniquePointer() {
|
||||||
|
decrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
validHierarchy<const CUniquePointer<U>&> operator=(const CUniquePointer<U>& rhs) = delete;
|
||||||
|
CUniquePointer& operator=(const CUniquePointer& rhs) = delete;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
validHierarchy<const CUniquePointer<U>&> operator=(CUniquePointer<U>&& rhs) {
|
||||||
|
std::swap(impl_, rhs.impl_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CUniquePointer& operator=(CUniquePointer&& rhs) {
|
||||||
|
std::swap(impl_, rhs.impl_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const {
|
||||||
|
return impl_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const CUniquePointer& lhs, const CUniquePointer& rhs) const {
|
||||||
|
return reinterpret_cast<uintptr_t>(lhs.impl_) < reinterpret_cast<uintptr_t>(rhs.impl_);
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->() const {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator*() const {
|
||||||
|
return *get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
decrement();
|
||||||
|
impl_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* get() const {
|
||||||
|
return impl_ ? static_cast<T*>(impl_->getData()) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Impl_::impl_base* impl_ = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*
|
||||||
|
no-op if there is no impl_
|
||||||
|
may delete the stored object if ref == 0
|
||||||
|
may delete and reset impl_ if ref == 0 and weak == 0
|
||||||
|
*/
|
||||||
|
void decrement() {
|
||||||
|
if (!impl_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
impl_->dec();
|
||||||
|
|
||||||
|
// if ref == 0, we can destroy impl
|
||||||
|
if (impl_->ref() == 0)
|
||||||
|
destroyImpl();
|
||||||
|
}
|
||||||
|
/* no-op if there is no impl_ */
|
||||||
|
void increment() {
|
||||||
|
if (!impl_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
impl_->inc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* destroy the pointed-to object
|
||||||
|
if able, will also destroy impl */
|
||||||
|
void destroyImpl() {
|
||||||
|
// destroy the impl contents
|
||||||
|
impl_->destroy();
|
||||||
|
|
||||||
|
// check for weak refs, if zero, we can also delete impl_
|
||||||
|
if (impl_->wref() == 0) {
|
||||||
|
delete impl_;
|
||||||
|
impl_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename U, typename... Args>
|
||||||
|
static CUniquePointer<U> makeUnique(Args&&... args) {
|
||||||
|
return CUniquePointer<U>(new U(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct std::hash<Hyprutils::Memory::CUniquePointer<T>> {
|
||||||
|
std::size_t operator()(const Hyprutils::Memory::CUniquePointer<T>& p) const noexcept {
|
||||||
|
return std::hash<void*>{}(p.impl_);
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "./SharedPtr.hpp"
|
#include "./SharedPtr.hpp"
|
||||||
|
#include "./UniquePtr.hpp"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is a Hyprland implementation of std::weak_ptr.
|
This is a Hyprland implementation of std::weak_ptr.
|
||||||
|
@ -28,6 +29,16 @@ namespace Hyprutils {
|
||||||
incrementWeak();
|
incrementWeak();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* create a weak ptr from a reference */
|
||||||
|
template <typename U, typename = isConstructible<U>>
|
||||||
|
CWeakPointer(const CUniquePointer<U>& ref) noexcept {
|
||||||
|
if (!ref.impl_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
impl_ = ref.impl_;
|
||||||
|
incrementWeak();
|
||||||
|
}
|
||||||
|
|
||||||
/* create a weak ptr from another weak ptr */
|
/* create a weak ptr from another weak ptr */
|
||||||
template <typename U, typename = isConstructible<U>>
|
template <typename U, typename = isConstructible<U>>
|
||||||
CWeakPointer(const CWeakPointer<U>& ref) noexcept {
|
CWeakPointer(const CWeakPointer<U>& ref) noexcept {
|
||||||
|
@ -119,7 +130,7 @@ namespace Hyprutils {
|
||||||
}
|
}
|
||||||
|
|
||||||
CSharedPointer<T> lock() const {
|
CSharedPointer<T> lock() const {
|
||||||
if (!impl_ || !impl_->dataNonNull() || impl_->destroying())
|
if (!impl_ || !impl_->dataNonNull() || impl_->destroying() || !impl_->lockable())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return CSharedPointer<T>(impl_);
|
return CSharedPointer<T>(impl_);
|
||||||
|
@ -154,7 +165,11 @@ namespace Hyprutils {
|
||||||
return get();
|
return get();
|
||||||
}
|
}
|
||||||
|
|
||||||
CSharedPointer_::impl_base* impl_ = nullptr;
|
T& operator*() const {
|
||||||
|
return *get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Impl_::impl_base* impl_ = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* no-op if there is no impl_ */
|
/* no-op if there is no impl_ */
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
|
#include <hyprutils/memory/UniquePtr.hpp>
|
||||||
#include "shared.hpp"
|
#include "shared.hpp"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -6,22 +7,32 @@ using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
#define SP CSharedPointer
|
#define SP CSharedPointer
|
||||||
#define WP CWeakPointer
|
#define WP CWeakPointer
|
||||||
|
#define UP CUniquePointer
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
int main(int argc, char** argv, char** envp) {
|
||||||
SP<int> intPtr = makeShared<int>(10);
|
SP<int> intPtr = makeShared<int>(10);
|
||||||
SP<int> intPtr2 = makeShared<int>(1337);
|
SP<int> intPtr2 = makeShared<int>(1337);
|
||||||
|
UP<int> intUnique = makeUnique<int>(420);
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
EXPECT(*intPtr, 10);
|
EXPECT(*intPtr, 10);
|
||||||
EXPECT(intPtr.strongRef(), 1);
|
EXPECT(intPtr.strongRef(), 1);
|
||||||
|
EXPECT(*intUnique, 420);
|
||||||
|
|
||||||
WP<int> weak = intPtr;
|
WP<int> weak = intPtr;
|
||||||
|
WP<int> weakUnique = intUnique;
|
||||||
|
|
||||||
EXPECT(*intPtr, 10);
|
EXPECT(*intPtr, 10);
|
||||||
EXPECT(intPtr.strongRef(), 1);
|
EXPECT(intPtr.strongRef(), 1);
|
||||||
EXPECT(*weak.get(), 10);
|
EXPECT(*weak, 10);
|
||||||
EXPECT(weak.expired(), false);
|
EXPECT(weak.expired(), false);
|
||||||
|
EXPECT(*weakUnique, 420);
|
||||||
|
EXPECT(weakUnique.expired(), false);
|
||||||
|
EXPECT(intUnique.impl_->wref(), 1);
|
||||||
|
|
||||||
|
SP<int> sharedFromUnique = weakUnique.lock();
|
||||||
|
EXPECT(sharedFromUnique, nullptr);
|
||||||
|
|
||||||
std::vector<SP<int>> sps;
|
std::vector<SP<int>> sps;
|
||||||
sps.push_back(intPtr);
|
sps.push_back(intPtr);
|
||||||
|
@ -31,11 +42,15 @@ int main(int argc, char** argv, char** envp) {
|
||||||
std::erase_if(sps, [intPtr](const auto& e) { return e == intPtr; });
|
std::erase_if(sps, [intPtr](const auto& e) { return e == intPtr; });
|
||||||
|
|
||||||
intPtr.reset();
|
intPtr.reset();
|
||||||
|
intUnique.reset();
|
||||||
|
|
||||||
EXPECT(weak.impl_->ref(), 0);
|
EXPECT(weak.impl_->ref(), 0);
|
||||||
|
EXPECT(weakUnique.impl_->ref(), 0);
|
||||||
|
EXPECT(weakUnique.impl_->wref(), 1);
|
||||||
EXPECT(intPtr2.strongRef(), 3);
|
EXPECT(intPtr2.strongRef(), 3);
|
||||||
|
|
||||||
EXPECT(weak.expired(), true);
|
EXPECT(weak.expired(), true);
|
||||||
|
EXPECT(weakUnique.expired(), true);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
Loading…
Reference in a new issue