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