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 qExchange(d, nullptr); } |
53 | |
54 | QSharedDataPointer() noexcept : d(nullptr) { } |
55 | ~QSharedDataPointer() { if (d && !d->ref.deref()) delete d; } |
56 | |
57 | explicit QSharedDataPointer(T *data) noexcept : d(data) |
58 | { if (d) d->ref.ref(); } |
59 | QSharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data) |
60 | {} |
61 | QSharedDataPointer(const QSharedDataPointer &o) noexcept : d(o.d) |
62 | { if (d) d->ref.ref(); } |
63 | |
64 | void reset(T *ptr = nullptr) noexcept |
65 | { |
66 | if (ptr != d) { |
67 | if (ptr) |
68 | ptr->ref.ref(); |
69 | T *old = qExchange(d, ptr); |
70 | if (old && !old->ref.deref()) |
71 | delete old; |
72 | } |
73 | } |
74 | |
75 | QSharedDataPointer &operator=(const QSharedDataPointer &o) noexcept |
76 | { |
77 | reset(ptr: o.d); |
78 | return *this; |
79 | } |
80 | inline QSharedDataPointer &operator=(T *o) noexcept |
81 | { |
82 | reset(ptr: o); |
83 | return *this; |
84 | } |
85 | QSharedDataPointer(QSharedDataPointer &&o) noexcept : d(qExchange(o.d, nullptr)) {} |
86 | QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedDataPointer) |
87 | |
88 | operator bool () const noexcept { return d != nullptr; } |
89 | bool operator!() const noexcept { return d == nullptr; } |
90 | |
91 | void swap(QSharedDataPointer &other) noexcept |
92 | { qt_ptr_swap(d, other.d); } |
93 | |
94 | #define DECLARE_COMPARE_SET(T1, A1, T2, A2) \ |
95 | friend bool operator<(T1, T2) noexcept \ |
96 | { return std::less<T*>{}(A1, A2); } \ |
97 | friend bool operator<=(T1, T2) noexcept \ |
98 | { return !std::less<T*>{}(A2, A1); } \ |
99 | friend bool operator>(T1, T2) noexcept \ |
100 | { return std::less<T*>{}(A2, A1); } \ |
101 | friend bool operator>=(T1, T2) noexcept \ |
102 | { return !std::less<T*>{}(A1, A2); } \ |
103 | friend bool operator==(T1, T2) noexcept \ |
104 | { return A1 == A2; } \ |
105 | friend bool operator!=(T1, T2) noexcept \ |
106 | { return A1 != A2; } \ |
107 | |
108 | DECLARE_COMPARE_SET(const QSharedDataPointer &p1, p1.d, const QSharedDataPointer &p2, p2.d) |
109 | DECLARE_COMPARE_SET(const QSharedDataPointer &p1, p1.d, const T *ptr, ptr) |
110 | DECLARE_COMPARE_SET(const T *ptr, ptr, const QSharedDataPointer &p2, p2.d) |
111 | DECLARE_COMPARE_SET(const QSharedDataPointer &p1, p1.d, std::nullptr_t, nullptr) |
112 | DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QSharedDataPointer &p2, p2.d) |
113 | |
114 | protected: |
115 | T *clone(); |
116 | |
117 | private: |
118 | void detach_helper(); |
119 | |
120 | T *d; |
121 | }; |
122 | |
123 | template <typename T> |
124 | class QExplicitlySharedDataPointer |
125 | { |
126 | public: |
127 | typedef T Type; |
128 | typedef T *pointer; |
129 | |
130 | T &operator*() const { return *d; } |
131 | T *operator->() noexcept { return d; } |
132 | T *operator->() const noexcept { return d; } |
133 | explicit operator T *() { return d; } |
134 | explicit operator const T *() const noexcept { return d; } |
135 | T *data() const noexcept { return d; } |
136 | T *get() const noexcept { return d; } |
137 | const T *constData() const noexcept { return d; } |
138 | T *take() noexcept { return qExchange(d, nullptr); } |
139 | |
140 | void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); } |
141 | |
142 | QExplicitlySharedDataPointer() noexcept : d(nullptr) { } |
143 | ~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d; } |
144 | |
145 | explicit QExplicitlySharedDataPointer(T *data) noexcept : d(data) |
146 | { if (d) d->ref.ref(); } |
147 | QExplicitlySharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data) |
148 | {} |
149 | QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer &o) noexcept : d(o.d) |
150 | { if (d) d->ref.ref(); } |
151 | |
152 | template<typename X> |
153 | QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer<X> &o) noexcept |
154 | #ifdef QT_ENABLE_QEXPLICITLYSHAREDDATAPOINTER_STATICCAST |
155 | : d(static_cast<T *>(o.data())) |
156 | #else |
157 | : d(o.data()) |
158 | #endif |
159 | { if (d) d->ref.ref(); } |
160 | |
161 | void reset(T *ptr = nullptr) noexcept |
162 | { |
163 | if (ptr != d) { |
164 | if (ptr) |
165 | ptr->ref.ref(); |
166 | T *old = qExchange(d, ptr); |
167 | if (old && !old->ref.deref()) |
168 | delete old; |
169 | } |
170 | } |
171 | |
172 | QExplicitlySharedDataPointer &operator=(const QExplicitlySharedDataPointer &o) noexcept |
173 | { |
174 | reset(ptr: o.d); |
175 | return *this; |
176 | } |
177 | QExplicitlySharedDataPointer &operator=(T *o) noexcept |
178 | { |
179 | reset(ptr: o); |
180 | return *this; |
181 | } |
182 | QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept : d(qExchange(o.d, nullptr)) {} |
183 | QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QExplicitlySharedDataPointer) |
184 | |
185 | operator bool () const noexcept { return d != nullptr; } |
186 | bool operator!() const noexcept { return d == nullptr; } |
187 | |
188 | void swap(QExplicitlySharedDataPointer &other) noexcept |
189 | { qt_ptr_swap(d, other.d); } |
190 | |
191 | DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, const QExplicitlySharedDataPointer &p2, p2.d) |
192 | DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, const T *ptr, ptr) |
193 | DECLARE_COMPARE_SET(const T *ptr, ptr, const QExplicitlySharedDataPointer &p2, p2.d) |
194 | DECLARE_COMPARE_SET(const QExplicitlySharedDataPointer &p1, p1.d, std::nullptr_t, nullptr) |
195 | DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QExplicitlySharedDataPointer &p2, p2.d) |
196 | |
197 | #undef DECLARE_COMPARE_SET |
198 | |
199 | protected: |
200 | T *clone(); |
201 | |
202 | private: |
203 | void detach_helper(); |
204 | |
205 | T *d; |
206 | }; |
207 | |
208 | // Declared here and as Q_OUTOFLINE_TEMPLATE to work-around MSVC bug causing missing symbols at link time. |
209 | template <typename T> |
210 | Q_INLINE_TEMPLATE T *QSharedDataPointer<T>::clone() |
211 | { |
212 | return new T(*d); |
213 | } |
214 | |
215 | template <typename T> |
216 | Q_OUTOFLINE_TEMPLATE void QSharedDataPointer<T>::detach_helper() |
217 | { |
218 | T *x = clone(); |
219 | x->ref.ref(); |
220 | if (!d->ref.deref()) |
221 | delete d; |
222 | d = x; |
223 | } |
224 | |
225 | template <typename T> |
226 | Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone() |
227 | { |
228 | return new T(*d); |
229 | } |
230 | |
231 | template <typename T> |
232 | Q_OUTOFLINE_TEMPLATE void QExplicitlySharedDataPointer<T>::detach_helper() |
233 | { |
234 | T *x = clone(); |
235 | x->ref.ref(); |
236 | if (!d->ref.deref()) |
237 | delete d; |
238 | d = x; |
239 | } |
240 | |
241 | template <typename T> |
242 | void swap(QSharedDataPointer<T> &p1, QSharedDataPointer<T> &p2) noexcept |
243 | { p1.swap(p2); } |
244 | |
245 | template <typename T> |
246 | void swap(QExplicitlySharedDataPointer<T> &p1, QExplicitlySharedDataPointer<T> &p2) noexcept |
247 | { p1.swap(p2); } |
248 | |
249 | template <typename T> |
250 | size_t qHash(const QSharedDataPointer<T> &ptr, size_t seed = 0) noexcept |
251 | { |
252 | return qHash(ptr.data(), seed); |
253 | } |
254 | template <typename T> |
255 | size_t qHash(const QExplicitlySharedDataPointer<T> &ptr, size_t seed = 0) noexcept |
256 | { |
257 | return qHash(ptr.data(), seed); |
258 | } |
259 | |
260 | template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedDataPointer<T>, Q_RELOCATABLE_TYPE); |
261 | template<typename T> Q_DECLARE_TYPEINFO_BODY(QExplicitlySharedDataPointer<T>, Q_RELOCATABLE_TYPE); |
262 | |
263 | #define QT_DECLARE_QSDP_SPECIALIZATION_DTOR(Class) \ |
264 | template<> QSharedDataPointer<Class>::~QSharedDataPointer(); |
265 | |
266 | #define QT_DECLARE_QSDP_SPECIALIZATION_DTOR_WITH_EXPORT(Class, ExportMacro) \ |
267 | template<> ExportMacro QSharedDataPointer<Class>::~QSharedDataPointer(); |
268 | |
269 | #define QT_DEFINE_QSDP_SPECIALIZATION_DTOR(Class) \ |
270 | template<> QSharedDataPointer<Class>::~QSharedDataPointer() \ |
271 | { \ |
272 | if (d && !d->ref.deref()) \ |
273 | delete d; \ |
274 | } |
275 | |
276 | #define QT_DECLARE_QESDP_SPECIALIZATION_DTOR(Class) \ |
277 | template<> QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer(); |
278 | |
279 | #define QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(Class, ExportMacro) \ |
280 | template<> ExportMacro QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer(); |
281 | |
282 | #define QT_DEFINE_QESDP_SPECIALIZATION_DTOR(Class) \ |
283 | template<> QExplicitlySharedDataPointer<Class>::~QExplicitlySharedDataPointer() \ |
284 | { \ |
285 | if (d && !d->ref.deref()) \ |
286 | delete d; \ |
287 | } |
288 | |
289 | QT_END_NAMESPACE |
290 | |
291 | #endif // QSHAREDDATA_H |
292 | |