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
25QT_BEGIN_NAMESPACE
26
27class QBindingStorage;
28
29template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
30class QObjectCompatProperty;
31
32struct QBindingObserverPtr;
33using PendingBindingObserverList = QVarLengthArray<QBindingObserverPtr>;
34
35namespace QtPrivate {
36// QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline
37// the constructor and copy constructor
38struct RefCounted {
39 int ref = 0;
40 void addRef() {++ref;}
41 bool deref() {--ref; return ref;}
42};
43}
44
45class QQmlPropertyBinding;
46class QPropertyBindingPrivate;
47class QPropertyBindingPrivatePtr
48{
49public:
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
115private:
116 QtPrivate::RefCounted *d;
117};
118
119class QUntypedPropertyBinding;
120class QPropertyBindingPrivate;
121struct QPropertyBindingDataPointer;
122class QPropertyObserver;
123struct QPropertyObserverPointer;
124
125class QUntypedPropertyData
126{
127public:
128 // sentinel to check whether a class inherits QUntypedPropertyData
129 struct InheritsQUntypedPropertyData {};
130};
131
132template <typename T>
133class QPropertyData;
134
135// Used for grouped property evaluations
136namespace QtPrivate {
137class QPropertyBindingData;
138}
139struct QPropertyDelayedNotifications;
140struct 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
153namespace QtPrivate {
154struct 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*/
160struct MSVCWorkAround {};
161
162struct 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
209template<typename Callable, typename PropertyType=MSVCWorkAround>
210inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>();
211
212
213// writes binding result into dataPtr
214struct QPropertyBindingFunction {
215 const QtPrivate::BindingFunctionVTable *vtable;
216 void *functor;
217};
218
219using QPropertyObserverCallback = void (*)(QUntypedPropertyData *);
220using 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 */
232class 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)
245public:
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;
290private:
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
325template <typename T, typename Tag>
326class QTagPreservingPointerToPointer
327{
328public:
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
367private:
368 quintptr *d = nullptr;
369};
370
371namespace detail {
372 template <typename F>
373 struct ExtractClassFromFunctionPointer;
374
375 template<typename T, typename C>
376 struct ExtractClassFromFunctionPointer<T C::*> { using Class = 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
390QT_END_NAMESPACE
391
392#endif // QPROPERTYPRIVATE_H
393

source code of qtbase/src/corelib/kernel/qpropertyprivate.h