// 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 class box { T* m_ptr; ASL_NO_UNIQUE_ADDRESS Allocator m_alloc; public: explicit constexpr box(niche_t) requires default_constructible : 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 requires convertible_to constexpr box(box&& 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()); 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(*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 requires hashable friend H AslHashValue(H h, const box& b) { return H::combine(std::move(h), *b); } template friend constexpr U* leak(box&&); template friend class box; }; template constexpr box make_box_in(Allocator allocator, Args&&... args) requires constructible_from { void* raw_ptr = allocator.alloc(layout::of()); auto* ptr = construct_at(raw_ptr, std::forward(args)...); return box(ptr, std::move(allocator)); } template constexpr box make_box(Args&&... args) requires default_constructible && constructible_from { Allocator allocator{}; void* raw_ptr = allocator.alloc(layout::of()); auto* ptr = construct_at(raw_ptr, std::forward(args)...); return box(ptr, std::move(allocator)); } template constexpr T* leak(box&& b) { return exchange(std::move(b).m_ptr, nullptr); } } // namespace asl