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