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 QPROPERTYPRIVATE_H |
5 | #define QPROPERTYPRIVATE_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include <QtCore/qglobal.h> |
19 | #include <QtCore/qtaggedpointer.h> |
20 | #include <QtCore/qmetatype.h> |
21 | #include <QtCore/qcontainerfwd.h> |
22 | |
23 | #include <functional> |
24 | |
25 | QT_BEGIN_NAMESPACE |
26 | |
27 | class QBindingStorage; |
28 | |
29 | template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter> |
30 | class QObjectCompatProperty; |
31 | |
32 | struct QBindingObserverPtr; |
33 | using PendingBindingObserverList = QVarLengthArray<QBindingObserverPtr>; |
34 | |
35 | namespace QtPrivate { |
36 | // QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline |
37 | // the constructor and copy constructor |
38 | struct RefCounted { |
39 | int ref = 0; |
40 | void addRef() {++ref;} |
41 | bool deref() {--ref; return ref;} |
42 | }; |
43 | } |
44 | |
45 | class QQmlPropertyBinding; |
46 | class QPropertyBindingPrivate; |
47 | class QPropertyBindingPrivatePtr |
48 | { |
49 | public: |
50 | using T = QtPrivate::RefCounted; |
51 | T &operator*() const { return *d; } |
52 | T *operator->() noexcept { return d; } |
53 | T *operator->() const noexcept { return d; } |
54 | explicit operator T *() { return d; } |
55 | explicit operator const T *() const noexcept { return d; } |
56 | T *data() const noexcept { return d; } |
57 | T *get() const noexcept { return d; } |
58 | const T *constData() const noexcept { return d; } |
59 | T *take() noexcept { T *x = d; d = nullptr; return x; } |
60 | |
61 | QPropertyBindingPrivatePtr() noexcept : d(nullptr) { } |
62 | ~QPropertyBindingPrivatePtr() |
63 | { |
64 | if (d && (--d->ref == 0)) |
65 | destroyAndFreeMemory(); |
66 | } |
67 | Q_CORE_EXPORT void destroyAndFreeMemory(); |
68 | |
69 | explicit QPropertyBindingPrivatePtr(T *data) noexcept : d(data) { if (d) d->addRef(); } |
70 | QPropertyBindingPrivatePtr(const QPropertyBindingPrivatePtr &o) noexcept |
71 | : d(o.d) { if (d) d->addRef(); } |
72 | |
73 | void reset(T *ptr = nullptr) noexcept; |
74 | |
75 | QPropertyBindingPrivatePtr &operator=(const QPropertyBindingPrivatePtr &o) noexcept |
76 | { |
77 | reset(ptr: o.d); |
78 | return *this; |
79 | } |
80 | QPropertyBindingPrivatePtr &operator=(T *o) noexcept |
81 | { |
82 | reset(ptr: o); |
83 | return *this; |
84 | } |
85 | QPropertyBindingPrivatePtr(QPropertyBindingPrivatePtr &&o) noexcept : d(std::exchange(obj&: o.d, new_val: nullptr)) {} |
86 | QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPropertyBindingPrivatePtr) |
87 | |
88 | operator bool () const noexcept { return d != nullptr; } |
89 | bool operator!() const noexcept { return d == nullptr; } |
90 | |
91 | void swap(QPropertyBindingPrivatePtr &other) noexcept |
92 | { qt_ptr_swap(lhs&: d, rhs&: other.d); } |
93 | |
94 | friend bool operator==(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept |
95 | { return p1.d == p2.d; } |
96 | friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept |
97 | { return p1.d != p2.d; } |
98 | friend bool operator==(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept |
99 | { return p1.d == ptr; } |
100 | friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept |
101 | { return p1.d != ptr; } |
102 | friend bool operator==(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept |
103 | { return ptr == p2.d; } |
104 | friend bool operator!=(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept |
105 | { return ptr != p2.d; } |
106 | friend bool operator==(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept |
107 | { return !p1; } |
108 | friend bool operator!=(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept |
109 | { return p1; } |
110 | friend bool operator==(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept |
111 | { return !p2; } |
112 | friend bool operator!=(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept |
113 | { return p2; } |
114 | |
115 | private: |
116 | QtPrivate::RefCounted *d; |
117 | }; |
118 | |
119 | class QUntypedPropertyBinding; |
120 | class QPropertyBindingPrivate; |
121 | struct QPropertyBindingDataPointer; |
122 | class QPropertyObserver; |
123 | struct QPropertyObserverPointer; |
124 | |
125 | class QUntypedPropertyData |
126 | { |
127 | }; |
128 | |
129 | namespace QtPrivate { |
130 | template <typename T> |
131 | using IsUntypedPropertyData = std::enable_if_t<std::is_base_of_v<QUntypedPropertyData, T>, bool>; |
132 | } |
133 | |
134 | template <typename T> |
135 | class QPropertyData; |
136 | |
137 | // Used for grouped property evaluations |
138 | namespace QtPrivate { |
139 | class QPropertyBindingData; |
140 | } |
141 | struct QPropertyDelayedNotifications; |
142 | struct QPropertyProxyBindingData |
143 | { |
144 | // acts as QPropertyBindingData::d_ptr |
145 | quintptr d_ptr; |
146 | /* |
147 | The two members below store the original binding data and property |
148 | data pointer of the property which gets proxied. |
149 | They are set in QPropertyDelayedNotifications::addProperty |
150 | */ |
151 | const QtPrivate::QPropertyBindingData *originalBindingData; |
152 | QUntypedPropertyData *propertyData; |
153 | }; |
154 | |
155 | namespace QtPrivate { |
156 | struct BindingEvaluationState; |
157 | |
158 | /* used in BindingFunctionVTable::createFor; on all other compilers, void would work, but on |
159 | MSVC this causes C2182 when compiling in C++20 mode. As we only need to provide some default |
160 | value which gets ignored, we introduce this dummy type. |
161 | */ |
162 | struct MSVCWorkAround {}; |
163 | |
164 | struct BindingFunctionVTable |
165 | { |
166 | using CallFn = bool(*)(QMetaType, QUntypedPropertyData *, void *); |
167 | using DtorFn = void(*)(void *); |
168 | using MoveCtrFn = void(*)(void *, void *); |
169 | const CallFn call; |
170 | const DtorFn destroy; |
171 | const MoveCtrFn moveConstruct; |
172 | const qsizetype size; |
173 | |
174 | template<typename Callable, typename PropertyType=MSVCWorkAround> |
175 | static constexpr BindingFunctionVTable createFor() |
176 | { |
177 | static_assert (alignof(Callable) <= alignof(std::max_align_t), "Bindings do not support overaligned functors!" ); |
178 | return { |
179 | /*call=*/[](QMetaType metaType, QUntypedPropertyData *dataPtr, void *f){ |
180 | if constexpr (!std::is_invocable_v<Callable>) { |
181 | // we got an untyped callable |
182 | static_assert (std::is_invocable_r_v<bool, Callable, QMetaType, QUntypedPropertyData *> ); |
183 | auto untypedEvaluationFunction = static_cast<Callable *>(f); |
184 | return std::invoke(*untypedEvaluationFunction, metaType, dataPtr); |
185 | } else if constexpr (!std::is_same_v<PropertyType, MSVCWorkAround>) { |
186 | Q_UNUSED(metaType); |
187 | QPropertyData<PropertyType> *propertyPtr = static_cast<QPropertyData<PropertyType> *>(dataPtr); |
188 | // That is allowed by POSIX even if Callable is a function pointer |
189 | auto evaluationFunction = static_cast<Callable *>(f); |
190 | PropertyType newValue = std::invoke(*evaluationFunction); |
191 | if constexpr (QTypeTraits::has_operator_equal_v<PropertyType>) { |
192 | if (newValue == propertyPtr->valueBypassingBindings()) |
193 | return false; |
194 | } |
195 | propertyPtr->setValueBypassingBindings(std::move(newValue)); |
196 | return true; |
197 | } else { |
198 | // Our code will never instantiate this |
199 | Q_UNREACHABLE_RETURN(false); |
200 | } |
201 | }, |
202 | /*destroy*/[](void *f){ static_cast<Callable *>(f)->~Callable(); }, |
203 | /*moveConstruct*/[](void *addr, void *other){ |
204 | new (addr) Callable(std::move(*static_cast<Callable *>(other))); |
205 | }, |
206 | /*size*/sizeof(Callable) |
207 | }; |
208 | } |
209 | }; |
210 | |
211 | template<typename Callable, typename PropertyType=MSVCWorkAround> |
212 | inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>(); |
213 | |
214 | |
215 | // writes binding result into dataPtr |
216 | struct QPropertyBindingFunction { |
217 | const QtPrivate::BindingFunctionVTable *vtable; |
218 | void *functor; |
219 | }; |
220 | |
221 | using QPropertyObserverCallback = void (*)(QUntypedPropertyData *); |
222 | using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction); |
223 | |
224 | /*! |
225 | \internal |
226 | A property normally consists of the actual property value and metadata for the binding system. |
227 | QPropertyBindingData is the latter part. It stores a pointer to either |
228 | - a (potentially empty) linked list of notifiers, in case there is no binding set, |
229 | - an actual QUntypedPropertyBinding when the property has a binding, |
230 | - or a pointer to QPropertyProxyBindingData when notifications occur inside a grouped update. |
231 | |
232 | \sa QPropertyDelayedNotifications, beginPropertyUpdateGroup |
233 | */ |
234 | class Q_CORE_EXPORT QPropertyBindingData |
235 | { |
236 | // Mutable because the address of the observer of the currently evaluating binding is stored here, for |
237 | // notification later when the value changes. |
238 | mutable quintptr d_ptr = 0; |
239 | friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer); |
240 | friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding); |
241 | friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications); |
242 | |
243 | template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter> |
244 | friend class QT_PREPEND_NAMESPACE(QObjectCompatProperty); |
245 | |
246 | Q_DISABLE_COPY(QPropertyBindingData) |
247 | public: |
248 | QPropertyBindingData() = default; |
249 | QPropertyBindingData(QPropertyBindingData &&other); |
250 | QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete; |
251 | ~QPropertyBindingData(); |
252 | |
253 | // Is d_ptr pointing to a binding (1) or list of notifiers (0)? |
254 | static inline constexpr quintptr BindingBit = 0x1; |
255 | // Is d_ptr pointing to QPropertyProxyBindingData (1) or to an actual binding/list of notifiers? |
256 | static inline constexpr quintptr DelayedNotificationBit = 0x2; |
257 | |
258 | bool hasBinding() const { return d_ptr & BindingBit; } |
259 | bool isNotificationDelayed() const { return d_ptr & DelayedNotificationBit; } |
260 | |
261 | QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding, |
262 | QUntypedPropertyData *propertyDataPtr, |
263 | QPropertyObserverCallback staticObserverCallback = nullptr, |
264 | QPropertyBindingWrapper bindingWrapper = nullptr); |
265 | |
266 | QPropertyBindingPrivate *binding() const |
267 | { |
268 | quintptr dd = d(); |
269 | if (dd & BindingBit) |
270 | return reinterpret_cast<QPropertyBindingPrivate*>(dd - BindingBit); |
271 | return nullptr; |
272 | |
273 | } |
274 | |
275 | void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused |
276 | |
277 | void removeBinding() |
278 | { |
279 | if (hasBinding()) |
280 | removeBinding_helper(); |
281 | } |
282 | |
283 | void registerWithCurrentlyEvaluatingBinding(QtPrivate::BindingEvaluationState *currentBinding) const |
284 | { |
285 | if (!currentBinding) |
286 | return; |
287 | registerWithCurrentlyEvaluatingBinding_helper(currentBinding); |
288 | } |
289 | void registerWithCurrentlyEvaluatingBinding() const; |
290 | void notifyObservers(QUntypedPropertyData *propertyDataPtr) const; |
291 | void notifyObservers(QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage) const; |
292 | private: |
293 | /*! |
294 | \internal |
295 | Returns a reference to d_ptr, except when d_ptr points to a proxy. |
296 | In that case, a reference to proxy->d_ptr is returned instead. |
297 | |
298 | To properly support proxying, direct access to d_ptr only occurs when |
299 | - a function actually deals with proxying (e.g. |
300 | QPropertyDelayedNotifications::addProperty), |
301 | - only the tag value is accessed (e.g. hasBinding) or |
302 | - inside a constructor. |
303 | */ |
304 | quintptr &d_ref() const |
305 | { |
306 | quintptr &d = d_ptr; |
307 | if (isNotificationDelayed()) |
308 | return proxyData()->d_ptr; |
309 | return d; |
310 | } |
311 | quintptr d() const { return d_ref(); } |
312 | QPropertyProxyBindingData *proxyData() const |
313 | { |
314 | Q_ASSERT(isNotificationDelayed()); |
315 | return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit)); |
316 | } |
317 | void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const; |
318 | void removeBinding_helper(); |
319 | |
320 | enum NotificationResult { Delayed, Evaluated }; |
321 | NotificationResult notifyObserver_helper( |
322 | QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage, |
323 | QPropertyObserverPointer observer, |
324 | PendingBindingObserverList &bindingObservers) const; |
325 | }; |
326 | |
327 | template <typename T, typename Tag> |
328 | class QTagPreservingPointerToPointer |
329 | { |
330 | public: |
331 | constexpr QTagPreservingPointerToPointer() = default; |
332 | |
333 | QTagPreservingPointerToPointer(T **ptr) |
334 | : d(reinterpret_cast<quintptr*>(ptr)) |
335 | {} |
336 | |
337 | QTagPreservingPointerToPointer<T, Tag> &operator=(T **ptr) |
338 | { |
339 | d = reinterpret_cast<quintptr *>(ptr); |
340 | return *this; |
341 | } |
342 | |
343 | QTagPreservingPointerToPointer<T, Tag> &operator=(QTaggedPointer<T, Tag> *ptr) |
344 | { |
345 | d = reinterpret_cast<quintptr *>(ptr); |
346 | return *this; |
347 | } |
348 | |
349 | void clear() |
350 | { |
351 | d = nullptr; |
352 | } |
353 | |
354 | void setPointer(T *ptr) |
355 | { |
356 | *d = reinterpret_cast<quintptr>(ptr) | (*d & QTaggedPointer<T, Tag>::tagMask()); |
357 | } |
358 | |
359 | T *get() const |
360 | { |
361 | return reinterpret_cast<T*>(*d & QTaggedPointer<T, Tag>::pointerMask()); |
362 | } |
363 | |
364 | explicit operator bool() const |
365 | { |
366 | return d != nullptr; |
367 | } |
368 | |
369 | private: |
370 | quintptr *d = nullptr; |
371 | }; |
372 | |
373 | namespace detail { |
374 | template <typename F> |
375 | struct ; |
376 | |
377 | template<typename T, typename C> |
378 | struct <T C::*> { using = C; }; |
379 | |
380 | constexpr size_t getOffset(size_t o) |
381 | { |
382 | return o; |
383 | } |
384 | constexpr size_t getOffset(size_t (*offsetFn)()) |
385 | { |
386 | return offsetFn(); |
387 | } |
388 | } |
389 | |
390 | } // namespace QtPrivate |
391 | |
392 | QT_END_NAMESPACE |
393 | |
394 | #endif // QPROPERTYPRIVATE_H |
395 | |