Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qproperty_p.h
Go to the documentation of this file.
1// Copyright (C) 2022 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// Qt-Security score:significant reason:default
4
5#ifndef QPROPERTY_P_H
6#define QPROPERTY_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists for the convenience
13// of a number of Qt sources files. This header file may change from
14// version to version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <private/qglobal_p.h>
20#include <qproperty.h>
21
22#include <qmetaobject.h>
23#include <qscopedvaluerollback.h>
24#include <qvariant.h>
25#include <vector>
26#include <QtCore/QVarLengthArray>
27
28#include <memory>
29
30QT_BEGIN_NAMESPACE
31
32namespace QtPrivate {
33 Q_CORE_EXPORT bool isAnyBindingEvaluating();
35
36 namespace BindableWarnings {
37 Q_CORE_EXPORT void printSignalArgumentsWithCustomGetter();
38 }
39}
40
41
42/*!
43 \internal
44 Similar to \c QPropertyBindingPrivatePtr, but stores a
45 \c QPropertyObserver * linking to the QPropertyBindingPrivate*
46 instead of the QPropertyBindingPrivate* itself
47 */
49{
50private:
51 QPropertyObserver *d = nullptr;
52public:
56 { qt_ptr_swap(d, other.d); }
59
60
62 inline ~QBindingObserverPtr();
63 inline QPropertyBindingPrivate *binding() const noexcept;
64 inline QPropertyObserver *operator ->();
65};
66
67using PendingBindingObserverList = QVarLengthArray<QPropertyBindingPrivatePtr>;
68
69// Keep all classes related to QProperty in one compilation unit. Performance of this code is crucial and
70// we need to allow the compiler to inline where it makes sense.
71
72// This is a helper "namespace"
74{
76
78 {
79 return ptr->binding();
80 }
81
82 void setObservers(QPropertyObserver *observer)
83 {
84 auto &d = ptr->d_ref();
85 observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
86 d = reinterpret_cast<quintptr>(observer);
87 }
88 static void fixupAfterMove(QtPrivate::QPropertyBindingData *ptr);
89 Q_ALWAYS_INLINE void addObserver(QPropertyObserver *observer);
90 inline void setFirstObserver(QPropertyObserver *observer);
92 static QPropertyProxyBindingData *proxyData(QtPrivate::QPropertyBindingData *ptr);
93
94 inline int observerCount() const;
95
96 template <typename T>
98 {
99 return QPropertyBindingDataPointer{&property.bindingData()};
100 }
101};
102
104{
106
120
121 QPropertyObserver *next() const { return m_placeHolder.next.data(); }
122
124};
125
126// This is a helper "namespace"
128{
129 QPropertyObserver *ptr = nullptr;
130
131 void unlink()
132 {
133 unlink_common();
134#if QT_DEPRECATED_SINCE(6, 6)
135 QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
136 if (ptr->next.tag() == QPropertyObserver::ObserverIsAlias)
137 ptr->aliasData = nullptr;
138 QT_WARNING_POP
139#endif
140 }
141
143 {
144#if QT_DEPRECATED_SINCE(6, 6)
145 QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
146 Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsAlias);
147 QT_WARNING_POP
148#endif
149 unlink_common();
150 }
151
152 void setBindingToNotify(QPropertyBindingPrivate *binding)
153 {
154 Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
155 ptr->binding = binding;
156 ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
157 }
158
159 void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding);
160 void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
161
163
164 void notify(QUntypedPropertyData *propertyDataPtr);
165#ifndef QT_NO_DEBUG
166 void noSelfDependencies(QPropertyBindingPrivate *binding);
167#else
169#endif
170 void evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status);
172
173 explicit operator bool() const { return ptr != nullptr; }
174
175 QPropertyObserverPointer nextObserver() const { return {ptr->next.data()}; }
176
178 {
179 Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding);
180 return ptr->binding;
181 }
182
183private:
184 void unlink_common()
185 {
186 if (ptr->next)
187 ptr->next->prev = ptr->prev;
188 if (ptr->prev)
189 ptr->prev.setPointer(ptr->next.data());
190 ptr->next = nullptr;
191 ptr->prev.clear();
192 }
193};
194
201
202namespace QtPrivate {
203
217
218/*!
219 * \internal
220 * CompatPropertySafePoint needs to be constructed before the setter of
221 * a QObjectCompatProperty runs. It prevents spurious binding dependencies
222 * caused by reads of properties inside the compat property setter.
223 * Moreover, it ensures that we don't destroy bindings when using operator=
224 */
239
240/*!
241 * \internal
242 * While the regular QProperty notification for a compat property runs we
243 * don't want to have any currentCompatProperty set. This would be a _different_
244 * one than the one we are current evaluating. Therefore it's misleading and
245 * prevents the registering of actual dependencies.
246 */
258
259}
260
261class Q_CORE_EXPORT QPropertyBindingPrivate : public QtPrivate::RefCounted
262{
263private:
264 friend struct QPropertyBindingDataPointer;
265 friend class QPropertyBindingPrivatePtr;
266
267 using ObserverArray = std::array<QPropertyObserver, 4>;
268
269private:
270
271 // used to detect binding loops for lazy evaluated properties
272 bool updating = false;
273 bool hasStaticObserver = false;
274 bool pendingNotify = false;
275 bool hasBindingWrapper:1;
276 // used to detect binding loops for eagerly evaluated properties
277 bool isQQmlPropertyBinding:1;
278 /* a sticky binding does not get removed in removeBinding
279 this is used to support QQmlPropertyData::DontRemoveBinding
280 in qtdeclarative
281 */
282 bool m_sticky:1;
283
284 const QtPrivate::BindingFunctionVTable *vtable;
285
286 union {
287 QtPrivate::QPropertyObserverCallback staticObserverCallback = nullptr;
288 QtPrivate::QPropertyBindingWrapper staticBindingWrapper;
289 };
290 ObserverArray inlineDependencyObservers; // for things we are observing
291
292 QPropertyObserverPointer firstObserver; // list of observers observing us
293 std::unique_ptr<std::vector<QPropertyObserver>> heapObservers; // for things we are observing
294
295protected:
296 QUntypedPropertyData *propertyDataPtr = nullptr;
297
298 /* For bindings set up from C++, location stores where the binding was created in the C++ source
299 For QQmlPropertyBinding that information does not make sense, and the location in the QML file
300 is stored somewhere else. To make efficient use of the space, we instead provide a scratch space
301 for QQmlPropertyBinding (which stores further binding information there).
302 Anything stored in the union must be trivially destructible.
303 (checked in qproperty.cpp)
304
305 The discriminator for this union is the isQQmlPropertyBinding flag above. It has nothing to
306 do with custom vtables per se. Depending on what is stored in declarativeExtraData, you may
307 want vtable support for it, but since declarativeExtraData is well inside
308 QPropertyBindingPrivate it can be a regular vtable. In practice, QML bindings have custom
309 vtables because they store additional data _outside_ QPropertyBindingPrivate.
310 */
311 using DeclarativeErrorCallback = void(*)(QPropertyBindingPrivate *);
312 union {
313 QPropertyBindingSourceLocation location;
314 struct {
315 std::byte declarativeExtraData[sizeof(QPropertyBindingSourceLocation) - sizeof(DeclarativeErrorCallback)];
316 DeclarativeErrorCallback errorCallBack;
317 };
318 };
319private:
320 QPropertyBindingError m_error;
321
322 QMetaType metaType;
323
324public:
325 static constexpr size_t getSizeEnsuringAlignment() {
326 constexpr auto align = alignof (std::max_align_t) - 1;
327 constexpr size_t sizeEnsuringAlignment = (sizeof(QPropertyBindingPrivate) + align) & ~align;
328 static_assert (sizeEnsuringAlignment % alignof (std::max_align_t) == 0,
329 "Required for placement new'ing the function behind it.");
330 return sizeEnsuringAlignment;
331 }
332
333 const QtPrivate::BindingFunctionVTable *bindingFunctionVTable() const { return vtable; }
334
335 // public because the auto-tests access it, too.
336 size_t dependencyObserverCount = 0;
337
338 bool isUpdating() {return updating;}
339 void setSticky(bool keep = true) {m_sticky = keep;}
340 bool isSticky() {return m_sticky;}
341 void scheduleNotify() {pendingNotify = true;}
342
343 QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
344 const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
345 : hasBindingWrapper(false)
346 , isQQmlPropertyBinding(isQQmlPropertyBinding)
347 , m_sticky(false)
348 , vtable(vtable)
349 , location(location)
350 , metaType(metaType)
351 {}
352 ~QPropertyBindingPrivate();
353
354
355 void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; }
356 void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper)
357 {
358 Q_ASSERT(!(callback && bindingWrapper));
359 if (callback) {
360 hasStaticObserver = true;
361 hasBindingWrapper = false;
362 staticObserverCallback = callback;
363 } else if (bindingWrapper) {
364 hasStaticObserver = false;
365 hasBindingWrapper = true;
366 staticBindingWrapper = bindingWrapper;
367 } else {
368 hasStaticObserver = false;
369 hasBindingWrapper = false;
370 staticObserverCallback = nullptr;
371 }
372 }
373 void prependObserver(QPropertyObserverPointer observer)
374 {
375 observer.ptr->prev = const_cast<QPropertyObserver **>(&firstObserver.ptr);
376 firstObserver = observer;
377 }
378
379 QPropertyObserverPointer takeObservers()
380 {
381 auto observers = firstObserver;
382 firstObserver.ptr = nullptr;
383 return observers;
384 }
385
386 void clearDependencyObservers();
387
388 Q_ALWAYS_INLINE QPropertyObserverPointer allocateDependencyObserver() {
389 if (dependencyObserverCount < inlineDependencyObservers.size()) {
390 ++dependencyObserverCount;
391 return {&inlineDependencyObservers[dependencyObserverCount - 1]};
392 }
393 return allocateDependencyObserver_slow();
394 }
395
396 QPropertyObserverPointer allocateDependencyObserver_slow();
397
398 QPropertyBindingSourceLocation sourceLocation() const
399 {
400 if (!isQQmlPropertyBinding)
401 return location;
402 return []() {
403 constexpr auto msg = "Custom location";
404 QPropertyBindingSourceLocation result;
405 result.fileName = msg;
406 return result;
407 }();
408 }
409 QPropertyBindingError bindingError() const { return m_error; }
410 QMetaType valueMetaType() const { return metaType; }
411
412 void unlinkAndDeref();
413
414 bool evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status = nullptr);
415
416 Q_ALWAYS_INLINE bool evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status);
417
418 void notifyNonRecursive(const PendingBindingObserverList &bindingObservers);
419 enum NotificationState : bool { Delayed, Sent };
420 NotificationState notifyNonRecursive();
421
422 static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding)
423 { return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
424 static QUntypedPropertyBinding makeUntyped(QPropertyBindingPrivate *d)
425 { return QUntypedPropertyBinding(static_cast<void *>(d)); }
426
427 void setError(QPropertyBindingError &&e)
428 { m_error = std::move(e); }
429
430 void detachFromProperty()
431 {
432 hasStaticObserver = false;
433 hasBindingWrapper = false;
434 propertyDataPtr = nullptr;
435 clearDependencyObservers();
436 }
437
438 static QPropertyBindingPrivate *currentlyEvaluatingBinding();
439
440 /* In some cases we want to run the vtable's dtor _in place_ of ~QPropertBindingPrivate() rather
441 than in addition, in order to cater to special memory layouts with extra trailing fields. The
442 vtable's size member acts as flag for this. We call this a "custom" vtable. Note that you
443 could have a QML binding with a regular vtable, as well as a non-QML binding with custom
444 vtable. In practice, QML bindings have custom vtables because they store additional data
445 after QPropertyBindingPrivate.
446 */
447 bool hasCustomVTable() const
448 {
449 return vtable->size == 0;
450 }
451
452 /* Determines whether we have a regular source location or declarativeExtraData + errorCallback.
453 * This has nothing to do with custom vtables per se.
454 */
455 bool isQmlBinding() const
456 {
457 return isQQmlPropertyBinding;
458 }
459
460 static void destroyAndFreeMemory(QPropertyBindingPrivate *priv) {
461 if (priv->hasCustomVTable()) {
462 // special hack for QQmlPropertyBinding which has a
463 // different memory layout than normal QPropertyBindings
464 priv->vtable->destroy(priv);
465 } else{
466 priv->~QPropertyBindingPrivate();
467 delete[] reinterpret_cast<std::byte *>(priv);
468 }
469 }
470};
471
472inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer)
473{
474 if (auto *b = binding()) {
475 b->firstObserver.ptr = observer;
476 return;
477 }
478 auto &d = ptr->d_ref();
479 d = reinterpret_cast<quintptr>(observer);
480}
481
482inline void QPropertyBindingDataPointer::fixupAfterMove(QtPrivate::QPropertyBindingData *ptr)
483{
484 auto &d = ptr->d_ref();
485 if (ptr->isNotificationDelayed()) {
486 QPropertyProxyBindingData *proxy = ptr->proxyData();
487 Q_ASSERT(proxy);
488 proxy->originalBindingData = ptr;
489 }
490 // If QPropertyBindingData has been moved, and it has an observer
491 // we have to adjust the firstObserver's prev pointer to point to
492 // the moved to QPropertyBindingData's d_ptr
493 if (d & QtPrivate::QPropertyBindingData::BindingBit)
494 return; // nothing to do if the observer is stored in the binding
495 if (auto observer = reinterpret_cast<QPropertyObserver *>(d))
496 observer->prev = reinterpret_cast<QPropertyObserver **>(&d);
497}
498
500{
501 if (auto *b = binding())
502 return b->firstObserver;
503 return { reinterpret_cast<QPropertyObserver *>(ptr->d()) };
504}
505
506/*!
507 \internal
508 Returns the proxy data of \a ptr, or \c nullptr if \a ptr has no delayed notification
509 */
510inline QPropertyProxyBindingData *QPropertyBindingDataPointer::proxyData(QtPrivate::QPropertyBindingData *ptr)
511{
512 if (!ptr->isNotificationDelayed())
513 return nullptr;
514 return ptr->proxyData();
515}
516
518{
519 int count = 0;
520 for (auto observer = firstObserver(); observer; observer = observer.nextObserver())
521 ++count;
522 return count;
523}
524
525namespace QtPrivate {
526 Q_CORE_EXPORT bool isPropertyInBindingWrapper(const QUntypedPropertyData *property);
527 void Q_CORE_EXPORT initBindingStatusThreadId();
528}
529
530template<typename Class, typename T, auto Offset, auto Setter, auto Signal = nullptr,
531 auto Getter = nullptr>
533{
534 template<typename Property, typename>
536
538 using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
539 Class *owner()
540 {
541 char *that = reinterpret_cast<char *>(this);
542 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
543 }
544 const Class *owner() const
545 {
546 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
547 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
548 }
549
550 static bool bindingWrapper(QMetaType type, QUntypedPropertyData *dataPtr, QtPrivate::QPropertyBindingFunction binding)
551 {
552 auto *thisData = static_cast<ThisType *>(dataPtr);
553 QBindingStorage *storage = qGetBindingStorage(thisData->owner());
554 QPropertyData<T> copy(thisData->valueBypassingBindings());
555 {
556 QtPrivate::CurrentCompatPropertyThief thief(storage->bindingStatus);
557 if (!binding.vtable->call(type, &copy, binding.functor))
558 return false;
559 }
560 // ensure value and setValue know we're currently evaluating our binding
561 QtPrivate::CompatPropertySafePoint guardThis(storage->bindingStatus, thisData);
562 (thisData->owner()->*Setter)(copy.valueBypassingBindings());
563 return true;
564 }
565 bool inBindingWrapper(const QBindingStorage *storage) const
566 {
567 return storage->bindingStatus && storage->bindingStatus->currentCompatProperty
568 && QtPrivate::isPropertyInBindingWrapper(this);
569 }
570
571 inline static T getPropertyValue(const QUntypedPropertyData *d) {
572 auto prop = static_cast<const ThisType *>(d);
573 if constexpr (std::is_null_pointer_v<decltype(Getter)>)
574 return prop->value();
575 else
576 return (prop->owner()->*Getter)();
577 }
578
579 inline static T getPropertyValueBypassingBindings(const QUntypedPropertyData *d) {
580 auto prop = static_cast<const ThisType *>(d);
581 if constexpr (std::is_null_pointer_v<decltype(Getter)>)
582 return prop->valueBypassingBindings();
583 else
584 return (prop->owner()->*Getter)();
585 }
586
587 inline static void warnIfSignalWithArgumentAndCustomGetter()
588 {
589 if constexpr (!std::is_null_pointer_v<decltype(Signal)>
590 && SignalTakesValue::value
591 && !std::is_null_pointer_v<decltype(Getter)>) {
593 }
594 }
595
596public:
597 using value_type = typename QPropertyData<T>::value_type;
600
601 QObjectCompatProperty() { warnIfSignalWithArgumentAndCustomGetter(); }
602 explicit QObjectCompatProperty(const T &initialValue) : QPropertyData<T>(initialValue)
603 {
604 warnIfSignalWithArgumentAndCustomGetter();
605 }
606 explicit QObjectCompatProperty(T &&initialValue) : QPropertyData<T>(std::move(initialValue))
607 {
608 warnIfSignalWithArgumentAndCustomGetter();
609 }
610
612 {
613 const QBindingStorage *storage = qGetBindingStorage(owner());
614 // make sure we don't register this binding as a dependency to itself
615 if (storage->bindingStatus && storage->bindingStatus->currentlyEvaluatingBinding && !inBindingWrapper(storage))
616 storage->registerDependency_helper(this);
617 return this->val;
618 }
619
621 {
622 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
623 return value();
624 } else if constexpr (std::is_pointer_v<T>) {
625 value();
626 return this->val;
627 } else {
628 return;
629 }
630 }
631
633 {
634 return value();
635 }
636
638 {
639 return value();
640 }
641
643 {
644 QBindingStorage *storage = qGetBindingStorage(owner());
645 if (auto *bd = storage->bindingData(this)) {
646 // make sure we don't remove the binding if called from the bindingWrapper
647 if (bd->hasBinding() && !inBindingWrapper(storage))
648 bd->removeBinding_helper();
649 }
650 this->val = t;
651 }
652
654 {
655 setValue(newValue);
656 return *this;
657 }
658
660 {
661 QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
662 QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, nullptr, bindingWrapper));
663 // notification is already handled in QPropertyBindingData::setBinding
664 return static_cast<QPropertyBinding<T> &>(oldBinding);
665 }
666
667 bool setBinding(const QUntypedPropertyBinding &newBinding)
668 {
669 if (!newBinding.isNull() && newBinding.valueMetaType() != QMetaType::fromType<T>())
670 return false;
671 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
672 return true;
673 }
674
675#ifndef Q_QDOC
676 template <typename Functor>
678 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
679 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
680 {
681 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
682 }
683#else
684 template <typename Functor>
686#endif
687
688 bool hasBinding() const {
689 auto *bd = qGetBindingStorage(owner())->bindingData(this);
690 return bd && bd->binding() != nullptr;
691 }
692
694 {
695 QBindingStorage *storage = qGetBindingStorage(owner());
696 if (auto *bd = storage->bindingData(this)) {
697 // make sure we don't remove the binding if called from the bindingWrapper
698 if (bd->hasBinding() && !inBindingWrapper(storage))
699 bd->removeBinding_helper();
700 }
701 }
702
703 void notify()
704 {
705 QBindingStorage *storage = qGetBindingStorage(owner());
706 if (auto bd = storage->bindingData(this, false)) {
707 // This partly duplicates QPropertyBindingData::notifyObservers because we want to
708 // check for inBindingWrapper() after checking for isNotificationDelayed() and
709 // firstObserver. This is because inBindingWrapper() is the most expensive check.
710 if (!bd->isNotificationDelayed()) {
713 if (!inBindingWrapper(storage)) {
714 PendingBindingObserverList bindingObservers;
715 if (bd->notifyObserver_helper(this, storage, observer, bindingObservers)
716 == QtPrivate::QPropertyBindingData::Evaluated) {
717 // evaluateBindings() can trash the observers.
718 // It can also reallocate binding data pointer.
719 // So, we need to re-fetch here.
720 bd = storage->bindingData(this, false);
723 obs.notify(this);
724 for (auto&& bindingPtr: bindingObservers) {
725 auto *binding = static_cast<QPropertyBindingPrivate *>(bindingPtr.get());
726 binding->notifyNonRecursive();
727 }
728 }
729 }
730 }
731 }
732 }
733 if constexpr (!std::is_null_pointer_v<decltype(Signal)>) {
734 if constexpr (SignalTakesValue::value)
735 (owner()->*Signal)(getPropertyValueBypassingBindings(this));
736 else
737 (owner()->*Signal)();
738 }
739 }
740
742 {
743 auto *bd = qGetBindingStorage(owner())->bindingData(this);
744 QUntypedPropertyBinding binding = QPropertyBindingPrivate::makeUntyped(bd ? bd->binding() : nullptr);
745 return static_cast<QPropertyBinding<T> &&>(binding);
746 }
747
752
753 template<typename Functor>
755 {
756 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
757 return QPropertyChangeHandler<Functor>(*this, f);
758 }
759
760 template<typename Functor>
762 {
763 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
764 f();
765 return onValueChanged(f);
766 }
767
768 template<typename Functor>
770 {
771 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
772 return QPropertyNotifier(*this, f);
773 }
774
776 {
777 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
778 return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true);
779 }
780};
781
782namespace QtPrivate {
783template<typename Class, typename Ty, auto Offset, auto Setter, auto Signal, auto Getter>
786{
788 using T = typename Property::value_type;
789public:
790 static constexpr QBindableInterface iface = {
791 [](const QUntypedPropertyData *d, void *value) -> void
792 { *static_cast<T*>(value) = Property::getPropertyValue(d); },
793 [](QUntypedPropertyData *d, const void *value) -> void
794 {
795 (static_cast<Property *>(d)->owner()->*Setter)(*static_cast<const T*>(value));
796 },
798 { return static_cast<const Property *>(d)->binding(); },
800 { return static_cast<Property *>(d)->setBinding(static_cast<const QPropertyBinding<T> &>(binding)); },
802 { return Qt::makePropertyBinding([d]() -> T { return Property::getPropertyValue(d); }, location); },
804 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
805 []() { return QMetaType::fromType<T>(); }
806 };
807};
808}
809
810#define QT_OBJECT_COMPAT_PROPERTY_4(Class, Type, name, setter)
811 static constexpr size_t _qt_property_##name##_offset() {
812 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
813 return offsetof(Class, name);
814 QT_WARNING_POP
815 }
816 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name;
817
818#define QT_OBJECT_COMPAT_PROPERTY_5(Class, Type, name, setter, signal)
819 static constexpr size_t _qt_property_##name##_offset() {
820 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
821 return offsetof(Class, name);
822 QT_WARNING_POP
823 }
824 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name;
825
826#define Q_OBJECT_COMPAT_PROPERTY(...)
827 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
828 QT_OVERLOADED_MACRO(QT_OBJECT_COMPAT_PROPERTY, __VA_ARGS__)
829 QT_WARNING_POP
830
831#define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_5(Class, Type, name, setter, value)
832 static constexpr size_t _qt_property_##name##_offset() {
833 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
834 return offsetof(Class, name);
835 QT_WARNING_POP
836 }
837 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name =
838 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter>(
839 value);
840
841#define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_6(Class, Type, name, setter, signal, value)
842 static constexpr size_t _qt_property_##name##_offset() {
843 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
844 return offsetof(Class, name);
845 QT_WARNING_POP
846 }
847 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name =
848 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter,
849 signal>(value);
850
851#define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_7(Class, Type, name, setter, signal, getter, value)
852 static constexpr size_t _qt_property_##name##_offset() {
853 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
854 return offsetof(Class, name);
855 QT_WARNING_POP
856 }
857 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal, getter>
858 name = QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter,
859 signal, getter>(value);
860
861#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(...)
862 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
863 QT_OVERLOADED_MACRO(QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS, __VA_ARGS__)
864 QT_WARNING_POP
865
866
867namespace QtPrivate {
868Q_CORE_EXPORT BindingEvaluationState *suspendCurrentBindingStatus();
870}
871
873{
875 {
876 return bindable.iface;
877 }
878
880 {
881 return bindable.data;
882 }
883};
884
885inline bool QPropertyBindingPrivate::evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
886{
887 if (updating) {
888 m_error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
889 if (isQQmlPropertyBinding)
890 errorCallBack(this);
891 return false;
892 }
893
894 /*
895 * Evaluating the binding might lead to the binding being broken. This can
896 * cause ref to reach zero at the end of the function. However, the
897 * updateGuard's destructor will then still trigger, trying to set the
898 * updating bool to its old value
899 * To prevent this, we create a QPropertyBindingPrivatePtr which ensures
900 * that the object is still alive when updateGuard's dtor runs.
901 */
902 QPropertyBindingPrivatePtr keepAlive {this};
903
904 QScopedValueRollback<bool> updateGuard(updating, true);
905
906 QtPrivate::BindingEvaluationState evaluationFrame(this, status);
907
908 auto bindingFunctor = reinterpret_cast<std::byte *>(this) +
909 QPropertyBindingPrivate::getSizeEnsuringAlignment();
910 bool changed = false;
911 if (hasBindingWrapper) {
912 changed = staticBindingWrapper(metaType, propertyDataPtr,
913 {vtable, bindingFunctor});
914 } else {
915 changed = vtable->call(metaType, propertyDataPtr, bindingFunctor);
916 }
917 // If there was a change, we must set pendingNotify.
918 // If there was not, we must not clear it, as that only should happen in notifyRecursive
919 pendingNotify = pendingNotify || changed;
920 if (!changed || !firstObserver)
921 return changed;
922
923 firstObserver.noSelfDependencies(this);
924 firstObserver.evaluateBindings(bindingObservers, status);
925 return true;
926}
927
928/*!
929 \internal
930
931 Walks through the list of property observers, and calls any ChangeHandler
932 found there.
933 It doesn't do anything with bindings, which are only handled in
934 QPropertyBindingPrivate::evaluateRecursive.
935 */
936inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
937{
938 auto observer = const_cast<QPropertyObserver*>(ptr);
939 /*
940 * The basic idea of the loop is as follows: We iterate over all observers in the linked list,
941 * and execute the functionality corresponding to their tag.
942 * However, complication arise due to the fact that the triggered operations might modify the list,
943 * which includes deletion and move of the current and next nodes.
944 * Therefore, we take a few safety precautions:
945 * 1. Before executing any action which might modify the list, we insert a placeholder node after the current node.
946 * As that one is stack allocated and owned by us, we can rest assured that it is
947 * still there after the action has executed, and placeHolder->next points to the actual next node in the list.
948 * Note that taking next at the beginning of the loop does not work, as the executed action might either move
949 * or delete that node.
950 * 2. After the triggered action has finished, we can use the next pointer in the placeholder node as a safe way to
951 * retrieve the next node.
952 * 3. Some care needs to be taken to avoid infinite recursion with change handlers, so we add an extra test there, that
953 * checks whether we're already have the same change handler in our call stack. This can be done by checking whether
954 * the node after the current one is a placeholder node.
955 */
956 while (observer) {
957 QPropertyObserver *next = observer->next.data();
958 switch (QPropertyObserver::ObserverTag(observer->next.tag())) {
959 case QPropertyObserver::ObserverNotifiesChangeHandler:
960 {
961 auto handlerToCall = observer->changeHandler;
962 // prevent recursion
963 if (next && next->next.tag() == QPropertyObserver::ObserverIsPlaceholder) {
964 observer = next->next.data();
965 continue;
966 }
967 // handlerToCall might modify the list
968 QPropertyObserverNodeProtector protector(observer);
969 handlerToCall(observer, propertyDataPtr);
970 next = protector.next();
971 break;
972 }
973 case QPropertyObserver::ObserverNotifiesBinding:
974 break;
975 case QPropertyObserver::ObserverIsPlaceholder:
976 // recursion is already properly handled somewhere else
977 break;
978#if QT_DEPRECATED_SINCE(6, 6)
979 QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
980 case QPropertyObserver::ObserverIsAlias:
981 break;
982 QT_WARNING_POP
983#endif
984 default: Q_UNREACHABLE();
985 }
986 observer = next;
987 }
988}
989
991{
992 QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)};
994}
995
996QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) noexcept : d(observer)
997{
998 Q_ASSERT(d);
999 QPropertyObserverPointer{d}.binding()->addRef();
1000}
1001
1003{
1004 if (!d)
1005 return;
1006
1007 QPropertyBindingPrivate *bindingPrivate = binding();
1008 if (!bindingPrivate->deref())
1009 QPropertyBindingPrivate::destroyAndFreeMemory(bindingPrivate);
1010}
1011
1013
1014QPropertyObserver *QBindingObserverPtr::operator->() { return d; }
1015
1016namespace QtPrivate {
1018{
1019 QPropertyBindingData bindingData_;
1020 QObject *obj;
1021 QMetaProperty metaProperty_;
1022
1023#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1024 static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret);
1025#else
1026 static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret);
1027#endif
1028
1029 QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty& p);
1030
1031public:
1032 static QPropertyAdaptorSlotObject *cast(QSlotObjectBase *ptr, int propertyIndex)
1033 {
1034 if (ptr->isImpl(&QPropertyAdaptorSlotObject::impl)) {
1035 auto p = static_cast<QPropertyAdaptorSlotObject *>(ptr);
1036 if (p->metaProperty_.propertyIndex() == propertyIndex)
1037 return p;
1038 }
1039 return nullptr;
1040 }
1041
1042 inline const QPropertyBindingData &bindingData() const { return bindingData_; }
1043 inline QPropertyBindingData &bindingData() { return bindingData_; }
1044 inline QObject *object() const { return obj; }
1045 inline const QMetaProperty &metaProperty() const { return metaProperty_; }
1046
1048};
1049}
1050
1051QT_END_NAMESPACE
1052
1053#endif // QPROPERTY_P_H
\inmodule QtCore
\macro Q_OBJECT_BINDABLE_PROPERTY(containingClass, type, name, signal)
QPropertyBinding< T > binding() const
operator parameter_type() const
typename QPropertyData< T >::value_type value_type
QtPrivate::QPropertyBindingData & bindingData() const
QObjectCompatProperty & operator=(parameter_type newValue)
typename QPropertyData< T >::arrow_operator_result arrow_operator_result
typename QPropertyData< T >::parameter_type parameter_type
parameter_type value() const
QPropertyChangeHandler< Functor > onValueChanged(Functor f)
void setValue(parameter_type t)
void removeBindingUnlessInWrapper()
bool setBinding(const QUntypedPropertyBinding &newBinding)
QPropertyBinding< T > setBinding(Functor &&f, const QPropertyBindingSourceLocation &location=QT_PROPERTY_DEFAULT_BINDING_LOCATION, std::enable_if_t< std::is_invocable_v< Functor > > *=nullptr)
arrow_operator_result operator->() const
QObjectCompatProperty(T &&initialValue)
QPropertyNotifier addNotifier(Functor f)
parameter_type operator*() const
QObjectCompatProperty(const T &initialValue)
QPropertyBinding< T > takeBinding()
QPropertyChangeHandler< Functor > subscribe(Functor f)
bool hasBinding() const
QPropertyBinding< T > setBinding(const QPropertyBinding< T > &newBinding)
\inmodule QtCore
Definition qproperty.h:302
\inmodule QtCore
Definition qproperty.h:71
\inmodule QtCore
Definition qproperty.h:329
\inmodule QtCore
Definition qproperty.h:361
~QTimerPrivate() override
QTimerPrivate(QTimer *qq)
Definition qtimer_p.h:28
void setIntervalDuration(std::chrono::nanoseconds nsec)
Definition qtimer_p.h:43
static constexpr int INV_TIMER
Definition qtimer_p.h:41
void setInterval(int msec)
Definition qtimer_p.h:53
bool isActive() const
Definition qtimer_p.h:59
const bool isQTimer
Definition qtimer_p.h:71
QTimerPrivate(std::chrono::nanoseconds nsec, QChronoTimer *qq)
Definition qtimer_p.h:33
Qt::TimerId id
Definition qtimer_p.h:61
\inmodule QtCore
Definition qtimer.h:21
\inmodule QtCore
Definition qproperty.h:751
const QtPrivate::QBindableInterface * iface
Definition qproperty.h:755
\inmodule QtCore
Definition qproperty.h:160
QPropertyBindingData & bindingData()
friend class QT_PREPEND_NAMESPACE(QUntypedBindable)
const QMetaProperty & metaProperty() const
static QPropertyAdaptorSlotObject * cast(QSlotObjectBase *ptr, int propertyIndex)
const QPropertyBindingData & bindingData() const
Combined button and popup list for selecting options.
Q_CORE_EXPORT void printSignalArgumentsWithCustomGetter()
void assertObjectType(QObjectPrivate *d)
Definition qobject_p.h:251
Q_CORE_EXPORT bool isAnyBindingEvaluating()
Q_CORE_EXPORT void restoreBindingStatus(BindingEvaluationState *status)
const QObject * getQObject(const QObjectPrivate *d)
Definition qobject_p.h:246
Q_CORE_EXPORT bool isPropertyInBindingWrapper(const QUntypedPropertyData *property)
Definition qcompare.h:111
#define QT_CONCAT(B, M, m, u)
Definition qobject_p.h:43
QBindingStorage * qGetBindingStorage(QObjectPrivate *o)
Definition qobject_p.h:456
QBindingStorage * qGetBindingStorage(QObjectPrivate::ExtraData *ed)
Definition qobject_p.h:464
const QBindingStorage * qGetBindingStorage(const QObjectPrivate *o)
Definition qobject_p.h:452
#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(...)
Definition qproperty.h:1356
#define QT_PROPERTY_DEFAULT_BINDING_LOCATION
Definition qproperty.h:48
#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...)
Definition qproperty.h:1445
#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(...)
QBindingObserverPtr()=default
QPropertyObserver * operator->()
QPropertyBindingPrivate * binding() const noexcept
static QPropertyProxyBindingData * proxyData(QtPrivate::QPropertyBindingData *ptr)
static void fixupAfterMove(QtPrivate::QPropertyBindingData *ptr)
Q_ALWAYS_INLINE void addObserver(QPropertyObserver *observer)
Definition qproperty.cpp:41
const QtPrivate::QPropertyBindingData * ptr
Definition qproperty_p.h:75
static QPropertyBindingDataPointer get(QProperty< T > &property)
Definition qproperty_p.h:97
QPropertyBindingPrivate * binding() const
Definition qproperty_p.h:77
void setObservers(QPropertyObserver *observer)
Definition qproperty_p.h:82
QPropertyObserverPointer firstObserver() const
void setFirstObserver(QPropertyObserver *observer)
QPropertyObserver * next() const
void noSelfDependencies(QPropertyBindingPrivate *binding)
void notify(QUntypedPropertyData *propertyDataPtr)
QPropertyBindingPrivate * binding() const
void evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
void observeProperty(QPropertyBindingDataPointer property)
QPropertyObserver * ptr
QPropertyObserverPointer nextObserver() const
void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding)
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler)
void setBindingToNotify(QPropertyBindingPrivate *binding)
void(* BeginCallback)(QObject *caller, int signal_or_method_index, void **argv)
Definition qobject_p.h:64
BeginCallback slot_begin_callback
Definition qobject_p.h:67
EndCallback slot_end_callback
Definition qobject_p.h:69
EndCallback signal_end_callback
Definition qobject_p.h:68
BeginCallback signal_begin_callback
Definition qobject_p.h:66
void(* EndCallback)(QObject *caller, int signal_or_method_index)
Definition qobject_p.h:65
static QtPrivate::QBindableInterface const * getInterface(const QUntypedBindable &bindable)
static QUntypedPropertyData * getPropertyData(const QUntypedBindable &bindable)
QPropertyBindingPrivate * binding
QVarLengthArray< const QPropertyBindingData *, 8 > alreadyCaptureProperties
BindingEvaluationState(QPropertyBindingPrivate *binding, QBindingStatus *status)
BindingEvaluationState * previousState
BindingEvaluationState ** currentState
QtPrivate::BindingEvaluationState ** currentlyEvaluatingBindingList
CompatPropertySafePoint * previousState
CompatPropertySafePoint ** currentState
QUntypedPropertyData * property
QtPrivate::BindingEvaluationState * bindingState