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