summaryrefslogtreecommitdiff
path: root/asl/box.hpp
blob: 9d3c7f0c4d8a6c9a2f5a9891e50720462a9b5e1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#pragma once

#include "asl/allocator.hpp"
#include "asl/assert.hpp"
#include "asl/annotations.hpp"
#include "asl/memory.hpp"
#include "asl/utility.hpp"
#include "asl/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{ASL_MOVE(alloc)}
    {
        ASL_ASSERT(m_ptr != nullptr);
    }

    constexpr box(box&& other)
        : m_ptr{exchange(other.m_ptr, nullptr)}
        , m_alloc{ASL_MOVE(other.m_alloc)}
    {}

    template<is_object U>
    requires convertible_from<T*, U*>
    constexpr box(box<U, Allocator>&& other) // NOLINT(*-explicit-conversions)
        : m_ptr{exchange(other.m_ptr, nullptr)}
        , m_alloc{ASL_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 = ASL_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 T* get() const { return m_ptr; }

    constexpr T& operator*() const
    {
        ASL_ASSERT(m_ptr != nullptr);
        return *m_ptr;
    }

    constexpr T* operator->() const
    {
        ASL_ASSERT(m_ptr != nullptr);
        return 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(ASL_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, ASL_FWD(args)...);
    return box(ptr, ASL_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, ASL_FWD(args)...);
    return box<T>(ptr, ASL_MOVE(allocator));
}

template<is_object T, allocator A>
constexpr T* leak(box<T, A>&& b)
{
    return exchange(b.m_ptr, nullptr);
}

} // namespace asl