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 | public: |
128 | // sentinel to check whether a class inherits QUntypedPropertyData |
129 | struct InheritsQUntypedPropertyData {}; |
130 | }; |
131 | |
132 | template <typename T> |
133 | class QPropertyData; |
134 | |
135 | // Used for grouped property evaluations |
136 | namespace QtPrivate { |
137 | class QPropertyBindingData; |
138 | } |
139 | struct QPropertyDelayedNotifications; |
140 | struct QPropertyProxyBindingData |
141 | { |
142 | // acts as QPropertyBindingData::d_ptr |
143 | quintptr d_ptr; |
144 | /* |
145 | The two members below store the original binding data and property |
146 | data pointer of the property which gets proxied. |
147 | They are set in QPropertyDelayedNotifications::addProperty |
148 | */ |
149 | const QtPrivate::QPropertyBindingData *originalBindingData; |
150 | QUntypedPropertyData *propertyData; |
151 | }; |
152 | |
153 | namespace QtPrivate { |
154 | struct BindingEvaluationState; |
155 | |
156 | /* used in BindingFunctionVTable::createFor; on all other compilers, void would work, but on |
157 | MSVC this causes C2182 when compiling in C++20 mode. As we only need to provide some default |
158 | value which gets ignored, we introduce this dummy type. |
159 | */ |
160 | struct MSVCWorkAround {}; |
161 | |
162 | struct BindingFunctionVTable |
163 | { |
164 | using CallFn = bool(*)(QMetaType, QUntypedPropertyData *, void *); |
165 | using DtorFn = void(*)(void *); |
166 | using MoveCtrFn = void(*)(void *, void *); |
167 | const CallFn call; |
168 | const DtorFn destroy; |
169 | const MoveCtrFn moveConstruct; |
170 | const qsizetype size; |
171 | |
172 | template<typename Callable, typename PropertyType=MSVCWorkAround> |
173 | static constexpr BindingFunctionVTable createFor() |
174 | { |
175 | static_assert (alignof(Callable) <= alignof(std::max_align_t), "Bindings do not support overaligned functors!" ); |
176 | return { |
177 | /*call=*/[](QMetaType metaType, QUntypedPropertyData *dataPtr, void *f){ |
178 | if constexpr (!std::is_invocable_v<Callable>) { |
179 | // we got an untyped callable |
180 | static_assert (std::is_invocable_r_v<bool, Callable, QMetaType, QUntypedPropertyData *> ); |
181 | auto untypedEvaluationFunction = static_cast<Callable *>(f); |
182 | return std::invoke(*untypedEvaluationFunction, metaType, dataPtr); |
183 | } else if constexpr (!std::is_same_v<PropertyType, MSVCWorkAround>) { |
184 | Q_UNUSED(metaType); |
185 | QPropertyData<PropertyType> *propertyPtr = static_cast<QPropertyData<PropertyType> *>(dataPtr); |
186 | // That is allowed by POSIX even if Callable is a function pointer |
187 | auto evaluationFunction = static_cast<Callable *>(f); |
188 | PropertyType newValue = std::invoke(*evaluationFunction); |
189 | if constexpr (QTypeTraits::has_operator_equal_v<PropertyType>) { |
190 | if (newValue == propertyPtr->valueBypassingBindings()) |
191 | return false; |
192 | } |
193 | propertyPtr->setValueBypassingBindings(std::move(newValue)); |
194 | return true; |
195 | } else { |
196 | // Our code will never instantiate this |
197 | Q_UNREACHABLE_RETURN(false); |
198 | } |
199 | }, |
200 | /*destroy*/[](void *f){ static_cast<Callable *>(f)->~Callable(); }, |
201 | /*moveConstruct*/[](void *addr, void *other){ |
202 | new (addr) Callable(std::move(*static_cast<Callable *>(other))); |
203 | }, |
204 | /*size*/sizeof(Callable) |
205 | }; |
206 | } |
207 | }; |
208 | |
209 | template<typename Callable, typename PropertyType=MSVCWorkAround> |
210 | inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>(); |
211 | |
212 | |
213 | // writes binding result into dataPtr |
214 | struct QPropertyBindingFunction { |
215 | const QtPrivate::BindingFunctionVTable *vtable; |
216 | void *functor; |
217 | }; |
218 | |
219 | using QPropertyObserverCallback = void (*)(QUntypedPropertyData *); |
220 | using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction); |
221 | |
222 | /*! |
223 | \internal |
224 | A property normally consists of the actual property value and metadata for the binding system. |
225 | QPropertyBindingData is the latter part. It stores a pointer to either |
226 | - a (potentially empty) linked list of notifiers, in case there is no binding set, |
227 | - an actual QUntypedPropertyBinding when the property has a binding, |
228 | - or a pointer to QPropertyProxyBindingData when notifications occur inside a grouped update. |
229 | |
230 | \sa QPropertyDelayedNotifications, beginPropertyUpdateGroup |
231 | */ |
232 | class Q_CORE_EXPORT QPropertyBindingData |
233 | { |
234 | // Mutable because the address of the observer of the currently evaluating binding is stored here, for |
235 | // notification later when the value changes. |
236 | mutable quintptr d_ptr = 0; |
237 | friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer); |
238 | friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding); |
239 | friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications); |
240 | |
241 | template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter> |
242 | friend class QT_PREPEND_NAMESPACE(QObjectCompatProperty); |
243 | |
244 | Q_DISABLE_COPY(QPropertyBindingData) |
245 | public: |
246 | QPropertyBindingData() = default; |
247 | QPropertyBindingData(QPropertyBindingData &&other); |
248 | QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete; |
249 | ~QPropertyBindingData(); |
250 | |
251 | // Is d_ptr pointing to a binding (1) or list of notifiers (0)? |
252 | static inline constexpr quintptr BindingBit = 0x1; |
253 | // Is d_ptr pointing to QPropertyProxyBindingData (1) or to an actual binding/list of notifiers? |
254 | static inline constexpr quintptr DelayedNotificationBit = 0x2; |
255 | |
256 | bool hasBinding() const { return d_ptr & BindingBit; } |
257 | bool isNotificationDelayed() const { return d_ptr & DelayedNotificationBit; } |
258 | |
259 | QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding, |
260 | QUntypedPropertyData *propertyDataPtr, |
261 | QPropertyObserverCallback staticObserverCallback = nullptr, |
262 | QPropertyBindingWrapper bindingWrapper = nullptr); |
263 | |
264 | QPropertyBindingPrivate *binding() const |
265 | { |
266 | quintptr dd = d(); |
267 | if (dd & BindingBit) |
268 | return reinterpret_cast<QPropertyBindingPrivate*>(dd - BindingBit); |
269 | return nullptr; |
270 | |
271 | } |
272 | |
273 | void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused |
274 | |
275 | void removeBinding() |
276 | { |
277 | if (hasBinding()) |
278 | removeBinding_helper(); |
279 | } |
280 | |
281 | void registerWithCurrentlyEvaluatingBinding(QtPrivate::BindingEvaluationState *currentBinding) const |
282 | { |
283 | if (!currentBinding) |
284 | return; |
285 | registerWithCurrentlyEvaluatingBinding_helper(currentBinding); |
286 | } |
287 | void registerWithCurrentlyEvaluatingBinding() const; |
288 | void notifyObservers(QUntypedPropertyData *propertyDataPtr) const; |
289 | void notifyObservers(QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage) const; |
290 | private: |
291 | /*! |
292 | \internal |
293 | Returns a reference to d_ptr, except when d_ptr points to a proxy. |
294 | In that case, a reference to proxy->d_ptr is returned instead. |
295 | |
296 | To properly support proxying, direct access to d_ptr only occurs when |
297 | - a function actually deals with proxying (e.g. |
298 | QPropertyDelayedNotifications::addProperty), |
299 | - only the tag value is accessed (e.g. hasBinding) or |
300 | - inside a constructor. |
301 | */ |
302 | quintptr &d_ref() const |
303 | { |
304 | quintptr &d = d_ptr; |
305 | if (isNotificationDelayed()) |
306 | return proxyData()->d_ptr; |
307 | return d; |
308 | } |
309 | quintptr d() const { return d_ref(); } |
310 | QPropertyProxyBindingData *proxyData() const |
311 | { |
312 | Q_ASSERT(isNotificationDelayed()); |
313 | return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit)); |
314 | } |
315 | void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const; |
316 | void removeBinding_helper(); |
317 | |
318 | enum NotificationResult { Delayed, Evaluated }; |
319 | NotificationResult notifyObserver_helper( |
320 | QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage, |
321 | QPropertyObserverPointer observer, |
322 | PendingBindingObserverList &bindingObservers) const; |
323 | }; |
324 | |
325 | template <typename T, typename Tag> |
326 | class QTagPreservingPointerToPointer |
327 | { |
328 | public: |
329 | constexpr QTagPreservingPointerToPointer() = default; |
330 | |
331 | QTagPreservingPointerToPointer(T **ptr) |
332 | : d(reinterpret_cast<quintptr*>(ptr)) |
333 | {} |
334 | |
335 | QTagPreservingPointerToPointer<T, Tag> &operator=(T **ptr) |
336 | { |
337 | d = reinterpret_cast<quintptr *>(ptr); |
338 | return *this; |
339 | } |
340 | |
341 | QTagPreservingPointerToPointer<T, Tag> &operator=(QTaggedPointer<T, Tag> *ptr) |
342 | { |
343 | d = reinterpret_cast<quintptr *>(ptr); |
344 | return *this; |
345 | } |
346 | |
347 | void clear() |
348 | { |
349 | d = nullptr; |
350 | } |
351 | |
352 | void setPointer(T *ptr) |
353 | { |
354 | *d = reinterpret_cast<quintptr>(ptr) | (*d & QTaggedPointer<T, Tag>::tagMask()); |
355 | } |
356 | |
357 | T *get() const |
358 | { |
359 | return reinterpret_cast<T*>(*d & QTaggedPointer<T, Tag>::pointerMask()); |
360 | } |
361 | |
362 | explicit operator bool() const |
363 | { |
364 | return d != nullptr; |
365 | } |
366 | |
367 | private: |
368 | quintptr *d = nullptr; |
369 | }; |
370 | |
371 | namespace detail { |
372 | template <typename F> |
373 | struct ; |
374 | |
375 | template<typename T, typename C> |
376 | struct <T C::*> { using = C; }; |
377 | |
378 | constexpr size_t getOffset(size_t o) |
379 | { |
380 | return o; |
381 | } |
382 | constexpr size_t getOffset(size_t (*offsetFn)()) |
383 | { |
384 | return offsetFn(); |
385 | } |
386 | } |
387 | |
388 | } // namespace QtPrivate |
389 | |
390 | QT_END_NAMESPACE |
391 | |
392 | #endif // QPROPERTYPRIVATE_H |
393 | |