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
26QT_BEGIN_NAMESPACE
27
28class QBindingStorage;
29
30template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
31class QObjectCompatProperty;
32
33class QPropertyBindingPrivatePtr;
34using PendingBindingObserverList = QVarLengthArray<QPropertyBindingPrivatePtr>;
35
36namespace QtPrivate {
37// QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline
38// the constructor and copy constructor
39struct RefCounted {
40
41 int refCount() const { return ref; }
42 void addRef() { ++ref; }
43 bool deref() { return --ref != 0; }
44
45private:
46 int ref = 0;
47};
48}
49
50class QQmlPropertyBinding;
51class QPropertyBindingPrivate;
52class QPropertyBindingPrivatePtr
53{
54public:
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
99private:
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
116class QUntypedPropertyBinding;
117class QPropertyBindingPrivate;
118struct QPropertyBindingDataPointer;
119class QPropertyObserver;
120struct QPropertyObserverPointer;
121
122class QUntypedPropertyData
123{
124};
125
126namespace QtPrivate {
127template <typename T>
128using IsUntypedPropertyData = std::enable_if_t<std::is_base_of_v<QUntypedPropertyData, T>, bool>;
129}
130
131template <typename T>
132class QPropertyData;
133
134// Used for grouped property evaluations
135namespace QtPrivate {
136class QPropertyBindingData;
137}
138struct QPropertyDelayedNotifications;
139struct 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
152namespace QtPrivate {
153struct 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*/
159struct MSVCWorkAround {};
160
161struct 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
208template<typename Callable, typename PropertyType=MSVCWorkAround>
209inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>();
210
211
212// writes binding result into dataPtr
213struct QPropertyBindingFunction {
214 const QtPrivate::BindingFunctionVTable *vtable;
215 void *functor;
216};
217
218using QPropertyObserverCallback = void (*)(QUntypedPropertyData *);
219using 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 */
231class 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)
244public:
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;
289private:
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
324template <typename T, typename Tag>
325class QTagPreservingPointerToPointer
326{
327public:
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
366private:
367 quintptr *d = nullptr;
368};
369
370namespace 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
389QT_END_NAMESPACE
390
391#endif // QPROPERTYPRIVATE_H
392

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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