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{
127};
128
129namespace QtPrivate {
130template <typename T>
131using IsUntypedPropertyData = std::enable_if_t<std::is_base_of_v<QUntypedPropertyData, T>, bool>;
132}
133
134template <typename T>
135class QPropertyData;
136
137// Used for grouped property evaluations
138namespace QtPrivate {
139class QPropertyBindingData;
140}
141struct QPropertyDelayedNotifications;
142struct 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
155namespace QtPrivate {
156struct 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*/
162struct MSVCWorkAround {};
163
164struct 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
211template<typename Callable, typename PropertyType=MSVCWorkAround>
212inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>();
213
214
215// writes binding result into dataPtr
216struct QPropertyBindingFunction {
217 const QtPrivate::BindingFunctionVTable *vtable;
218 void *functor;
219};
220
221using QPropertyObserverCallback = void (*)(QUntypedPropertyData *);
222using 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 */
234class 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)
247public:
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;
292private:
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
327template <typename T, typename Tag>
328class QTagPreservingPointerToPointer
329{
330public:
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
369private:
370 quintptr *d = nullptr;
371};
372
373namespace detail {
374 template <typename F>
375 struct ExtractClassFromFunctionPointer;
376
377 template<typename T, typename C>
378 struct ExtractClassFromFunctionPointer<T C::*> { using Class = 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
392QT_END_NAMESPACE
393
394#endif // QPROPERTYPRIVATE_H
395