summaryrefslogtreecommitdiff
path: root/src/ref_ptr.hh
blob: fcbd6a71c4e7ec701f67c566ac6bd77065b17ec5 (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
#ifndef ref_ptr_hh_INCLUDED
#define ref_ptr_hh_INCLUDED

namespace Kakoune
{

struct RefCountable
{
    RefCountable() = default;
    RefCountable(const RefCountable&) {}
    RefCountable(RefCountable&&) {}
    virtual ~RefCountable() = default;

    RefCountable& operator=(const RefCountable&) { return *this; }
    RefCountable& operator=(RefCountable&&) { return *this; }

    int refcount = 0;
};

struct RefCountablePolicy
{
    static void inc_ref(RefCountable* r, void*) noexcept { ++r->refcount; }
    static void dec_ref(RefCountable* r, void*) noexcept { if (--r->refcount == 0) delete r; }
    static void ptr_moved(RefCountable*, void*, void*) noexcept {}
};

template<typename T, typename Policy = RefCountablePolicy>
struct RefPtr
{
    RefPtr() = default;
    explicit RefPtr(T* ptr) : m_ptr(ptr) { acquire(); }
    ~RefPtr() noexcept { release(); }
    RefPtr(const RefPtr& other) : m_ptr(other.m_ptr) { acquire(); }
    RefPtr(RefPtr&& other)
        noexcept(noexcept(moved(nullptr)))
        : m_ptr(other.m_ptr) { other.m_ptr = nullptr; moved(&other); }

    RefPtr& operator=(const RefPtr& other)
    {
        if (other.m_ptr != m_ptr)
        {
            release();
            m_ptr = other.m_ptr;
            acquire();
        }
        return *this;
    }

    RefPtr& operator=(RefPtr&& other)
    {
        release();
        m_ptr = other.m_ptr;
        other.m_ptr = nullptr;
        moved(&other);
        return *this;
    }

    RefPtr& operator=(T* ptr)
    {
        if (ptr != m_ptr)
        {
            release();
            m_ptr = ptr;
            acquire();
        }
        return *this;
    }

    [[gnu::always_inline]]
    T* operator->() const { return m_ptr; }
    [[gnu::always_inline]]
    T& operator*() const { return *m_ptr; }

    [[gnu::always_inline]]
    T* get() const { return m_ptr; }

    [[gnu::always_inline]]
    explicit operator bool() const { return m_ptr; }

    void reset(T* ptr = nullptr)
    {
        if (ptr == m_ptr)
            return;
        release();
        m_ptr = ptr;
        acquire();
    }

    friend bool operator==(const RefPtr& lhs, const RefPtr& rhs) = default;
    friend bool operator==(const RefPtr& lhs, const T* rhs) { return lhs.m_ptr == rhs; }

private:
    T* m_ptr = nullptr;

    [[gnu::always_inline]]
    void acquire()
    {
        if (m_ptr)
            Policy::inc_ref(m_ptr, this);
    }

    [[gnu::always_inline]]
    void release() noexcept
    {
        if (m_ptr)
            Policy::dec_ref(m_ptr, this);
    }

    [[gnu::always_inline]]
    void moved(void* from)
        noexcept(noexcept(Policy::ptr_moved(nullptr, nullptr, nullptr)))
    {
        if (m_ptr)
            Policy::ptr_moved(m_ptr, from, this);
    }
};

}

#endif // ref_ptr_hh_INCLUDED