1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QSHAREDDATA_H |
5 | #define QSHAREDDATA_H |
6 | |
7 | #include <QtCore/qglobal.h> |
8 | #include <QtCore/qatomic.h> |
9 | #include <QtCore/qhashfunctions.h> |
10 | |
11 | #include <functional> |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | |
16 | template <class T> class QSharedDataPointer; |
17 | |
18 | class QSharedData |
19 | { |
20 | public: |
21 | mutable QAtomicInt ref; |
22 | |
23 | QSharedData() noexcept : ref(0) { } |
24 | QSharedData(const QSharedData &) noexcept : ref(0) { } |
25 | |
26 | // using the assignment operator would lead to corruption in the ref-counting |
27 | QSharedData &operator=(const QSharedData &) = delete; |
28 | ~QSharedData() = default; |
29 | }; |
30 | |
31 | struct QAdoptSharedDataTag { explicit constexpr QAdoptSharedDataTag() = default; }; |
32 | |
33 | template <typename T> |
34 | class QSharedDataPointer |
35 | { |
36 | public: |
37 | typedef T Type; |
38 | typedef T *pointer; |
39 | |
40 | void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } |
41 | T &operator*() { detach(); return *d; } |
42 | const T &operator*() const { return *d; } |
43 | T *operator->() { detach(); return d; } |
44 | const T *operator->() const noexcept { return d; } |
45 | operator T *() { detach(); return d; } |
46 | operator const T *() const noexcept { return d; } |
47 | T *data() { detach(); return d; } |
48 | T *get() { detach(); return d; } |
49 | const T *data() const noexcept { return d; } |
50 | const T *get() const noexcept { return d; } |
51 | const T *constData() const noexcept { return d; } |
52 | T *take() noexcept { return std::exchange(d, nullptr); } |
53 | |
54 | Q_NODISCARD_CTOR |
55 | QSharedDataPointer() noexcept : d(nullptr) { } |
56 | ~QSharedDataPointer() { if (d && !d->ref.deref()) delete d; } |
57 | |
58 | Q_NODISCARD_CTOR |
59 | explicit QSharedDataPointer(T *data) noexcept : d(data) |
60 | { if (d) d->ref.ref(); } |
61 | Q_NODISCARD_CTOR |
62 | QSharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data) |
63 | {} |
64 | Q_NODISCARD_CTOR |
65 | QSharedDataPointer(const QSharedDataPointer &o) noexcept : d(o.d) |
66 | { if (d) d->ref.ref(); } |
67 | |
68 | void reset(T *ptr = nullptr) noexcept |
69 | { |
70 | if (ptr != d) { |
71 | if (ptr) |
72 | ptr->ref.ref(); |
73 | T *old = std::exchange(d, ptr); |
74 | if (old && !old->ref.deref()) |
75 | delete old; |
76 | } |
77 | } |
78 | |
79 | QSharedDataPointer &operator=(const QSharedDataPointer &o) noexcept |
80 | { |
81 | reset(ptr: o.d); |
82 | return *this; |
83 | } |
84 | inline QSharedDataPointer &operator=(T *o) noexcept |
85 | { |
86 | reset(ptr: o); |
87 | return *this; |
88 | } |
89 | Q_NODISCARD_CTOR |
90 | QSharedDataPointer(QSharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {} |
91 | QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedDataPointer) |
92 | |
93 | operator bool () const noexcept { return d != nullptr; } |
94 | bool operator!() const noexcept { return d == nullptr; } |
95 | |
96 | void swap(QSharedDataPointer &other) noexcept |
97 | { qt_ptr_swap(d, other.d); } |
98 | |
99 | #define DECLARE_COMPARE_SET(T1, A1, T2, A2) \ |
100 | friend bool operator<(T1, T2) noexcept \ |
101 | { return std::less<T*>{}(A1, A2); } \ |
102 | friend bool operator<=(T1, T2) noexcept \ |
103 | { return !std::less<T*>{}(A2, A1); } \ |
104 | friend bool operator>(T1, T2) noexcept \ |
105 | { return std::less<T*>{}(A2, A1); } \ |
106 | friend bool operator>=(T1, T2) noexcept \ |
107 | { return !std::less<T*>{}(A1, A2); } \ |
108 | friend bool operator==(T1, T2) noexcept \ |
109 | { return A1 == A2; } \ |
110 | friend bool operator!=(T1, T2) noexcept \ |
111 | { return A1 != A2; } \ |
112 | |
113 | DECLARE_COMPARE_SET(const QSharedDataPointer &p1, p1.d, const QSharedDataPointer &p2, p2.d) |
114 | DECLARE_COMPARE_SET(const QSharedDataPointer &p1, p1.d, const T *ptr, ptr) |
115 | DECLARE_COMPARE_SET(const T *ptr, ptr, const QSharedDataPointer &p2, p2.d) |
116 | DECLARE_COMPARE_SET(const QSharedDataPointer &p1, p1.d, std::nullptr_t, nullptr) |
117 | DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QSharedDataPointer &p2, p2.d) |
118 | |
119 | protected: |
120 | T *clone(); |
121 | |
122 | private: |
123 | void detach_helper(); |
124 | |
125 | T *d; |
126 | }; |
127 | |
128 | template <typename T> |
129 | class QExplicitlySharedDataPointer |
130 | { |
131 | public: |
132 | typedef T Type; |
133 | typedef T *pointer; |
134 | |
135 | T &operator*() const { return *d; } |
136 | T *operator->() noexcept { return d; } |
137 | T *operator->() const noexcept { return d; } |
138 | explicit operator T *() { return d; } |
139 | explicit operator const T *() const noexcept { return d; } |
140 | T *data() const noexcept { return d; } |
141 | T *get() const noexcept { return d; } |
142 | const T *constData() const noexcept { return d; } |
143 | T *take() noexcept { return std::exchange(d, nullptr); } |
144 | |
145 | void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } |
146 | |
147 | Q_NODISCARD_CTOR |
148 | QExplicitlySharedDataPointer() noexcept : d(nullptr) { } |
149 | ~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d; } |
150 | |
151 | Q_NODISCARD_CTOR |
152 | explicit QExplicitlySharedDataPointer(T *data) noexcept : d(data) |
153 | { if (d) d->ref.ref(); } |
154 | Q_NODISCARD_CTOR |
155 | QExplicitlySharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data) |
156 | {} |
157 | Q_NODISCARD_CTOR |
158 | QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer &o) noexcept : d(o.d) |
159 | { if (d) d->ref.ref(); } |
160 | |
161 | template<typename X> |
162 | Q_NODISCARD_CTOR |
163 | QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer<X> &o) noexcept |
164 | #ifdef QT_ENABLE_QEXPLICITLYSHAREDDATAPOINTER_STATICCAST |
165 | : d(static_cast<T *>(o.data())) |
166 | #else |
167 | : d(o.data()) |
168 | #endif |
169 | { if (d) d->ref.ref(); } |
170 | |
171 | void reset(T *ptr = nullptr) noexcept |
172 | { |
173 | if (ptr != d) { |
174 | if (ptr) |
175 | ptr->ref.ref(); |
176 | T *old = std::exchange(d, ptr); |
177 | if (old && !old->ref.deref()) |
178 | delete old; |
179 | } |
180 | } |
181 | |
182 | QExplicitlySharedDataPointer &operator=(const QExplicitlySharedDataPointer &o) noexcept |
183 | { |
184 | reset(ptr: o.d); |
185 | return *this; |
186 | } |
187 | QExplicitlySharedDataPointer &operator=(T *o) noexcept |
188 | { |
189 | reset(ptr: o); |
190 | return *this; |
191 | } |
192 | Q_NODISCARD_CTOR |
193 | QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {} |
194 | QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QExplicitlySharedDataPointer) |
195 | |
196 | operator bool () const noexcept { return d != nullptr; } |
197 | bool operator!() const noexcept { return d == nullptr; } |
198 | |
199 | void swap(QExplicitlySharedDataPointer &other) noexcept |
200 | { qt_ptr_swap(d, other.d); } |
201 | |
202 | DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, const QExplicitlySharedDataPointer &p2, p2.d) |
203 | DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, const T *ptr, ptr) |
204 | DECLARE_COMPARE_SET(const T *ptr, ptr, const QExplicitlySharedDataPointer &p2, p2.d) |
205 | DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, std::nullptr_t, nullptr) |
206 | DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QExplicitlySharedDataPointer &p2, p2.d) |
207 | |
208 | #undef DECLARE_COMPARE_SET |
209 | |
210 | protected: |
211 | T *clone(); |
212 | |
213 | private: |
214 | void detach_helper(); |
215 | |
216 | T *d; |
217 | }; |
218 | |
219 | // Declared here and as Q_OUTOFLINE_TEMPLATE to work-around MSVC bug causing missing symbols at link time. |
220 | template <typename T> |
221 | Q_INLINE_TEMPLATE T *QSharedDataPointer<T>::clone() |
222 | { |
223 | return new T(*d); |
224 | } |
225 | |
226 | template <typename T> |
227 | Q_OUTOFLINE_TEMPLATE void QSharedDataPointer<T>::detach_helper() |
228 | { |
229 | T *x = clone(); |
230 | x->ref.ref(); |
231 | if (!d->ref.deref()) |
232 | delete d; |
233 | d = x; |
234 | } |
235 | |
236 | template <typename T> |
237 | Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone() |
238 | { |
239 | return new T(*d); |
240 | } |
241 | |
242 | template <typename T> |
243 | Q_OUTOFLINE_TEMPLATE void QExplicitlySharedDataPointer<T>::detach_helper() |
244 | { |
245 | T *x = clone(); |
246 | x->ref.ref(); |
247 | if (!d->ref.deref()) |
248 | delete d; |
249 | d = x; |
250 | } |
251 | |
252 | template <typename T> |
253 | void swap(QSharedDataPointer<T> &p1, QSharedDataPointer<T> &p2) noexcept |
254 | { p1.swap(p2); } |
255 | |
256 | template <typename T> |
257 | void swap(QExplicitlySharedDataPointer<T> &p1, QExplicitlySharedDataPointer<T> &p2) noexcept |
258 | { p1.swap(p2); } |
259 | |
260 | template <typename T> |
261 | size_t qHash(const QSharedDataPointer<T> &ptr, size_t seed = 0) noexcept |
262 | { |
263 | return qHash(ptr.data(), seed); |
264 | } |
265 | template <typename T> |
266 | size_t qHash(const QExplicitlySharedDataPointer<T> &ptr, size_t seed = 0) noexcept |
267 | { |
268 | return qHash(ptr.data(), seed); |
269 | } |
270 | |
271 | template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedDataPointer<T>, Q_RELOCATABLE_TYPE); |
272 | template<typename T> Q_DECLARE_TYPEINFO_BODY(QExplicitlySharedDataPointer<T>, Q_RELOCATABLE_TYPE); |
273 | |
274 | #define QT_DECLARE_QSDP_SPECIALIZATION_DTOR(Class) \ |
275 | template<> QSharedDataPointer<Class>::~QSharedDataPointer(); |
276 | |
277 | #define QT_DECLARE_QSDP_SPECIALIZATION_DTOR_WITH_EXPORT(Class, ExportMacro) \ |
278 | template<> ExportMacro QSharedDataPointer<Class>::~QSharedDataPointer(); |
279 | |
280 | #define QT_DEFINE_QSDP_SPECIALIZATION_DTOR(Class) \ |
281 | template<> QSharedDataPointer<Class>::~QSharedDataPointer() \ |
282 | { \ |
283 | if (d && !d->ref.deref()) \ |
284 | delete d; \ |
285 | } |
286 | |
287 | #define QT_DECLARE_QESDP_SPECIALIZATION_DTOR(Class) \ |
288 | template<> QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer(); |
289 | |
290 | #define QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(Class, ExportMacro) \ |
291 | template<> ExportMacro QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer(); |
292 | |
293 | #define QT_DEFINE_QESDP_SPECIALIZATION_DTOR(Class) \ |
294 | template<> QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer() \ |
295 | { \ |
296 | if (d && !d->ref.deref()) \ |
297 | delete d; \ |
298 | } |
299 | |
300 | QT_END_NAMESPACE |
301 | |
302 | #endif // QSHAREDDATA_H |
303 | |