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
13QT_BEGIN_NAMESPACE
14
15
16template <class T> class QSharedDataPointer;
17
18class QSharedData
19{
20public:
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
31struct QAdoptSharedDataTag { explicit constexpr QAdoptSharedDataTag() = default; };
32
33template <typename T>
34class QSharedDataPointer
35{
36public:
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
119protected:
120 T *clone();
121
122private:
123 void detach_helper();
124
125 T *d;
126};
127
128template <typename T>
129class QExplicitlySharedDataPointer
130{
131public:
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
210protected:
211 T *clone();
212
213private:
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.
220template <typename T>
221Q_INLINE_TEMPLATE T *QSharedDataPointer<T>::clone()
222{
223 return new T(*d);
224}
225
226template <typename T>
227Q_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
236template <typename T>
237Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone()
238{
239 return new T(*d);
240}
241
242template <typename T>
243Q_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
252template <typename T>
253void swap(QSharedDataPointer<T> &p1, QSharedDataPointer<T> &p2) noexcept
254{ p1.swap(p2); }
255
256template <typename T>
257void swap(QExplicitlySharedDataPointer<T> &p1, QExplicitlySharedDataPointer<T> &p2) noexcept
258{ p1.swap(p2); }
259
260template <typename T>
261size_t qHash(const QSharedDataPointer<T> &ptr, size_t seed = 0) noexcept
262{
263 return qHash(ptr.data(), seed);
264}
265template <typename T>
266size_t qHash(const QExplicitlySharedDataPointer<T> &ptr, size_t seed = 0) noexcept
267{
268 return qHash(ptr.data(), seed);
269}
270
271template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedDataPointer<T>, Q_RELOCATABLE_TYPE);
272template<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
300QT_END_NAMESPACE
301
302#endif // QSHAREDDATA_H
303

source code of qtbase/src/corelib/tools/qshareddata.h