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
334 // public because the auto-tests access it, too.
335 size_t dependencyObserverCount = 0;
336
337 bool isUpdating() {return updating;}
338 void setSticky(bool keep = true) {m_sticky = keep;}
339 bool isSticky() {return m_sticky;}
340 void scheduleNotify() {pendingNotify = true;}
341
342 QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
343 const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
344 : hasBindingWrapper(false)
345 , isQQmlPropertyBinding(isQQmlPropertyBinding)
346 , m_sticky(false)
347 , vtable(vtable)
348 , location(location)
349 , metaType(metaType)
350 {}
351 ~QPropertyBindingPrivate();
352
353
354 void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; }
355 void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper)
356 {
357 Q_ASSERT(!(callback && bindingWrapper));
358 if (callback) {
359 hasStaticObserver = true;
360 hasBindingWrapper = false;
361 staticObserverCallback = callback;
362 } else if (bindingWrapper) {
363 hasStaticObserver = false;
364 hasBindingWrapper = true;
365 staticBindingWrapper = bindingWrapper;
366 } else {
367 hasStaticObserver = false;
368 hasBindingWrapper = false;
369 staticObserverCallback = nullptr;
370 }
371 }
372 void prependObserver(QPropertyObserverPointer observer)
373 {
374 observer.ptr->prev = const_cast<QPropertyObserver **>(&firstObserver.ptr);
375 firstObserver = observer;
376 }
377
378 QPropertyObserverPointer takeObservers()
379 {
380 auto observers = firstObserver;
381 firstObserver.ptr = nullptr;
382 return observers;
383 }
384
385 void clearDependencyObservers();
386
387 Q_ALWAYS_INLINE QPropertyObserverPointer allocateDependencyObserver() {
388 if (dependencyObserverCount < inlineDependencyObservers.size()) {
389 ++dependencyObserverCount;
390 return {&inlineDependencyObservers[dependencyObserverCount - 1]};
391 }
392 return allocateDependencyObserver_slow();
393 }
394
395 QPropertyObserverPointer allocateDependencyObserver_slow();
396
397 QPropertyBindingSourceLocation sourceLocation() const
398 {
399 if (!isQQmlPropertyBinding)
400 return location;
401 return []() {
402 constexpr auto msg = "Custom location";
403 QPropertyBindingSourceLocation result;
404 result.fileName = msg;
405 return result;
406 }();
407 }
408 QPropertyBindingError bindingError() const { return m_error; }
409 QMetaType valueMetaType() const { return metaType; }
410
411 void unlinkAndDeref();
412
413 bool evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status = nullptr);
414
415 Q_ALWAYS_INLINE bool evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status);
416
417 void notifyNonRecursive(const PendingBindingObserverList &bindingObservers);
418 enum NotificationState : bool { Delayed, Sent };
419 NotificationState notifyNonRecursive();
420
421 static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding)
422 { return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
423 static QUntypedPropertyBinding makeUntyped(QPropertyBindingPrivate *d)
424 { return QUntypedPropertyBinding(static_cast<void *>(d)); }
425
426 void setError(QPropertyBindingError &&e)
427 { m_error = std::move(e); }
428
429 void detachFromProperty()
430 {
431 hasStaticObserver = false;
432 hasBindingWrapper = false;
433 propertyDataPtr = nullptr;
434 clearDependencyObservers();
435 }
436
437 static QPropertyBindingPrivate *currentlyEvaluatingBinding();
438
439 /* In some cases we want to run the vtable's dtor _in place_ of ~QPropertBindingPrivate() rather
440 than in addition, in order to cater to special memory layouts with extra trailing fields. The
441 vtable's size member acts as flag for this. We call this a "custom" vtable. Note that you
442 could have a QML binding with a regular vtable, as well as a non-QML binding with custom
443 vtable. In practice, QML bindings have custom vtables because they store additional data
444 after QPropertyBindingPrivate.
445 */
446 bool hasCustomVTable() const
447 {
448 return vtable->size == 0;
449 }
450
451 /* Determines whether we have a regular source location or declarativeExtraData + errorCallback.
452 * This has nothing to do with custom vtables per se.
453 */
454 bool isQmlBinding() const
455 {
456 return isQQmlPropertyBinding;
457 }
458
459 static void destroyAndFreeMemory(QPropertyBindingPrivate *priv) {
460 if (priv->hasCustomVTable()) {
461 // special hack for QQmlPropertyBinding which has a
462 // different memory layout than normal QPropertyBindings
463 priv->vtable->destroy(priv);
464 } else{
465 priv->~QPropertyBindingPrivate();
466 delete[] reinterpret_cast<std::byte *>(priv);
467 }
468 }
469};
470
471inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer)
472{
473 if (auto *b = binding()) {
474 b->firstObserver.ptr = observer;
475 return;
476 }
477 auto &d = ptr->d_ref();
478 d = reinterpret_cast<quintptr>(observer);
479}
480
481inline void QPropertyBindingDataPointer::fixupAfterMove(QtPrivate::QPropertyBindingData *ptr)
482{
483 auto &d = ptr->d_ref();
484 if (ptr->isNotificationDelayed()) {
485 QPropertyProxyBindingData *proxy = ptr->proxyData();
486 Q_ASSERT(proxy);
487 proxy->originalBindingData = ptr;
488 }
489 // If QPropertyBindingData has been moved, and it has an observer
490 // we have to adjust the firstObserver's prev pointer to point to
491 // the moved to QPropertyBindingData's d_ptr
492 if (d & QtPrivate::QPropertyBindingData::BindingBit)
493 return; // nothing to do if the observer is stored in the binding
494 if (auto observer = reinterpret_cast<QPropertyObserver *>(d))
495 observer->prev = reinterpret_cast<QPropertyObserver **>(&d);
496}
497
499{
500 if (auto *b = binding())
501 return b->firstObserver;
502 return { reinterpret_cast<QPropertyObserver *>(ptr->d()) };
503}
504
505/*!
506 \internal
507 Returns the proxy data of \a ptr, or \c nullptr if \a ptr has no delayed notification
508 */
509inline QPropertyProxyBindingData *QPropertyBindingDataPointer::proxyData(QtPrivate::QPropertyBindingData *ptr)
510{
511 if (!ptr->isNotificationDelayed())
512 return nullptr;
513 return ptr->proxyData();
514}
515
517{
518 int count = 0;
519 for (auto observer = firstObserver(); observer; observer = observer.nextObserver())
520 ++count;
521 return count;
522}
523
524namespace QtPrivate {
525 Q_CORE_EXPORT bool isPropertyInBindingWrapper(const QUntypedPropertyData *property);
526 void Q_CORE_EXPORT initBindingStatusThreadId();
527}
528
529template<typename Class, typename T, auto Offset, auto Setter, auto Signal = nullptr,
530 auto Getter = nullptr>
532{
533 template<typename Property, typename>
535
537 using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
538 Class *owner()
539 {
540 char *that = reinterpret_cast<char *>(this);
541 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
542 }
543 const Class *owner() const
544 {
545 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
546 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
547 }
548
549 static bool bindingWrapper(QMetaType type, QUntypedPropertyData *dataPtr, QtPrivate::QPropertyBindingFunction binding)
550 {
551 auto *thisData = static_cast<ThisType *>(dataPtr);
552 QBindingStorage *storage = qGetBindingStorage(thisData->owner());
553 QPropertyData<T> copy(thisData->valueBypassingBindings());
554 {
555 QtPrivate::CurrentCompatPropertyThief thief(storage->bindingStatus);
556 if (!binding.vtable->call(type, &copy, binding.functor))
557 return false;
558 }
559 // ensure value and setValue know we're currently evaluating our binding
560 QtPrivate::CompatPropertySafePoint guardThis(storage->bindingStatus, thisData);
561 (thisData->owner()->*Setter)(copy.valueBypassingBindings());
562 return true;
563 }
564 bool inBindingWrapper(const QBindingStorage *storage) const
565 {
566 return storage->bindingStatus && storage->bindingStatus->currentCompatProperty
567 && QtPrivate::isPropertyInBindingWrapper(this);
568 }
569
570 inline static T getPropertyValue(const QUntypedPropertyData *d) {
571 auto prop = static_cast<const ThisType *>(d);
572 if constexpr (std::is_null_pointer_v<decltype(Getter)>)
573 return prop->value();
574 else
575 return (prop->owner()->*Getter)();
576 }
577
578 inline static T getPropertyValueBypassingBindings(const QUntypedPropertyData *d) {
579 auto prop = static_cast<const ThisType *>(d);
580 if constexpr (std::is_null_pointer_v<decltype(Getter)>)
581 return prop->valueBypassingBindings();
582 else
583 return (prop->owner()->*Getter)();
584 }
585
586 inline static void warnIfSignalWithArgumentAndCustomGetter()
587 {
588 if constexpr (!std::is_null_pointer_v<decltype(Signal)>
589 && SignalTakesValue::value
590 && !std::is_null_pointer_v<decltype(Getter)>) {
592 }
593 }
594
595public:
596 using value_type = typename QPropertyData<T>::value_type;
599
600 QObjectCompatProperty() { warnIfSignalWithArgumentAndCustomGetter(); }
601 explicit QObjectCompatProperty(const T &initialValue) : QPropertyData<T>(initialValue)
602 {
603 warnIfSignalWithArgumentAndCustomGetter();
604 }
605 explicit QObjectCompatProperty(T &&initialValue) : QPropertyData<T>(std::move(initialValue))
606 {
607 warnIfSignalWithArgumentAndCustomGetter();
608 }
609
611 {
612 const QBindingStorage *storage = qGetBindingStorage(owner());
613 // make sure we don't register this binding as a dependency to itself
614 if (storage->bindingStatus && storage->bindingStatus->currentlyEvaluatingBinding && !inBindingWrapper(storage))
615 storage->registerDependency_helper(this);
616 return this->val;
617 }
618
620 {
621 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
622 return value();
623 } else if constexpr (std::is_pointer_v<T>) {
624 value();
625 return this->val;
626 } else {
627 return;
628 }
629 }
630
632 {
633 return value();
634 }
635
637 {
638 return value();
639 }
640
642 {
643 QBindingStorage *storage = qGetBindingStorage(owner());
644 if (auto *bd = storage->bindingData(this)) {
645 // make sure we don't remove the binding if called from the bindingWrapper
646 if (bd->hasBinding() && !inBindingWrapper(storage))
647 bd->removeBinding_helper();
648 }
649 this->val = t;
650 }
651
653 {
654 setValue(newValue);
655 return *this;
656 }
657
659 {
660 QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
661 QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, nullptr, bindingWrapper));
662 // notification is already handled in QPropertyBindingData::setBinding
663 return static_cast<QPropertyBinding<T> &>(oldBinding);
664 }
665
666 bool setBinding(const QUntypedPropertyBinding &newBinding)
667 {
668 if (!newBinding.isNull() && newBinding.valueMetaType() != QMetaType::fromType<T>())
669 return false;
670 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
671 return true;
672 }
673
674#ifndef Q_QDOC
675 template <typename Functor>
677 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
678 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
679 {
680 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
681 }
682#else
683 template <typename Functor>
685#endif
686
687 bool hasBinding() const {
688 auto *bd = qGetBindingStorage(owner())->bindingData(this);
689 return bd && bd->binding() != nullptr;
690 }
691
693 {
694 QBindingStorage *storage = qGetBindingStorage(owner());
695 if (auto *bd = storage->bindingData(this)) {
696 // make sure we don't remove the binding if called from the bindingWrapper
697 if (bd->hasBinding() && !inBindingWrapper(storage))
698 bd->removeBinding_helper();
699 }
700 }
701
702 void notify()
703 {
704 QBindingStorage *storage = qGetBindingStorage(owner());
705 if (auto bd = storage->bindingData(this, false)) {
706 // This partly duplicates QPropertyBindingData::notifyObservers because we want to
707 // check for inBindingWrapper() after checking for isNotificationDelayed() and
708 // firstObserver. This is because inBindingWrapper() is the most expensive check.
709 if (!bd->isNotificationDelayed()) {
712 if (!inBindingWrapper(storage)) {
713 PendingBindingObserverList bindingObservers;
714 if (bd->notifyObserver_helper(this, storage, observer, bindingObservers)
715 == QtPrivate::QPropertyBindingData::Evaluated) {
716 // evaluateBindings() can trash the observers.
717 // It can also reallocate binding data pointer.
718 // So, we need to re-fetch here.
719 bd = storage->bindingData(this, false);
722 obs.notify(this);
723 for (auto&& bindingPtr: bindingObservers) {
724 auto *binding = static_cast<QPropertyBindingPrivate *>(bindingPtr.get());
725 binding->notifyNonRecursive();
726 }
727 }
728 }
729 }
730 }
731 }
732 if constexpr (!std::is_null_pointer_v<decltype(Signal)>) {
733 if constexpr (SignalTakesValue::value)
734 (owner()->*Signal)(getPropertyValueBypassingBindings(this));
735 else
736 (owner()->*Signal)();
737 }
738 }
739
741 {
742 auto *bd = qGetBindingStorage(owner())->bindingData(this);
743 QUntypedPropertyBinding binding = QPropertyBindingPrivate::makeUntyped(bd ? bd->binding() : nullptr);
744 return static_cast<QPropertyBinding<T> &&>(binding);
745 }
746
751
752 template<typename Functor>
754 {
755 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
756 return QPropertyChangeHandler<Functor>(*this, f);
757 }
758
759 template<typename Functor>
761 {
762 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
763 f();
764 return onValueChanged(f);
765 }
766
767 template<typename Functor>
769 {
770 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
771 return QPropertyNotifier(*this, f);
772 }
773
775 {
776 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
777 return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true);
778 }
779};
780
781namespace QtPrivate {
782template<typename Class, typename Ty, auto Offset, auto Setter, auto Signal, auto Getter>
785{
787 using T = typename Property::value_type;
788public:
789 static constexpr QBindableInterface iface = {
790 [](const QUntypedPropertyData *d, void *value) -> void
791 { *static_cast<T*>(value) = Property::getPropertyValue(d); },
792 [](QUntypedPropertyData *d, const void *value) -> void
793 {
794 (static_cast<Property *>(d)->owner()->*Setter)(*static_cast<const T*>(value));
795 },
797 { return static_cast<const Property *>(d)->binding(); },
799 { return static_cast<Property *>(d)->setBinding(static_cast<const QPropertyBinding<T> &>(binding)); },
801 { return Qt::makePropertyBinding([d]() -> T { return Property::getPropertyValue(d); }, location); },
803 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
804 []() { return QMetaType::fromType<T>(); }
805 };
806};
807}
808
809#define QT_OBJECT_COMPAT_PROPERTY_4(Class, Type, name, setter)
810 static constexpr size_t _qt_property_##name##_offset() {
811 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
812 return offsetof(Class, name);
813 QT_WARNING_POP
814 }
815 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name;
816
817#define QT_OBJECT_COMPAT_PROPERTY_5(Class, Type, name, setter, signal)
818 static constexpr size_t _qt_property_##name##_offset() {
819 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
820 return offsetof(Class, name);
821 QT_WARNING_POP
822 }
823 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name;
824
825#define Q_OBJECT_COMPAT_PROPERTY(...)
826 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
827 QT_OVERLOADED_MACRO(QT_OBJECT_COMPAT_PROPERTY, __VA_ARGS__)
828 QT_WARNING_POP
829
830#define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_5(Class, Type, name, setter, value)
831 static constexpr size_t _qt_property_##name##_offset() {
832 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
833 return offsetof(Class, name);
834 QT_WARNING_POP
835 }
836 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name =
837 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter>(
838 value);
839
840#define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_6(Class, Type, name, setter, signal, value)
841 static constexpr size_t _qt_property_##name##_offset() {
842 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
843 return offsetof(Class, name);
844 QT_WARNING_POP
845 }
846 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name =
847 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter,
848 signal>(value);
849
850#define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_7(Class, Type, name, setter, signal, getter, value)
851 static constexpr size_t _qt_property_##name##_offset() {
852 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
853 return offsetof(Class, name);
854 QT_WARNING_POP
855 }
856 QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal, getter>
857 name = QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter,
858 signal, getter>(value);
859
860#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(...)
861 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
862 QT_OVERLOADED_MACRO(QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS, __VA_ARGS__)
863 QT_WARNING_POP
864
865
866namespace QtPrivate {
867Q_CORE_EXPORT BindingEvaluationState *suspendCurrentBindingStatus();
869}
870
872{
874 {
875 return bindable.iface;
876 }
877
879 {
880 return bindable.data;
881 }
882};
883
884inline bool QPropertyBindingPrivate::evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
885{
886 if (updating) {
887 m_error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
888 if (isQQmlPropertyBinding)
889 errorCallBack(this);
890 return false;
891 }
892
893 /*
894 * Evaluating the binding might lead to the binding being broken. This can
895 * cause ref to reach zero at the end of the function. However, the
896 * updateGuard's destructor will then still trigger, trying to set the
897 * updating bool to its old value
898 * To prevent this, we create a QPropertyBindingPrivatePtr which ensures
899 * that the object is still alive when updateGuard's dtor runs.
900 */
901 QPropertyBindingPrivatePtr keepAlive {this};
902
903 QScopedValueRollback<bool> updateGuard(updating, true);
904
905 QtPrivate::BindingEvaluationState evaluationFrame(this, status);
906
907 auto bindingFunctor = reinterpret_cast<std::byte *>(this) +
908 QPropertyBindingPrivate::getSizeEnsuringAlignment();
909 bool changed = false;
910 if (hasBindingWrapper) {
911 changed = staticBindingWrapper(metaType, propertyDataPtr,
912 {vtable, bindingFunctor});
913 } else {
914 changed = vtable->call(metaType, propertyDataPtr, bindingFunctor);
915 }
916 // If there was a change, we must set pendingNotify.
917 // If there was not, we must not clear it, as that only should happen in notifyRecursive
918 pendingNotify = pendingNotify || changed;
919 if (!changed || !firstObserver)
920 return changed;
921
922 firstObserver.noSelfDependencies(this);
923 firstObserver.evaluateBindings(bindingObservers, status);
924 return true;
925}
926
927/*!
928 \internal
929
930 Walks through the list of property observers, and calls any ChangeHandler
931 found there.
932 It doesn't do anything with bindings, which are only handled in
933 QPropertyBindingPrivate::evaluateRecursive.
934 */
935inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
936{
937 auto observer = const_cast<QPropertyObserver*>(ptr);
938 /*
939 * The basic idea of the loop is as follows: We iterate over all observers in the linked list,
940 * and execute the functionality corresponding to their tag.
941 * However, complication arise due to the fact that the triggered operations might modify the list,
942 * which includes deletion and move of the current and next nodes.
943 * Therefore, we take a few safety precautions:
944 * 1. Before executing any action which might modify the list, we insert a placeholder node after the current node.
945 * As that one is stack allocated and owned by us, we can rest assured that it is
946 * still there after the action has executed, and placeHolder->next points to the actual next node in the list.
947 * Note that taking next at the beginning of the loop does not work, as the executed action might either move
948 * or delete that node.
949 * 2. After the triggered action has finished, we can use the next pointer in the placeholder node as a safe way to
950 * retrieve the next node.
951 * 3. Some care needs to be taken to avoid infinite recursion with change handlers, so we add an extra test there, that
952 * checks whether we're already have the same change handler in our call stack. This can be done by checking whether
953 * the node after the current one is a placeholder node.
954 */
955 while (observer) {
956 QPropertyObserver *next = observer->next.data();
957 switch (QPropertyObserver::ObserverTag(observer->next.tag())) {
958 case QPropertyObserver::ObserverNotifiesChangeHandler:
959 {
960 auto handlerToCall = observer->changeHandler;
961 // prevent recursion
962 if (next && next->next.tag() == QPropertyObserver::ObserverIsPlaceholder) {
963 observer = next->next.data();
964 continue;
965 }
966 // handlerToCall might modify the list
967 QPropertyObserverNodeProtector protector(observer);
968 handlerToCall(observer, propertyDataPtr);
969 next = protector.next();
970 break;
971 }
972 case QPropertyObserver::ObserverNotifiesBinding:
973 break;
974 case QPropertyObserver::ObserverIsPlaceholder:
975 // recursion is already properly handled somewhere else
976 break;
977#if QT_DEPRECATED_SINCE(6, 6)
978 QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
979 case QPropertyObserver::ObserverIsAlias:
980 break;
981 QT_WARNING_POP
982#endif
983 default: Q_UNREACHABLE();
984 }
985 observer = next;
986 }
987}
988
990{
991 QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)};
993}
994
995QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) noexcept : d(observer)
996{
997 Q_ASSERT(d);
998 QPropertyObserverPointer{d}.binding()->addRef();
999}
1000
1002{
1003 if (!d)
1004 return;
1005
1006 QPropertyBindingPrivate *bindingPrivate = binding();
1007 if (!bindingPrivate->deref())
1008 QPropertyBindingPrivate::destroyAndFreeMemory(bindingPrivate);
1009}
1010
1012
1013QPropertyObserver *QBindingObserverPtr::operator->() { return d; }
1014
1015namespace QtPrivate {
1017{
1018 QPropertyBindingData bindingData_;
1019 QObject *obj;
1020 QMetaProperty metaProperty_;
1021
1022#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1023 static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret);
1024#else
1025 static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret);
1026#endif
1027
1028 QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty& p);
1029
1030public:
1031 static QPropertyAdaptorSlotObject *cast(QSlotObjectBase *ptr, int propertyIndex)
1032 {
1033 if (ptr->isImpl(&QPropertyAdaptorSlotObject::impl)) {
1034 auto p = static_cast<QPropertyAdaptorSlotObject *>(ptr);
1035 if (p->metaProperty_.propertyIndex() == propertyIndex)
1036 return p;
1037 }
1038 return nullptr;
1039 }
1040
1041 inline const QPropertyBindingData &bindingData() const { return bindingData_; }
1042 inline QPropertyBindingData &bindingData() { return bindingData_; }
1043 inline QObject *object() const { return obj; }
1044 inline const QMetaProperty &metaProperty() const { return metaProperty_; }
1045
1047};
1048}
1049
1050QT_END_NAMESPACE
1051
1052#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