Files

142 lines
3.3 KiB
C++

// Copyright 2025 Steven Le Rouzic
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
#include "asl/base/assert.hpp"
#include "asl/base/annotations.hpp"
#include "asl/base/utility.hpp"
#include "asl/memory/memory.hpp"
#include "asl/memory/allocator.hpp"
#include "asl/hashing/hash.hpp"
namespace asl
{
template<is_object T, allocator Allocator = DefaultAllocator>
class box
{
T* m_ptr;
ASL_NO_UNIQUE_ADDRESS Allocator m_alloc;
public:
explicit constexpr box(niche_t)
requires default_constructible<Allocator>
: m_ptr{nullptr}
, m_alloc{}
{}
constexpr box(T* ptr, Allocator alloc)
: m_ptr{ptr}
, m_alloc{std::move(alloc)}
{
ASL_ASSERT(m_ptr != nullptr);
}
constexpr box(box&& other)
: m_ptr{exchange(other.m_ptr, nullptr)}
, m_alloc{std::move(other.m_alloc)}
{}
template<is_object U>
requires convertible_to<U*, T*>
constexpr box(box<U, Allocator>&& other) // NOLINT(*explicit*,*-not-moved)
: m_ptr{exchange(other.m_ptr, nullptr)}
, m_alloc{std::move(other.m_alloc)}
{}
constexpr box& operator=(box&& other)
{
if (this == &other) { return *this; }
if (m_ptr != nullptr) { reset(); }
m_ptr = exchange(other.m_ptr, nullptr);
m_alloc = std::move(other.m_alloc);
return *this;
}
box(const box&) = delete;
box& operator=(const box&) = delete;
constexpr ~box()
{
reset();
}
constexpr void reset()
{
if (m_ptr != nullptr)
{
destroy(m_ptr);
m_alloc.dealloc(m_ptr, layout::of<T>());
m_ptr = nullptr;
}
}
constexpr auto* get(this auto&& self)
{
return self.m_ptr;
}
constexpr auto&& operator*(this auto&& self)
{
ASL_ASSERT(self.m_ptr != nullptr);
return std::forward_like<decltype(self)&&>(*self.m_ptr);
}
constexpr auto* operator->(this auto&& self)
{
ASL_ASSERT(self.m_ptr != nullptr);
return self.m_ptr;
}
constexpr bool operator==(niche_t) const
{
return m_ptr == nullptr;
}
template<typename H>
requires hashable<T>
friend H AslHashValue(H h, const box& b)
{
return H::combine(std::move(h), *b);
}
template<is_object U, allocator A>
friend constexpr U* leak(box<U, A>&&);
template<is_object U, allocator A>
friend class box;
};
template<is_object T, allocator Allocator = DefaultAllocator, typename... Args>
constexpr box<T, Allocator> make_box_in(Allocator allocator, Args&&... args)
requires constructible_from<T, Args&&...>
{
void* raw_ptr = allocator.alloc(layout::of<T>());
auto* ptr = construct_at<T>(raw_ptr, std::forward<Args>(args)...);
return box(ptr, std::move(allocator));
}
template<is_object T, allocator Allocator = DefaultAllocator, typename... Args>
constexpr box<T, Allocator> make_box(Args&&... args)
requires default_constructible<Allocator> && constructible_from<T, Args&&...>
{
Allocator allocator{};
void* raw_ptr = allocator.alloc(layout::of<T>());
auto* ptr = construct_at<T>(raw_ptr, std::forward<Args>(args)...);
return box<T>(ptr, std::move(allocator));
}
template<is_object T, allocator A>
constexpr T* leak(box<T, A>&& b)
{
return exchange(std::move(b).m_ptr, nullptr);
}
} // namespace asl