1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
3
4#pragma once
5
6#include <cstddef>
7#include <new>
8#include <algorithm>
9#include <optional>
10#include <atomic>
11
12#ifdef __APPLE__
13# include <AvailabilityMacros.h>
14#endif
15
16namespace vtable {
17
18template<typename T>
19struct VRefMut
20{
21 const T *vtable;
22 void *instance;
23};
24
25struct Layout
26{
27 std::size_t size;
28 std::size_t align;
29};
30
31// For the C++'s purpose, they are all the same
32template<typename T>
33using VRef = VRefMut<T>;
34
35template<typename T>
36using Pin = T;
37
38template<typename T>
39struct VBox
40{
41 const T *vtable = nullptr;
42 void *instance = nullptr;
43 VBox(const VBox &) = delete;
44 VBox() = default;
45 VBox &operator=(const VBox &) = delete;
46 ~VBox()
47 {
48 if (vtable && instance) {
49 vtable->drop({ vtable, instance });
50 }
51 }
52};
53
54struct AllowPin;
55
56template<typename Base, typename T, typename Flag = void>
57struct VOffset
58{
59 const T *vtable;
60 std::uintptr_t offset;
61};
62
63template<typename VTable, typename X>
64struct VRcInner
65{
66 template<typename VTable_, typename X_>
67 friend class VRc;
68 template<typename VTable_, typename X_>
69 friend class VWeak;
70
71private:
72 VRcInner() : layout {} { }
73 const VTable *vtable = &X::static_vtable;
74 std::atomic<int> strong_ref = 1;
75 std::atomic<int> weak_ref = 1;
76 std::uint16_t data_offset = offsetof(VRcInner, data);
77 union {
78 X data;
79 Layout layout;
80 };
81
82 void *data_ptr() { return reinterpret_cast<char *>(this) + data_offset; }
83 ~VRcInner() = delete;
84};
85
86struct Dyn
87{
88};
89
90template<typename VTable, typename X = Dyn>
91class VRc
92{
93 VRcInner<VTable, X> *inner;
94 VRc(VRcInner<VTable, X> *inner) : inner(inner) { }
95 template<typename VTable_, typename X_>
96 friend class VWeak;
97
98public:
99 ~VRc()
100 {
101 if (!--inner->strong_ref) {
102 Layout layout = inner->vtable->drop_in_place({ inner->vtable, &inner->data });
103 layout.size += inner->data_offset;
104 layout.align = std::max<size_t>(a: layout.align, b: alignof(VRcInner<VTable, Dyn>));
105 inner->layout = layout;
106 if (!--inner->weak_ref) {
107 inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t *>(inner), layout);
108 }
109 }
110 }
111 VRc(const VRc &other) : inner(other.inner) { inner->strong_ref++; }
112 VRc &operator=(const VRc &other)
113 {
114 if (inner == other.inner)
115 return *this;
116 this->~VRc();
117 new (this) VRc(other);
118 return *this;
119 }
120 /// Construct a new VRc holding an X.
121 ///
122 /// The type X must have a static member `static_vtable` of type VTable
123 template<typename... Args>
124 static VRc make(Args... args)
125 {
126#if !defined(__APPLE__) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
127 auto mem = ::operator new(sizeof(VRcInner<VTable, X>),
128 static_cast<std::align_val_t>(alignof(VRcInner<VTable, X>)));
129#else
130 auto mem = ::operator new(sizeof(VRcInner<VTable, X>));
131#endif
132 auto inner = new (mem) VRcInner<VTable, X>;
133 new (&inner->data) X(args...);
134 return VRc(inner);
135 }
136
137 const X *operator->() const
138 {
139 return &inner->data;
140 }
141 const X &operator*() const
142 {
143 return inner->data;
144 }
145 X *operator->()
146 {
147 return &inner->data;
148 }
149 X &operator*()
150 {
151 return inner->data;
152 }
153
154 const VRc<VTable, Dyn> &into_dyn() const
155 {
156 return *reinterpret_cast<const VRc<VTable, Dyn> *>(this);
157 }
158
159 VRef<VTable> borrow() const
160 {
161 return { inner->vtable, inner->data_ptr() };
162 }
163
164 friend bool operator==(const VRc &a, const VRc &b)
165 {
166 return a.inner == b.inner;
167 }
168 friend bool operator!=(const VRc &a, const VRc &b)
169 {
170 return a.inner != b.inner;
171 }
172 const VTable *vtable() const
173 {
174 return inner->vtable;
175 }
176};
177
178template<typename VTable, typename X = Dyn>
179class VWeak
180{
181 VRcInner<VTable, X> *inner = nullptr;
182
183public:
184 VWeak() = default;
185 ~VWeak()
186 {
187 if (inner && !--inner->weak_ref) {
188 inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t *>(inner),
189 inner->layout);
190 }
191 }
192 VWeak(const VWeak &other) : inner(other.inner) { inner && inner->weak_ref++; }
193 VWeak(const VRc<VTable, X> &other) : inner(other.inner) { inner && inner->weak_ref++; }
194 VWeak &operator=(const VWeak &other)
195 {
196 if (inner == other.inner)
197 return *this;
198 this->~VWeak();
199 new (this) VWeak(other);
200 return *this;
201 }
202
203 std::optional<VRc<VTable, X>> lock() const
204 {
205 if (!inner || inner->strong_ref == 0)
206 return {};
207 inner->strong_ref++;
208 return { VRc<VTable, X>(inner) };
209 }
210
211 const VWeak<VTable, Dyn> &into_dyn() const
212 {
213 return *reinterpret_cast<const VWeak<VTable, Dyn> *>(this);
214 }
215
216 friend bool operator==(const VWeak &a, const VWeak &b) { return a.inner == b.inner; }
217 friend bool operator!=(const VWeak &a, const VWeak &b) { return a.inner != b.inner; }
218 const VTable *vtable() const { return inner ? inner->vtable : nullptr; }
219};
220
221} // namespace vtable
222

source code of slint/api/cpp/include/vtable.h