#pragma once #include "gp/functional/bind_front.hpp" #include "gp/algorithms/move.hpp" #include "gp/containers/buffer.hpp" #include "gp/functional/function.hpp" #include #include namespace gp { template class unique_ptr { T* data; gp::allocator& owner; void dirty_clear() { if(data) { data->~T(); owner.deallocate(data); } } public: unique_ptr(T* _data, gp::allocator& _owner) : data(_data) , owner(_owner) {} template static unique_ptr make(gp::allocator& owner, U&&... args) { auto ptr = owner.allocate(sizeof(T)); return unique_ptr(new(ptr) T(gp::forward(args)...), owner); } template unique_ptr cast() { auto save = data; data = nullptr; return unique_ptr(save, owner); } T* operator->() { return data; } T& operator*() { return *data; } operator bool() { return data != nullptr; } unique_ptr(unique_ptr&) = delete; unique_ptr(unique_ptr&& oth) : data(oth.data) , owner(oth.owner) { oth.data = nullptr; } unique_ptr& operator=(unique_ptr&) = delete; unique_ptr& operator=(unique_ptr&& oth) { dirty_clear(); data = oth.data; owner = oth.owner; } ~unique_ptr() { dirty_clear(); } }; template class shared_ptr { T* data; std::atomic_int* refcounter; gp::allocator& owner; shared_ptr(T* _data, gp::allocator& _owner) : data(_data) , refcounter((std::atomic_int*)owner.allocate(sizeof(std::atomic_int))) , owner(_owner) { refcounter->store(1); } shared_ptr(T* _data, std::atomic_int* refctr, gp::allocator& _owner) : data(_data) , refcounter(refctr) , owner(_owner) { refcounter->store(1); } void dirty_clear() { if(!refcounter) return; if(refcounter->fetch_sub(1, std::memory_order::acq_rel) == 0) { if(data) { data->~T(); owner.deallocate(refcounter); owner.deallocate(data); } } } public: template static shared_ptr make(gp::allocator& owner, U&&... args) { auto ptr = owner.allocate(sizeof(T)); return shared_ptr(new(ptr) T(gp::forward(args)...), owner); } template shared_ptr cast() { return shared_ptr(data, refcounter, owner); } T* operator->() { return data; } T& operator*() { return *data; } operator bool() { return data != nullptr; } shared_ptr(shared_ptr& oth) { oth.refcounter->fetch_add(1, std::memory_order::acquire); data = oth.data; refcounter = oth.refcounter; owner = oth.owner; } shared_ptr(shared_ptr&& oth) : data(oth.data) , owner(oth.owner) { oth.data = nullptr; oth.refcounter = nullptr; } shared_ptr& operator=(shared_ptr& oth) { dirty_clear(); (*oth.refcounter)++; data = oth.data; refcounter = oth.refcounter; owner = oth.owner; } shared_ptr& operator=(shared_ptr&& oth) { dirty_clear(); data = oth.data; owner = oth.owner; } ~shared_ptr() { dirty_clear(); } }; template struct resource_traits { using identifier = uint64_t; static constexpr bool create_if_not_exist = false; static T* create(const identifier&, gp::allocator&); static T* retrieve(const identifier&, gp::allocator&); static bool imprint(const identifier&, const T& value); static bool annihilate(const identifier&); }; template requires std::copyable::identifier> class resource_ptr { typename resource_traits::identifier _identifier; gp::allocator& _alloc; T* value = nullptr; operator T&() { if(!value) { value = resource_traits::retrieve(_identifier, _alloc); } if(value) return *value; else if constexpr (resource_traits::create_if_not_exist) { value = resource_traits::create(_identifier, _alloc); if(value) return *value; } gp_config::assertion(false, "unavailable resource lead to crash"); } gp::optional read() { if(!value) { value = resource_traits::retrieve(_identifier, _alloc); } if(value) return *value; else if constexpr (resource_traits::create_if_not_exist) { value = resource_traits::create(_identifier, _alloc); if(value) return *value; } return gp::nullopt; } gp::optional force_read() { value = resource_traits::retrieve(_identifier, _alloc); if(value) return *value; return gp::nullopt; } template requires std::is_base_of_v gp::optional operator=(U& new_value) { if(resource_traits::imprint(_identifier, new_value)) { if(value) { value->~T(); gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange"); } value = _alloc.allocate(sizeof(U)); gp_config::assertion(value != nullptr, "could not allocate during resource transcription"); new(value) U(new_value); return *value; } return gp::nullopt; } template requires std::is_base_of_v gp::optional operator=(const U& new_value) { if(resource_traits::imprint(_identifier, new_value)) { if(value) { value->~T(); gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange"); } value = _alloc.allocate(sizeof(U)); gp_config::assertion(value != nullptr, "could not allocate during resource transcription"); new(value) U(new_value); return *value; } return gp::nullopt; } template<> gp::optional operator=(const gp::nullopt_t) { if(resource_traits::annihilate(_identifier)) { if(value) { value->~T(); gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange"); } value = nullptr; } return gp::nullopt; } }; }