QtBase  v6.3.1
qproperty_p.h
Go to the documentation of this file.
1 /***************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #ifndef QPROPERTY_P_H
41 #define QPROPERTY_P_H
42 
43 //
44 // W A R N I N G
45 // -------------
46 //
47 // This file is not part of the Qt API. It exists for the convenience
48 // of a number of Qt sources files. This header file may change from
49 // version to version without notice, or even be removed.
50 //
51 // We mean it.
52 //
53 
54 #include <qglobal.h>
55 #include <qproperty.h>
56 
57 #include <qscopedpointer.h>
58 #include <qscopedvaluerollback.h>
59 #include <vector>
60 
62 
63 namespace QtPrivate {
64  Q_CORE_EXPORT bool isAnyBindingEvaluating();
65 }
66 
67 // Keep all classes related to QProperty in one compilation unit. Performance of this code is crucial and
68 // we need to allow the compiler to inline where it makes sense.
69 
70 // This is a helper "namespace"
72 {
74 
76  {
77  return ptr->binding();
78  }
79 
81  {
82  auto &d = ptr->d_ref();
83  observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
84  d = reinterpret_cast<quintptr>(observer);
85  }
87  void Q_ALWAYS_INLINE addObserver(QPropertyObserver *observer);
88  inline void setFirstObserver(QPropertyObserver *observer);
90 
91  inline int observerCount() const;
92 
93  template <typename T>
95  {
96  return QPropertyBindingDataPointer{&property.bindingData()};
97  }
98 };
99 
100 struct [[nodiscard]] QPropertyObserverNodeProtector
101 {
103 
104  QPropertyObserverBase m_placeHolder;
106  {
107  // insert m_placeholder after observer into the linked list
108  QPropertyObserver *next = observer->next.data();
109  m_placeHolder.next = next;
110  observer->next = static_cast<QPropertyObserver *>(&m_placeHolder);
111  if (next)
112  next->prev = &m_placeHolder.next;
113  m_placeHolder.prev = &observer->next;
115  }
116 
117  QPropertyObserver *next() const { return m_placeHolder.next.data(); }
118 
120 };
121 
122 // This is a helper "namespace"
124 {
125  QPropertyObserver *ptr = nullptr;
126 
127  void unlink()
128  {
129  unlink_common();
130  if (ptr->next.tag() == QPropertyObserver::ObserverIsAlias)
131  ptr->aliasData = nullptr;
132  }
133 
134  void unlink_fast()
135  {
137  unlink_common();
138  }
139 
143 
144  void notify(QUntypedPropertyData *propertyDataPtr);
145 #ifndef QT_NO_DEBUG
147 #else
149 #endif
150  void evaluateBindings(QBindingStatus *status);
152 
153  explicit operator bool() const { return ptr != nullptr; }
154 
155  QPropertyObserverPointer nextObserver() const { return {ptr->next.data()}; }
156 
157 private:
158  void unlink_common()
159  {
160  if (ptr->next)
161  ptr->next->prev = ptr->prev;
162  if (ptr->prev)
163  ptr->prev.setPointer(ptr->next.data());
164  ptr->next = nullptr;
165  ptr->prev.clear();
166  }
167 };
168 
170 {
171 public:
174 };
175 
176 namespace QtPrivate {
177 
179 {
182  {
184  }
185 
189 };
190 
199 {
202  {
205  }
211 };
212 
213 }
214 
215 class Q_CORE_EXPORT QPropertyBindingPrivate : public QtPrivate::RefCounted
216 {
217 private:
220 
221  using ObserverArray = std::array<QPropertyObserver, 4>;
222 
223 private:
224 
225  // used to detect binding loops for lazy evaluated properties
226  bool updating = false;
227  bool hasStaticObserver = false;
228  bool pendingNotify = false;
229  bool hasBindingWrapper:1;
230  // used to detect binding loops for eagerly evaluated properties
231  bool isQQmlPropertyBinding:1;
232  /* a sticky binding does not get removed in removeBinding
233  this is used to support QQmlPropertyData::DontRemoveBinding
234  in qtdeclarative
235  */
236  bool m_sticky:1;
237 
238  const QtPrivate::BindingFunctionVTable *vtable;
239 
240  union {
241  QtPrivate::QPropertyObserverCallback staticObserverCallback = nullptr;
243  };
244  ObserverArray inlineDependencyObservers; // for things we are observing
245 
246  QPropertyObserverPointer firstObserver; // list of observers observing us
247  QScopedPointer<std::vector<QPropertyObserver>> heapObservers; // for things we are observing
248 
249 protected:
250  QUntypedPropertyData *propertyDataPtr = nullptr;
251 
252  /* For bindings set up from C++, location stores where the binding was created in the C++ source
253  For QQmlPropertyBinding that information does not make sense, and the location in the QML file
254  is stored somewhere else. To make efficient use of the space, we instead provide a scratch space
255  for QQmlPropertyBinding (which stores further binding information there).
256  Anything stored in the union must be trivially destructible.
257  (checked in qproperty.cpp)
258  */
260  union {
262  struct {
263  std::byte declarativeExtraData[sizeof(QPropertyBindingSourceLocation) - sizeof(DeclarativeErrorCallback)];
265  };
266  };
267 private:
269 
270  QMetaType metaType;
271 
272 public:
273  static constexpr size_t getSizeEnsuringAlignment() {
274  constexpr auto align = alignof (std::max_align_t) - 1;
275  constexpr size_t sizeEnsuringAlignment = (sizeof(QPropertyBindingPrivate) + align) & ~align;
276  static_assert (sizeEnsuringAlignment % alignof (std::max_align_t) == 0,
277  "Required for placement new'ing the function behind it.");
278  return sizeEnsuringAlignment;
279  }
280 
281 
282  // public because the auto-tests access it, too.
283  size_t dependencyObserverCount = 0;
284 
285  bool isUpdating() {return updating;}
286  void setSticky(bool keep = true) {m_sticky = keep;}
287  bool isSticky() {return m_sticky;}
288  void scheduleNotify() {pendingNotify = true;}
289 
291  const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
292  : hasBindingWrapper(false)
293  , isQQmlPropertyBinding(isQQmlPropertyBinding)
294  , m_sticky(false)
295  , vtable(vtable)
296  , location(location)
297  , metaType(metaType)
298  {}
300 
301 
302  void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; }
304  {
305  Q_ASSERT(!(callback && bindingWrapper));
306  if (callback) {
307  hasStaticObserver = true;
308  hasBindingWrapper = false;
309  staticObserverCallback = callback;
310  } else if (bindingWrapper) {
311  hasStaticObserver = false;
312  hasBindingWrapper = true;
313  staticBindingWrapper = bindingWrapper;
314  } else {
315  hasStaticObserver = false;
316  hasBindingWrapper = false;
317  staticObserverCallback = nullptr;
318  }
319  }
321  {
322  observer.ptr->prev = const_cast<QPropertyObserver **>(&firstObserver.ptr);
323  firstObserver = observer;
324  }
325 
327  {
328  auto observers = firstObserver;
329  firstObserver.ptr = nullptr;
330  return observers;
331  }
332 
334  for (size_t i = 0; i < qMin(dependencyObserverCount, inlineDependencyObservers.size()); ++i) {
335  QPropertyObserverPointer p{&inlineDependencyObservers[i]};
336  p.unlink_fast();
337  }
338  if (heapObservers)
339  heapObservers->clear();
340  dependencyObserverCount = 0;
341  }
342 
344  if (dependencyObserverCount < inlineDependencyObservers.size()) {
345  ++dependencyObserverCount;
346  return {&inlineDependencyObservers[dependencyObserverCount - 1]};
347  }
348  return allocateDependencyObserver_slow();
349  }
350 
352  {
353  ++dependencyObserverCount;
354  if (!heapObservers)
355  heapObservers.reset(new std::vector<QPropertyObserver>());
356  return {&heapObservers->emplace_back()};
357  }
358 
360  {
361  if (!hasCustomVTable())
362  return this->location;
364  constexpr auto msg = "Custom location";
365  location.fileName = msg;
366  return location;
367  }
369  QMetaType valueMetaType() const { return metaType; }
370 
371  void unlinkAndDeref();
372 
373  void evaluateRecursive(QBindingStatus *status = nullptr);
374  void Q_ALWAYS_INLINE evaluateRecursive_inline(QBindingStatus *status);
375 
376  void notifyRecursive();
377 
379  { return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
380 
382  { error = std::move(e); }
383 
385  {
386  hasStaticObserver = false;
387  hasBindingWrapper = false;
388  propertyDataPtr = nullptr;
389  clearDependencyObservers();
390  }
391 
392  static QPropertyBindingPrivate *currentlyEvaluatingBinding();
393 
394  bool hasCustomVTable() const
395  {
396  return vtable->size == 0;
397  }
398 
400  if (priv->hasCustomVTable()) {
401  // special hack for QQmlPropertyBinding which has a
402  // different memory layout than normal QPropertyBindings
403  priv->vtable->destroy(priv);
404  } else{
405  priv->~QPropertyBindingPrivate();
406  delete[] reinterpret_cast<std::byte *>(priv);
407  }
408  }
409 };
410 
412 {
413  if (auto *b = binding()) {
414  b->firstObserver.ptr = observer;
415  return;
416  }
417  auto &d = ptr->d_ref();
418  d = reinterpret_cast<quintptr>(observer);
419 }
420 
422 {
423  auto &d = ptr->d_ref();
424  if (ptr->isNotificationDelayed()) {
425  QPropertyProxyBindingData *proxyData
426  = reinterpret_cast<QPropertyProxyBindingData*>(d & ~QtPrivate::QPropertyBindingData::BindingBit);
427  proxyData->originalBindingData = ptr;
428  }
429  // If QPropertyBindingData has been moved, and it has an observer
430  // we have to adjust the firstObserver's prev pointer to point to
431  // the moved to QPropertyBindingData's d_ptr
433  return; // nothing to do if the observer is stored in the binding
434  if (auto observer = reinterpret_cast<QPropertyObserver *>(d))
435  observer->prev = reinterpret_cast<QPropertyObserver **>(&d);
436 }
437 
439 {
440  if (auto *b = binding())
441  return b->firstObserver;
442  return { reinterpret_cast<QPropertyObserver *>(ptr->d()) };
443 }
444 
446 {
447  int count = 0;
448  for (auto observer = firstObserver(); observer; observer = observer.nextObserver())
449  ++count;
450  return count;
451 }
452 
453 namespace QtPrivate {
454  Q_CORE_EXPORT bool isPropertyInBindingWrapper(const QUntypedPropertyData *property);
455  void Q_CORE_EXPORT initBindingStatusThreadId();
456 }
457 
458 template<typename Class, typename T, auto Offset, auto Setter, auto Signal = nullptr,
459  auto Getter = nullptr>
461 {
462  template<typename Property, typename>
464 
466  using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
467  Class *owner()
468  {
469  char *that = reinterpret_cast<char *>(this);
470  return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
471  }
472  const Class *owner() const
473  {
474  char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
475  return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
476  }
477 
478  static bool bindingWrapper(QMetaType type, QUntypedPropertyData *dataPtr, QtPrivate::QPropertyBindingFunction binding)
479  {
480  auto *thisData = static_cast<ThisType *>(dataPtr);
481  QPropertyData<T> copy;
482  binding.vtable->call(type, &copy, binding.functor);
483  if constexpr (QTypeTraits::has_operator_equal_v<T>)
484  if (copy.valueBypassingBindings() == thisData->valueBypassingBindings())
485  return false;
486  // ensure value and setValue know we're currently evaluating our binding
487  QBindingStorage *storage = qGetBindingStorage(thisData->owner());
488  QtPrivate::CompatPropertySafePoint guardThis(storage->bindingStatus, thisData);
489  (thisData->owner()->*Setter)(copy.valueBypassingBindings());
490  return true;
491  }
492  bool inBindingWrapper(const QBindingStorage *storage) const
493  {
494  return storage->bindingStatus->currentCompatProperty
496  }
497 
498  inline static T getPropertyValue(const QUntypedPropertyData *d) {
499  auto prop = static_cast<const ThisType *>(d);
500  if constexpr (std::is_null_pointer_v<decltype(Getter)>)
501  return prop->value();
502  else
503  return (prop->owner()->*Getter)();
504  }
505 
506 public:
507  using value_type = typename QPropertyData<T>::value_type;
510 
512  explicit QObjectCompatProperty(const T &initialValue) : QPropertyData<T>(initialValue) {}
513  explicit QObjectCompatProperty(T &&initialValue) : QPropertyData<T>(std::move(initialValue)) {}
514 
516  {
517  const QBindingStorage *storage = qGetBindingStorage(owner());
518  // make sure we don't register this binding as a dependency to itself
519  if (storage->bindingStatus->currentlyEvaluatingBinding && !inBindingWrapper(storage))
520  storage->registerDependency_helper(this);
521  return this->val;
522  }
523 
525  {
526  if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
527  return value();
528  } else if constexpr (std::is_pointer_v<T>) {
529  value();
530  return this->val;
531  } else {
532  return;
533  }
534  }
535 
537  {
538  return value();
539  }
540 
541  operator parameter_type() const
542  {
543  return value();
544  }
545 
547  {
549  if (auto *bd = storage->bindingData(this)) {
550  // make sure we don't remove the binding if called from the bindingWrapper
551  if (bd->hasBinding() && !inBindingWrapper(storage))
552  bd->removeBinding_helper();
553  }
554  this->val = t;
555  }
556 
558  {
559  setValue(newValue);
560  return *this;
561  }
562 
564  {
566  QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, nullptr, bindingWrapper));
567  // notification is already handled in QPropertyBindingData::setBinding
568  return static_cast<QPropertyBinding<T> &>(oldBinding);
569  }
570 
571  bool setBinding(const QUntypedPropertyBinding &newBinding)
572  {
573  if (!newBinding.isNull() && newBinding.valueMetaType() != QMetaType::fromType<T>())
574  return false;
575  setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
576  return true;
577  }
578 
579 #ifndef Q_CLANG_QDOC
580  template <typename Functor>
583  std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
584  {
585  return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
586  }
587 #else
588  template <typename Functor>
590 #endif
591 
592  bool hasBinding() const {
593  auto *bd = qGetBindingStorage(owner())->bindingData(this);
594  return bd && bd->binding() != nullptr;
595  }
596 
598  {
600  if (auto *bd = storage->bindingData(this)) {
601  // make sure we don't remove the binding if called from the bindingWrapper
602  if (bd->hasBinding() && !inBindingWrapper(storage))
603  bd->removeBinding_helper();
604  }
605  }
606 
607  void notify()
608  {
610  if (auto bd = storage->bindingData(this, false)) {
611  // This partly duplicates QPropertyBindingData::notifyObservers because we want to
612  // check for inBindingWrapper() after checking for isNotificationDelayed() and
613  // firstObserver. This is because inBindingWrapper() is the most expensive check.
614  if (!bd->isNotificationDelayed()) {
616  if (QPropertyObserverPointer observer = d.firstObserver()) {
617  if (!inBindingWrapper(storage)) {
618  if (bd->notifyObserver_helper(this, observer, storage)
619  == QtPrivate::QPropertyBindingData::Evaluated) {
620  // evaluateBindings() can trash the observers. We need to re-fetch here.
621  if (QPropertyObserverPointer observer = d.firstObserver())
622  observer.notify(this);
623  }
624  }
625  }
626  }
627  }
628  if constexpr (!std::is_null_pointer_v<decltype(Signal)>) {
629  if constexpr (SignalTakesValue::value)
630  (owner()->*Signal)(getPropertyValue(this));
631  else
632  (owner()->*Signal)();
633  }
634  }
635 
637  {
638  auto *bd = qGetBindingStorage(owner())->bindingData(this);
639  return static_cast<QPropertyBinding<T> &&>(QUntypedPropertyBinding(bd ? bd->binding() : nullptr));
640  }
641 
643  {
645  }
646 
647  template<typename Functor>
649  {
650  static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
651  return QPropertyChangeHandler<Functor>(*this, f);
652  }
653 
654  template<typename Functor>
656  {
657  static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
658  f();
659  return onValueChanged(f);
660  }
661 
662  template<typename Functor>
664  {
665  static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
666  return QPropertyNotifier(*this, f);
667  }
668 
670  {
671  auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
672  return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true);
673  }
674 };
675 
676 namespace QtPrivate {
677 template<typename Class, typename Ty, auto Offset, auto Setter, auto Signal, auto Getter>
679  QObjectCompatProperty<Class, Ty, Offset, Setter, Signal, Getter>, std::void_t<Class>>
680 {
682  using T = typename Property::value_type;
683 public:
684  static constexpr QBindableInterface iface = {
685  [](const QUntypedPropertyData *d, void *value) -> void
686  { *static_cast<T*>(value) = Property::getPropertyValue(d); },
687  [](QUntypedPropertyData *d, const void *value) -> void
688  {
689  (static_cast<Property *>(d)->owner()->*Setter)(*static_cast<const T*>(value));
690  },
692  { return static_cast<const Property *>(d)->binding(); },
694  { return static_cast<Property *>(d)->setBinding(static_cast<const QPropertyBinding<T> &>(binding)); },
696  { return Qt::makePropertyBinding([d]() -> T { return Property::getPropertyValue(d); }, location); },
697  [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
698  { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
699  []() { return QMetaType::fromType<T>(); }
700  };
701 };
702 }
703 
704 #define QT_OBJECT_COMPAT_PROPERTY_4(Class, Type, name, setter) \
705  static constexpr size_t _qt_property_##name##_offset() { \
706  QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
707  return offsetof(Class, name); \
708  QT_WARNING_POP \
709  } \
710  QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name;
711 
712 #define QT_OBJECT_COMPAT_PROPERTY_5(Class, Type, name, setter, signal) \
713  static constexpr size_t _qt_property_##name##_offset() { \
714  QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
715  return offsetof(Class, name); \
716  QT_WARNING_POP \
717  } \
718  QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name;
719 
720 #define Q_OBJECT_COMPAT_PROPERTY(...) \
721  QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
722  QT_OVERLOADED_MACRO(QT_OBJECT_COMPAT_PROPERTY, __VA_ARGS__) \
723  QT_WARNING_POP
724 
725 #define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_5(Class, Type, name, setter, value) \
726  static constexpr size_t _qt_property_##name##_offset() { \
727  QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
728  return offsetof(Class, name); \
729  QT_WARNING_POP \
730  } \
731  QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name = \
732  QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter>( \
733  value);
734 
735 #define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_6(Class, Type, name, setter, signal, value) \
736  static constexpr size_t _qt_property_##name##_offset() { \
737  QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
738  return offsetof(Class, name); \
739  QT_WARNING_POP \
740  } \
741  QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name = \
742  QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, \
743  signal>(value);
744 
745 #define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_7(Class, Type, name, setter, signal, getter, value) \
746  static constexpr size_t _qt_property_##name##_offset() { \
747  QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
748  return offsetof(Class, name); \
749  QT_WARNING_POP \
750  } \
751  QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal, getter>\
752  name = QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, \
753  signal, getter>(value);
754 
755 #define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(...) \
756  QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
757  QT_OVERLOADED_MACRO(QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS, __VA_ARGS__) \
758  QT_WARNING_POP
759 
760 
761 namespace QtPrivate {
762 Q_CORE_EXPORT BindingEvaluationState *suspendCurrentBindingStatus();
763 Q_CORE_EXPORT void restoreBindingStatus(BindingEvaluationState *status);
764 }
765 
767 {
769  {
770  return bindable.iface;
771  }
772 
774  {
775  return bindable.data;
776  }
777 };
778 
780 {
781  if (updating) {
783  if (isQQmlPropertyBinding)
784  errorCallBack(this);
785  return;
786  }
787 
788  /*
789  * Evaluating the binding might lead to the binding being broken. This can
790  * cause ref to reach zero at the end of the function. However, the
791  * updateGuard's destructor will then still trigger, trying to set the
792  * updating bool to its old value
793  * To prevent this, we create a QPropertyBindingPrivatePtr which ensures
794  * that the object is still alive when updateGuard's dtor runs.
795  */
796  QPropertyBindingPrivatePtr keepAlive {this};
797 
798  QScopedValueRollback<bool> updateGuard(updating, true);
799 
800  QtPrivate::BindingEvaluationState evaluationFrame(this, status);
801 
802  auto bindingFunctor = reinterpret_cast<std::byte *>(this) +
804  bool changed = false;
805  if (hasBindingWrapper) {
806  changed = staticBindingWrapper(metaType, propertyDataPtr,
807  {vtable, bindingFunctor});
808  } else {
809  changed = vtable->call(metaType, propertyDataPtr, bindingFunctor);
810  }
811  // If there was a change, we must set pendingNotify.
812  // If there was not, we must not clear it, as that only should happen in notifyRecursive
813  pendingNotify = pendingNotify || changed;
814  if (!changed || !firstObserver)
815  return;
816 
817  firstObserver.noSelfDependencies(this);
818  firstObserver.evaluateBindings(status);
819 }
820 
822 {
823  auto observer = const_cast<QPropertyObserver*>(ptr);
824  /*
825  * The basic idea of the loop is as follows: We iterate over all observers in the linked list,
826  * and execute the functionality corresponding to their tag.
827  * However, complication arise due to the fact that the triggered operations might modify the list,
828  * which includes deletion and move of the current and next nodes.
829  * Therefore, we take a few safety precautions:
830  * 1. Before executing any action which might modify the list, we insert a placeholder node after the current node.
831  * As that one is stack allocated and owned by us, we can rest assured that it is
832  * still there after the action has executed, and placeHolder->next points to the actual next node in the list.
833  * Note that taking next at the beginning of the loop does not work, as the executed action might either move
834  * or delete that node.
835  * 2. After the triggered action has finished, we can use the next pointer in the placeholder node as a safe way to
836  * retrieve the next node.
837  * 3. Some care needs to be taken to avoid infinite recursion with change handlers, so we add an extra test there, that
838  * checks whether we're already have the same change handler in our call stack. This can be done by checking whether
839  * the node after the current one is a placeholder node.
840  */
841  while (observer) {
842  QPropertyObserver *next = observer->next.data();
843  switch (QPropertyObserver::ObserverTag(observer->next.tag())) {
845  {
846  auto handlerToCall = observer->changeHandler;
847  // prevent recursion
848  if (next && next->next.tag() == QPropertyObserver::ObserverIsPlaceholder) {
849  observer = next->next.data();
850  continue;
851  }
852  // handlerToCall might modify the list
853  QPropertyObserverNodeProtector protector(observer);
854  handlerToCall(observer, propertyDataPtr);
855  next = protector.next();
856  break;
857  }
859  {
860  auto bindingToNotify = observer->binding;
861  QPropertyObserverNodeProtector protector(observer);
862  bindingToNotify->notifyRecursive();
863  next = protector.next();
864  break;
865  }
867  // recursion is already properly handled somewhere else
868  break;
870  break;
871  default: Q_UNREACHABLE();
872  }
873  observer = next;
874  }
875 }
876 
878 {
879  QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)};
880  d.unlink_fast();
881 }
882 
884 
885 #endif // QPROPERTY_P_H
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
const char msg[]
Definition: arch.cpp:46
#define value
[5]
FT_Error error
Definition: cffdrivr.c:657
QtPrivate::QPropertyBindingData * bindingData(const QUntypedPropertyData *data) const
The QMetaType class manages named types in the meta-object system.
Definition: qmetatype.h:328
Declares a \l QObjectBindableProperty inside containingClass of type type with name name....
Definition: qproperty_p.h:461
QPropertyBinding< T > takeBinding()
Definition: qproperty_p.h:642
QPropertyBinding< T > setBinding(Functor &&f, const QPropertyBindingSourceLocation &location=QT_PROPERTY_DEFAULT_BINDING_LOCATION, std::enable_if_t< std::is_invocable_v< Functor >> *=nullptr)
Definition: qproperty_p.h:581
QPropertyBinding< T > binding() const
Definition: qproperty_p.h:636
QPropertyChangeHandler< Functor > subscribe(Functor f)
Definition: qproperty_p.h:655
typename QPropertyData< T >::parameter_type parameter_type
Definition: qproperty_p.h:508
parameter_type value() const
Definition: qproperty_p.h:515
QObjectCompatProperty & operator=(parameter_type newValue)
Definition: qproperty_p.h:557
void setValue(parameter_type t)
Definition: qproperty_p.h:546
void removeBindingUnlessInWrapper()
Definition: qproperty_p.h:597
bool setBinding(const QUntypedPropertyBinding &newBinding)
Definition: qproperty_p.h:571
QtPrivate::QPropertyBindingData & bindingData() const
Definition: qproperty_p.h:669
arrow_operator_result operator->() const
Definition: qproperty_p.h:524
QObjectCompatProperty(T &&initialValue)
Definition: qproperty_p.h:513
QPropertyNotifier addNotifier(Functor f)
Definition: qproperty_p.h:663
parameter_type operator*() const
Definition: qproperty_p.h:536
QPropertyChangeHandler< Functor > onValueChanged(Functor f)
Definition: qproperty_p.h:648
QPropertyBinding< T > setBinding(const QPropertyBinding< T > &newBinding)
Definition: qproperty_p.h:563
bool hasBinding() const
Definition: qproperty_p.h:592
QPropertyBindingError bindingError() const
Definition: qproperty_p.h:368
QMetaType valueMetaType() const
Definition: qproperty_p.h:369
Q_NEVER_INLINE QPropertyObserverPointer allocateDependencyObserver_slow()
Definition: qproperty_p.h:351
void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper)
Definition: qproperty_p.h:303
QPropertyBindingSourceLocation sourceLocation() const
Definition: qproperty_p.h:359
QPropertyBindingSourceLocation location
Definition: qproperty_p.h:261
QPropertyObserverPointer takeObservers()
Definition: qproperty_p.h:326
void setSticky(bool keep=true)
Definition: qproperty_p.h:286
static constexpr size_t getSizeEnsuringAlignment()
Definition: qproperty_p.h:273
void setProperty(QUntypedPropertyData *propertyPtr)
Definition: qproperty_p.h:302
QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable, const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
Definition: qproperty_p.h:290
static void destroyAndFreeMemory(QPropertyBindingPrivate *priv)
Definition: qproperty_p.h:399
Q_ALWAYS_INLINE QPropertyObserverPointer allocateDependencyObserver()
Definition: qproperty_p.h:343
bool hasCustomVTable() const
Definition: qproperty_p.h:394
void setError(QPropertyBindingError &&e)
Definition: qproperty_p.h:381
void(*)(QPropertyBindingPrivate *) DeclarativeErrorCallback
Definition: qproperty_p.h:259
void Q_ALWAYS_INLINE evaluateRecursive_inline(QBindingStatus *status)
Definition: qproperty_p.h:779
DeclarativeErrorCallback errorCallBack
Definition: qproperty_p.h:264
QtPrivate::QPropertyBindingWrapper staticBindingWrapper
Definition: qproperty_p.h:242
static QPropertyBindingPrivate * get(const QUntypedPropertyBinding &binding)
Definition: qproperty_p.h:378
void prependObserver(QPropertyObserverPointer observer)
Definition: qproperty_p.h:320
T * data() const noexcept
The QPropertyChangeHandler class controls the lifecycle of change callback installed on a QProperty.
Definition: qproperty.h:297
The QPropertyData class is a helper class for properties with automatic property bindings.
Definition: qproperty.h:85
std::conditional_t< UseReferences, const T &, T > parameter_type
Definition: qproperty.h:94
std::conditional_t< std::is_pointer_v< T >, const T &, std::conditional_t< QTypeTraits::is_dereferenceable_v< T >, const T &, void > > arrow_operator_result
Definition: qproperty.h:97
parameter_type valueBypassingBindings() const
Definition: qproperty.h:104
The QProperty class is a template class that enables automatic property bindings.
Definition: qproperty.h:350
The QPropertyNotifier class controls the lifecycle of change callback installed on a QProperty.
Definition: qproperty.h:322
QUntypedPropertyData * aliasData
Definition: qproperty.h:262
void(*)(QPropertyObserver *, QUntypedPropertyData *) ChangeHandler
Definition: qproperty.h:244
The QScopedPointer class stores a pointer to a dynamically allocated object, and deletes it upon dest...
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
The QScopedValueRollback class resets a variable to its previous value on destruction.
The QSharedData class is a base class for shared data objects. \reentrant.
Definition: qshareddata.h:55
The QString class provides a Unicode character string.
Definition: qstring.h:388
void setTag(Tag tag)
QUntypedBindable is a uniform interface over bindable properties like QProperty<T> and QObjectBindabl...
Definition: qproperty.h:624
const QtPrivate::QBindableInterface * iface
Definition: qproperty.h:628
QUntypedPropertyData * data
Definition: qproperty.h:627
QMetaType valueMetaType() const
Definition: qproperty.cpp:426
QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding, QUntypedPropertyData *propertyDataPtr, QPropertyObserverCallback staticObserverCallback=nullptr, QPropertyBindingWrapper bindingWrapper=nullptr)
Definition: qproperty.cpp:445
QPropertyBindingPrivate * binding() const
static constexpr quintptr BindingBit
#define T(x)
Definition: main.cpp:42
double e
else
Definition: ftgrays.c:1658
auto it unsigned count const
Definition: hb-iter.hh:848
short next
Definition: keywords.cpp:454
typename C::value_type value_type
auto makePropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location=QT_PROPERTY_DEFAULT_BINDING_LOCATION, std::enable_if_t< std::is_invocable_v< Functor >> *=nullptr)
Definition: qproperty.h:222
constexpr size_t getOffset(size_t o)
bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction) QPropertyBindingWrapper
bool isAnyBindingEvaluating()
Definition: qproperty.cpp:2274
void initBindingStatusThreadId()
Definition: qproperty.cpp:2249
BindingEvaluationState * suspendCurrentBindingStatus()
Definition: qproperty.cpp:2254
bool isPropertyInBindingWrapper(const QUntypedPropertyData *property)
Definition: qproperty.cpp:2279
void(*)(QUntypedPropertyData *) QPropertyObserverCallback
void restoreBindingStatus(BindingEvaluationState *status)
Definition: qproperty.cpp:2261
default
Definition: devices.py:76
Definition: qfloat16.h:381
void
Definition: png.h:1080
#define Q_UNREACHABLE()
EGLOutputLayerEXT EGLint EGLAttrib value
quint16 Offset
#define Q_DISABLE_COPY_MOVE(Class)
Definition: qglobal.h:519
size_t quintptr
Definition: qglobal.h:310
const QBindingStorage * qGetBindingStorage(const QObject *o)
Definition: qobject.h:513
GLenum type
Definition: qopengl.h:270
GLint location
GLboolean GLboolean GLboolean b
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLuint GLfloat * val
Definition: qopenglext.h:1513
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
#define QT_PROPERTY_DEFAULT_BINDING_LOCATION
Definition: qproperty.h:73
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
const char property[13]
Definition: qwizard.cpp:136
#define explicit
QStorageInfo storage
[1]
QPropertyBindingPrivate * binding() const
Definition: qproperty_p.h:75
static void fixupAfterMove(QtPrivate::QPropertyBindingData *ptr)
Definition: qproperty_p.h:421
void Q_ALWAYS_INLINE addObserver(QPropertyObserver *observer)
Definition: qproperty.cpp:71
const QtPrivate::QPropertyBindingData * ptr
Definition: qproperty_p.h:73
static QPropertyBindingDataPointer get(QProperty< T > &property)
Definition: qproperty_p.h:94
void setObservers(QPropertyObserver *observer)
Definition: qproperty_p.h:80
QPropertyObserverPointer firstObserver() const
Definition: qproperty_p.h:438
void setFirstObserver(QPropertyObserver *observer)
Definition: qproperty_p.h:411
QPropertyObserver * next() const
Definition: qproperty_p.h:117
void noSelfDependencies(QPropertyBindingPrivate *binding)
Definition: qproperty.cpp:728
void notify(QUntypedPropertyData *propertyDataPtr)
Definition: qproperty_p.h:821
void observeProperty(QPropertyBindingDataPointer property)
Definition: qproperty.cpp:764
QPropertyObserver * ptr
Definition: qproperty_p.h:125
QPropertyObserverPointer nextObserver() const
Definition: qproperty_p.h:155
void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding)
Definition: qproperty.cpp:708
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler)
Definition: qproperty.cpp:690
void setBindingToNotify(QPropertyBindingPrivate *binding)
Definition: qproperty.cpp:697
void evaluateBindings(QBindingStatus *status)
Definition: qproperty.cpp:745
const QtPrivate::QPropertyBindingData * originalBindingData
static QtPrivate::QBindableInterface const * getInterface(const QUntypedBindable &bindable)
Definition: qproperty_p.h:768
static QUntypedPropertyData * getPropertyData(const QUntypedBindable &bindable)
Definition: qproperty_p.h:773
QPropertyBindingPrivate * binding
Definition: qproperty_p.h:186
BindingEvaluationState(QPropertyBindingPrivate *binding, QBindingStatus *status)
Definition: qproperty.cpp:501
BindingEvaluationState * previousState
Definition: qproperty_p.h:187
BindingEvaluationState ** currentState
Definition: qproperty_p.h:188
QtPrivate::BindingEvaluationState ** currentlyEvaluatingBindingList
Definition: qproperty_p.h:209
CompatPropertySafePoint * previousState
Definition: qproperty_p.h:207
CompatPropertySafePoint ** currentState
Definition: qproperty_p.h:208
QUntypedPropertyData * property
Definition: qproperty_p.h:206
QtPrivate::BindingEvaluationState * bindingState
Definition: qproperty_p.h:210
Q_CORE_EXPORT CompatPropertySafePoint(QBindingStatus *status, QUntypedPropertyData *property)
Definition: qproperty.cpp:514
Definition: main.cpp:38