1// Copyright (C) 2020 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_H
5#define QPROPERTY_H
6
7#include <QtCore/qglobal.h>
8#include <QtCore/qshareddata.h>
9#include <QtCore/qstring.h>
10#include <QtCore/qbindingstorage.h>
11
12#include <type_traits>
13
14#include <QtCore/qpropertyprivate.h>
15
16#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_QDOC)
17#include <source_location>
18#if defined(__cpp_lib_source_location)
19#define QT_SOURCE_LOCATION_NAMESPACE std
20#define QT_PROPERTY_COLLECT_BINDING_LOCATION
21#if defined(Q_CC_MSVC)
22/* MSVC runs into an issue with constexpr with source location (error C7595)
23 so use the factory function as a workaround */
24# define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation::fromStdSourceLocation(std::source_location::current())
25#else
26/* some versions of gcc in turn run into
27 expression ‘std::source_location::current()’ is not a constant expression
28 so don't use the workaround there */
29# define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::source_location::current())
30#endif
31#endif
32#endif
33
34#if __has_include(<experimental/source_location>) && !defined(Q_QDOC)
35#include <experimental/source_location>
36#if !defined(QT_PROPERTY_COLLECT_BINDING_LOCATION)
37#if defined(__cpp_lib_experimental_source_location)
38#define QT_SOURCE_LOCATION_NAMESPACE std::experimental
39#define QT_PROPERTY_COLLECT_BINDING_LOCATION
40#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::experimental::source_location::current())
41#endif // defined(__cpp_lib_experimental_source_location)
42#endif
43#endif
44
45#if !defined(QT_PROPERTY_COLLECT_BINDING_LOCATION)
46#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation()
47#endif
48
49QT_BEGIN_NAMESPACE
50
51namespace Qt {
52Q_CORE_EXPORT void beginPropertyUpdateGroup();
53Q_CORE_EXPORT void endPropertyUpdateGroup();
54}
55
56class QScopedPropertyUpdateGroup
57{
58 Q_DISABLE_COPY_MOVE(QScopedPropertyUpdateGroup)
59public:
60 Q_NODISCARD_CTOR
61 QScopedPropertyUpdateGroup()
62 { Qt::beginPropertyUpdateGroup(); }
63 ~QScopedPropertyUpdateGroup() noexcept(false)
64 { Qt::endPropertyUpdateGroup(); }
65};
66
67template <typename T>
68class QPropertyData : public QUntypedPropertyData
69{
70protected:
71 mutable T val = T();
72private:
73 class DisableRValueRefs {};
74protected:
75 static constexpr bool UseReferences = !(std::is_arithmetic_v<T> || std::is_enum_v<T> || std::is_pointer_v<T>);
76public:
77 using value_type = T;
78 using parameter_type = std::conditional_t<UseReferences, const T &, T>;
79 using rvalue_ref = typename std::conditional_t<UseReferences, T &&, DisableRValueRefs>;
80 using arrow_operator_result = std::conditional_t<std::is_pointer_v<T>, const T &,
81 std::conditional_t<QTypeTraits::is_dereferenceable_v<T>, const T &, void>>;
82
83 QPropertyData() = default;
84 QPropertyData(parameter_type t) : val(t) {}
85 QPropertyData(rvalue_ref t) : val(std::move(t)) {}
86 ~QPropertyData() = default;
87
88 parameter_type valueBypassingBindings() const { return val; }
89 void setValueBypassingBindings(parameter_type v) { val = v; }
90 void setValueBypassingBindings(rvalue_ref v) { val = std::move(v); }
91};
92
93// ### Qt 7: un-export
94struct Q_CORE_EXPORT QPropertyBindingSourceLocation
95{
96 const char *fileName = nullptr;
97 const char *functionName = nullptr;
98 quint32 line = 0;
99 quint32 column = 0;
100 QPropertyBindingSourceLocation() = default;
101#ifdef __cpp_lib_source_location
102 constexpr QPropertyBindingSourceLocation(const std::source_location &cppLocation)
103 {
104 fileName = cppLocation.file_name();
105 functionName = cppLocation.function_name();
106 line = cppLocation.line();
107 column = cppLocation.column();
108 }
109 QT_POST_CXX17_API_IN_EXPORTED_CLASS
110 static consteval QPropertyBindingSourceLocation
111 fromStdSourceLocation(const std::source_location &cppLocation)
112 {
113 return cppLocation;
114 }
115#endif
116#ifdef __cpp_lib_experimental_source_location
117 constexpr QPropertyBindingSourceLocation(const std::experimental::source_location &cppLocation)
118 {
119 fileName = cppLocation.file_name();
120 functionName = cppLocation.function_name();
121 line = cppLocation.line();
122 column = cppLocation.column();
123 }
124#endif
125};
126
127template <typename Functor> class QPropertyChangeHandler;
128class QPropertyBindingErrorPrivate;
129
130class Q_CORE_EXPORT QPropertyBindingError
131{
132public:
133 enum Type {
134 NoError,
135 BindingLoop,
136 EvaluationError,
137 UnknownError
138 };
139
140 QPropertyBindingError();
141 QPropertyBindingError(Type type, const QString &description = QString());
142
143 QPropertyBindingError(const QPropertyBindingError &other);
144 QPropertyBindingError &operator=(const QPropertyBindingError &other);
145 QPropertyBindingError(QPropertyBindingError &&other);
146 QPropertyBindingError &operator=(QPropertyBindingError &&other);
147 ~QPropertyBindingError();
148
149 bool hasError() const { return d.get() != nullptr; }
150 Type type() const;
151 QString description() const;
152
153private:
154 QSharedDataPointer<QPropertyBindingErrorPrivate> d;
155};
156
157class Q_CORE_EXPORT QUntypedPropertyBinding
158{
159public:
160 // writes binding result into dataPtr
161 using BindingFunctionVTable = QtPrivate::BindingFunctionVTable;
162
163 QUntypedPropertyBinding();
164 QUntypedPropertyBinding(QMetaType metaType, const BindingFunctionVTable *vtable, void *function, const QPropertyBindingSourceLocation &location);
165
166 template<typename Functor>
167 QUntypedPropertyBinding(QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location)
168 : QUntypedPropertyBinding(metaType, &QtPrivate::bindingFunctionVTable<std::remove_reference_t<Functor>>, &f, location)
169 {}
170
171 QUntypedPropertyBinding(QUntypedPropertyBinding &&other);
172 QUntypedPropertyBinding(const QUntypedPropertyBinding &other);
173 QUntypedPropertyBinding &operator=(const QUntypedPropertyBinding &other);
174 QUntypedPropertyBinding &operator=(QUntypedPropertyBinding &&other);
175 ~QUntypedPropertyBinding();
176
177 bool isNull() const;
178
179 QPropertyBindingError error() const;
180
181 QMetaType valueMetaType() const;
182
183 explicit QUntypedPropertyBinding(QPropertyBindingPrivate *priv);
184private:
185 friend class QtPrivate::QPropertyBindingData;
186 friend class QPropertyBindingPrivate;
187 template <typename> friend class QPropertyBinding;
188 QPropertyBindingPrivatePtr d;
189};
190
191template <typename PropertyType>
192class QPropertyBinding : public QUntypedPropertyBinding
193{
194
195public:
196 QPropertyBinding() = default;
197
198 template<typename Functor>
199 QPropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location)
200 : QUntypedPropertyBinding(QMetaType::fromType<PropertyType>(), &QtPrivate::bindingFunctionVTable<std::remove_reference_t<Functor>, PropertyType>, &f, location)
201 {}
202
203
204 // Internal
205 explicit QPropertyBinding(const QUntypedPropertyBinding &binding)
206 : QUntypedPropertyBinding(binding)
207 {}
208};
209
210namespace Qt {
211 template <typename Functor>
212 auto makePropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
213 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
214 {
215 return QPropertyBinding<std::invoke_result_t<Functor>>(std::forward<Functor>(f), location);
216 }
217}
218
219struct QPropertyObserverPrivate;
220struct QPropertyObserverPointer;
221class QPropertyObserver;
222
223class QPropertyObserverBase
224{
225public:
226 // Internal
227 enum ObserverTag {
228 ObserverNotifiesBinding, // observer was installed to notify bindings that obsverved property changed
229 ObserverNotifiesChangeHandler, // observer is a change handler, which runs on every change
230 ObserverIsPlaceholder, // the observer before this one is currently evaluated in QPropertyObserver::notifyObservers.
231#if QT_DEPRECATED_SINCE(6, 6)
232 ObserverIsAlias QT_DEPRECATED_VERSION_X_6_6("Use QProperty and add a binding to the target.")
233#endif
234 };
235protected:
236 using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
237
238private:
239 friend struct QPropertyDelayedNotifications;
240 friend struct QPropertyObserverNodeProtector;
241 friend class QPropertyObserver;
242 friend struct QPropertyObserverPointer;
243 friend struct QPropertyBindingDataPointer;
244 friend class QPropertyBindingPrivate;
245
246 QTaggedPointer<QPropertyObserver, ObserverTag> next;
247 // prev is a pointer to the "next" element within the previous node, or to the "firstObserverPtr" if it is the
248 // first node.
249 QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev;
250
251 union {
252 QPropertyBindingPrivate *binding = nullptr;
253 ChangeHandler changeHandler;
254 QUntypedPropertyData *aliasData;
255 };
256};
257
258class Q_CORE_EXPORT QPropertyObserver : public QPropertyObserverBase
259{
260public:
261 constexpr QPropertyObserver() = default;
262 QPropertyObserver(QPropertyObserver &&other) noexcept;
263 QPropertyObserver &operator=(QPropertyObserver &&other) noexcept;
264 ~QPropertyObserver();
265
266 template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
267 void setSource(const Property &property)
268 { setSource(property.bindingData()); }
269 void setSource(const QtPrivate::QPropertyBindingData &property);
270
271protected:
272 QPropertyObserver(ChangeHandler changeHandler);
273#if QT_DEPRECATED_SINCE(6, 6)
274 QT_DEPRECATED_VERSION_X_6_6("This constructor was only meant for internal use. Use QProperty and add a binding to the target.")
275 QPropertyObserver(QUntypedPropertyData *aliasedPropertyPtr);
276#endif
277
278 QUntypedPropertyData *aliasedProperty() const
279 {
280 return aliasData;
281 }
282
283private:
284
285 QPropertyObserver(const QPropertyObserver &) = delete;
286 QPropertyObserver &operator=(const QPropertyObserver &) = delete;
287
288};
289
290template <typename Functor>
291class QPropertyChangeHandler : public QPropertyObserver
292{
293 Functor m_handler;
294public:
295 Q_NODISCARD_CTOR
296 QPropertyChangeHandler(Functor handler)
297 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
298 auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
299 This->m_handler();
300 })
301 , m_handler(handler)
302 {
303 }
304
305 template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
306 Q_NODISCARD_CTOR
307 QPropertyChangeHandler(const Property &property, Functor handler)
308 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
309 auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
310 This->m_handler();
311 })
312 , m_handler(handler)
313 {
314 setSource(property);
315 }
316};
317
318class QPropertyNotifier : public QPropertyObserver
319{
320 std::function<void()> m_handler;
321public:
322 Q_NODISCARD_CTOR
323 QPropertyNotifier() = default;
324 template<typename Functor>
325 Q_NODISCARD_CTOR
326 QPropertyNotifier(Functor handler)
327 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
328 auto This = static_cast<QPropertyNotifier *>(self);
329 This->m_handler();
330 })
331 , m_handler(handler)
332 {
333 }
334
335 template<typename Functor, typename Property, typename = typename Property::InheritsQUntypedPropertyData>
336 Q_NODISCARD_CTOR
337 QPropertyNotifier(const Property &property, Functor handler)
338 : QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
339 auto This = static_cast<QPropertyNotifier *>(self);
340 This->m_handler();
341 })
342 , m_handler(handler)
343 {
344 setSource(property);
345 }
346};
347
348template <typename T>
349class QProperty : public QPropertyData<T>
350{
351 QtPrivate::QPropertyBindingData d;
352 bool is_equal(const T &v)
353 {
354 if constexpr (QTypeTraits::has_operator_equal_v<T>) {
355 if (v == this->val)
356 return true;
357 }
358 return false;
359 }
360
361public:
362 using value_type = typename QPropertyData<T>::value_type;
363 using parameter_type = typename QPropertyData<T>::parameter_type;
364 using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
365 using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
366
367 QProperty() = default;
368 explicit QProperty(parameter_type initialValue) : QPropertyData<T>(initialValue) {}
369 explicit QProperty(rvalue_ref initialValue) : QPropertyData<T>(std::move(initialValue)) {}
370 explicit QProperty(const QPropertyBinding<T> &binding)
371 : QProperty()
372 { setBinding(binding); }
373#ifndef Q_QDOC
374 template <typename Functor>
375 explicit QProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
376 typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
377 : QProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
378 {}
379#else
380 template <typename Functor>
381 explicit QProperty(Functor &&f);
382#endif
383 ~QProperty() = default;
384
385 parameter_type value() const
386 {
387 d.registerWithCurrentlyEvaluatingBinding();
388 return this->val;
389 }
390
391 arrow_operator_result operator->() const
392 {
393 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
394 return value();
395 } else if constexpr (std::is_pointer_v<T>) {
396 value();
397 return this->val;
398 } else {
399 return;
400 }
401 }
402
403 parameter_type operator*() const
404 {
405 return value();
406 }
407
408 operator parameter_type() const
409 {
410 return value();
411 }
412
413 void setValue(rvalue_ref newValue)
414 {
415 d.removeBinding();
416 if (is_equal(v: newValue))
417 return;
418 this->val = std::move(newValue);
419 notify();
420 }
421
422 void setValue(parameter_type newValue)
423 {
424 d.removeBinding();
425 if (is_equal(v: newValue))
426 return;
427 this->val = newValue;
428 notify();
429 }
430
431 QProperty<T> &operator=(rvalue_ref newValue)
432 {
433 setValue(std::move(newValue));
434 return *this;
435 }
436
437 QProperty<T> &operator=(parameter_type newValue)
438 {
439 setValue(newValue);
440 return *this;
441 }
442
443 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
444 {
445 return QPropertyBinding<T>(d.setBinding(newBinding, propertyDataPtr: this));
446 }
447
448 bool setBinding(const QUntypedPropertyBinding &newBinding)
449 {
450 if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
451 return false;
452 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
453 return true;
454 }
455
456#ifndef Q_QDOC
457 template <typename Functor>
458 QPropertyBinding<T> setBinding(Functor &&f,
459 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
460 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
461 {
462 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
463 }
464#else
465 template <typename Functor>
466 QPropertyBinding<T> setBinding(Functor f);
467#endif
468
469 bool hasBinding() const { return d.hasBinding(); }
470
471 QPropertyBinding<T> binding() const
472 {
473 return QPropertyBinding<T>(QUntypedPropertyBinding(d.binding()));
474 }
475
476 QPropertyBinding<T> takeBinding()
477 {
478 return QPropertyBinding<T>(d.setBinding(newBinding: QUntypedPropertyBinding(), propertyDataPtr: this));
479 }
480
481 template<typename Functor>
482 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
483 {
484 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
485 return QPropertyChangeHandler<Functor>(*this, f);
486 }
487
488 template<typename Functor>
489 QPropertyChangeHandler<Functor> subscribe(Functor f)
490 {
491 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
492 f();
493 return onValueChanged(f);
494 }
495
496 template<typename Functor>
497 QPropertyNotifier addNotifier(Functor f)
498 {
499 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
500 return QPropertyNotifier(*this, f);
501 }
502
503 const QtPrivate::QPropertyBindingData &bindingData() const { return d; }
504private:
505 void notify()
506 {
507 d.notifyObservers(this);
508 }
509
510 Q_DISABLE_COPY_MOVE(QProperty)
511};
512
513namespace Qt {
514 template <typename PropertyType>
515 QPropertyBinding<PropertyType> makePropertyBinding(const QProperty<PropertyType> &otherProperty,
516 const QPropertyBindingSourceLocation &location =
517 QT_PROPERTY_DEFAULT_BINDING_LOCATION)
518 {
519 return Qt::makePropertyBinding([&otherProperty]() -> PropertyType { return otherProperty; }, location);
520 }
521}
522
523
524namespace QtPrivate
525{
526
527struct QBindableInterface
528{
529 using Getter = void (*)(const QUntypedPropertyData *d, void *value);
530 using Setter = void (*)(QUntypedPropertyData *d, const void *value);
531 using BindingGetter = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d);
532 using BindingSetter = QUntypedPropertyBinding (*)(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding);
533 using MakeBinding = QUntypedPropertyBinding (*)(const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location);
534 using SetObserver = void (*)(const QUntypedPropertyData *d, QPropertyObserver *observer);
535 using GetMetaType = QMetaType (*)();
536 Getter getter;
537 Setter setter;
538 BindingGetter getBinding;
539 BindingSetter setBinding;
540 MakeBinding makeBinding;
541 SetObserver setObserver;
542 GetMetaType metaType;
543
544 static constexpr quintptr MetaTypeAccessorFlag = 0x1;
545};
546
547template<typename Property, typename = void>
548class QBindableInterfaceForProperty
549{
550 using T = typename Property::value_type;
551public:
552 // interface for computed properties. Those do not have a binding()/setBinding() method, but one can
553 // install observers on them.
554 static constexpr QBindableInterface iface = {
555 [](const QUntypedPropertyData *d, void *value) -> void
556 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
557 nullptr,
558 nullptr,
559 nullptr,
560 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
561 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
562 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
563 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
564 []() { return QMetaType::fromType<T>(); }
565 };
566};
567
568template<typename Property>
569class QBindableInterfaceForProperty<const Property, std::void_t<decltype(std::declval<Property>().binding())>>
570{
571 using T = typename Property::value_type;
572public:
573 // A bindable created from a const property results in a read-only interface, too.
574 static constexpr QBindableInterface iface = {
575
576 [](const QUntypedPropertyData *d, void *value) -> void
577 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
578 /*setter=*/nullptr,
579 [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
580 { return static_cast<const Property *>(d)->binding(); },
581 /*setBinding=*/nullptr,
582 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
583 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
584 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
585 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
586 []() { return QMetaType::fromType<T>(); }
587 };
588};
589
590template<typename Property>
591class QBindableInterfaceForProperty<Property, std::void_t<decltype(std::declval<Property>().binding())>>
592{
593 using T = typename Property::value_type;
594public:
595 static constexpr QBindableInterface iface = {
596 [](const QUntypedPropertyData *d, void *value) -> void
597 { *static_cast<T*>(value) = static_cast<const Property *>(d)->value(); },
598 [](QUntypedPropertyData *d, const void *value) -> void
599 { static_cast<Property *>(d)->setValue(*static_cast<const T*>(value)); },
600 [](const QUntypedPropertyData *d) -> QUntypedPropertyBinding
601 { return static_cast<const Property *>(d)->binding(); },
602 [](QUntypedPropertyData *d, const QUntypedPropertyBinding &binding) -> QUntypedPropertyBinding
603 { return static_cast<Property *>(d)->setBinding(static_cast<const QPropertyBinding<T> &>(binding)); },
604 [](const QUntypedPropertyData *d, const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding
605 { return Qt::makePropertyBinding([d]() -> T { return static_cast<const Property *>(d)->value(); }, location); },
606 [](const QUntypedPropertyData *d, QPropertyObserver *observer) -> void
607 { observer->setSource(static_cast<const Property *>(d)->bindingData()); },
608 []() { return QMetaType::fromType<T>(); }
609 };
610};
611
612}
613
614namespace QtPrivate {
615// used in Q(Untyped)Bindable to print warnings about various binding errors
616namespace BindableWarnings {
617enum Reason { InvalidInterface, NonBindableInterface, ReadOnlyInterface };
618Q_CORE_EXPORT void printUnsuitableBindableWarning(QAnyStringView prefix, Reason reason);
619Q_CORE_EXPORT void printMetaTypeMismatch(QMetaType actual, QMetaType expected);
620}
621
622namespace PropertyAdaptorSlotObjectHelpers {
623Q_CORE_EXPORT void getter(const QUntypedPropertyData *d, void *value);
624Q_CORE_EXPORT void setter(QUntypedPropertyData *d, const void *value);
625Q_CORE_EXPORT QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d);
626Q_CORE_EXPORT bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
627 QtPrivate::QPropertyBindingFunction binding,
628 QUntypedPropertyData *temp, void *value);
629Q_CORE_EXPORT QUntypedPropertyBinding setBinding(QUntypedPropertyData *d,
630 const QUntypedPropertyBinding &binding,
631 QPropertyBindingWrapper wrapper);
632Q_CORE_EXPORT void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer);
633
634template<typename T>
635bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
636 QtPrivate::QPropertyBindingFunction binding)
637{
638 struct Data : QPropertyData<T>
639 {
640 void *data() { return &this->val; }
641 } temp;
642 return bindingWrapper(type, d, binding, &temp, temp.data());
643}
644
645template<typename T>
646QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding)
647{
648 return setBinding(d, binding, &bindingWrapper<T>);
649}
650
651template<typename T>
652QUntypedPropertyBinding makeBinding(const QUntypedPropertyData *d,
653 const QPropertyBindingSourceLocation &location)
654{
655 return Qt::makePropertyBinding(
656 [d]() -> T {
657 T r;
658 getter(d, &r);
659 return r;
660 },
661 location);
662}
663
664template<class T>
665inline constexpr QBindableInterface iface = {
666 &getter,
667 &setter,
668 &getBinding,
669 &setBinding<T>,
670 &makeBinding<T>,
671 &setObserver,
672 &QMetaType::fromType<T>,
673};
674}
675}
676
677class QUntypedBindable
678{
679 friend struct QUntypedBindablePrivate; // allows access to internal data
680protected:
681 QUntypedPropertyData *data = nullptr;
682 const QtPrivate::QBindableInterface *iface = nullptr;
683 constexpr QUntypedBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i)
684 : data(d), iface(i)
685 {}
686
687 Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const QMetaProperty &property, const QtPrivate::QBindableInterface *i);
688 Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const char* property, const QtPrivate::QBindableInterface *i);
689
690public:
691 constexpr QUntypedBindable() = default;
692 template<typename Property>
693 QUntypedBindable(Property *p)
694 : data(const_cast<std::remove_cv_t<Property> *>(p)),
695 iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
696 { Q_ASSERT(data && iface); }
697
698 bool isValid() const { return data != nullptr; }
699 bool isBindable() const { return iface && iface->getBinding; }
700 bool isReadOnly() const { return !(iface && iface->setBinding && iface->setObserver); }
701
702 QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
703 {
704 return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding();
705 }
706
707 QUntypedPropertyBinding takeBinding()
708 {
709 if (!iface)
710 return QUntypedPropertyBinding {};
711 // We do not have a dedicated takeBinding function pointer in the interface
712 // therefore we synthesize takeBinding by retrieving the binding with binding
713 // and calling setBinding with a default constructed QUntypedPropertyBinding
714 // afterwards.
715 if (!(iface->getBinding && iface->setBinding))
716 return QUntypedPropertyBinding {};
717 QUntypedPropertyBinding binding = iface->getBinding(data);
718 iface->setBinding(data, QUntypedPropertyBinding{});
719 return binding;
720 }
721
722 void observe(QPropertyObserver *observer) const
723 {
724 if (iface)
725 iface->setObserver(data, observer);
726#ifndef QT_NO_DEBUG
727 else
728 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "observe:",
729 reason: QtPrivate::BindableWarnings::InvalidInterface);
730#endif
731 }
732
733 template<typename Functor>
734 QPropertyChangeHandler<Functor> onValueChanged(Functor f) const
735 {
736 QPropertyChangeHandler<Functor> handler(f);
737 observe(observer: &handler);
738 return handler;
739 }
740
741 template<typename Functor>
742 QPropertyChangeHandler<Functor> subscribe(Functor f) const
743 {
744 f();
745 return onValueChanged(f);
746 }
747
748 template<typename Functor>
749 QPropertyNotifier addNotifier(Functor f)
750 {
751 QPropertyNotifier handler(f);
752 observe(observer: &handler);
753 return handler;
754 }
755
756 QUntypedPropertyBinding binding() const
757 {
758 if (!isBindable()) {
759#ifndef QT_NO_DEBUG
760 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "binding: ",
761 reason: QtPrivate::BindableWarnings::NonBindableInterface);
762#endif
763 return QUntypedPropertyBinding();
764 }
765 return iface->getBinding(data);
766 }
767 bool setBinding(const QUntypedPropertyBinding &binding)
768 {
769 if (isReadOnly()) {
770#ifndef QT_NO_DEBUG
771 const auto errorType = iface ? QtPrivate::BindableWarnings::ReadOnlyInterface :
772 QtPrivate::BindableWarnings::InvalidInterface;
773 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "setBinding: Could not set binding via bindable interface.", reason: errorType);
774#endif
775 return false;
776 }
777 if (!binding.isNull() && binding.valueMetaType() != metaType()) {
778#ifndef QT_NO_DEBUG
779 QtPrivate::BindableWarnings::printMetaTypeMismatch(actual: metaType(), expected: binding.valueMetaType());
780#endif
781 return false;
782 }
783 iface->setBinding(data, binding);
784 return true;
785 }
786 bool hasBinding() const
787 {
788 return !binding().isNull();
789 }
790
791 QMetaType metaType() const
792 {
793 if (!(iface && data))
794 return QMetaType();
795 if (iface->metaType)
796 return iface->metaType();
797 // ### Qt 7: Change the metatype function to take data as its argument
798 // special casing for QML's proxy bindable: allow multiplexing in the getter
799 // function to retrieve the metatype from data
800 Q_ASSERT(iface->getter);
801 QMetaType result;
802 iface->getter(data, reinterpret_cast<void *>(quintptr(&result) | QtPrivate::QBindableInterface::MetaTypeAccessorFlag));
803 return result;
804 }
805
806};
807
808template<typename T>
809class QBindable : public QUntypedBindable
810{
811 template<typename U>
812 friend class QPropertyAlias;
813 constexpr QBindable(QUntypedPropertyData *d, const QtPrivate::QBindableInterface *i)
814 : QUntypedBindable(d, i)
815 {}
816public:
817 using QUntypedBindable::QUntypedBindable;
818 explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b)
819 {
820 if (iface && metaType() != QMetaType::fromType<T>()) {
821 data = nullptr;
822 iface = nullptr;
823 }
824 }
825
826 explicit QBindable(QObject *obj, const QMetaProperty &property)
827 : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
828
829 explicit QBindable(QObject *obj, const char *property)
830 : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
831
832 QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
833 {
834 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location));
835 }
836 QPropertyBinding<T> binding() const
837 {
838 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::binding());
839 }
840
841 QPropertyBinding<T> takeBinding()
842 {
843 return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::takeBinding());
844 }
845
846 using QUntypedBindable::setBinding;
847 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding)
848 {
849 Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == metaType());
850
851 if (iface && iface->setBinding)
852 return static_cast<QPropertyBinding<T> &&>(iface->setBinding(data, binding));
853#ifndef QT_NO_DEBUG
854 if (!iface)
855 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "setBinding", reason: QtPrivate::BindableWarnings::InvalidInterface);
856 else
857 QtPrivate::BindableWarnings::printUnsuitableBindableWarning(prefix: "setBinding: Could not set binding via bindable interface.", reason: QtPrivate::BindableWarnings::ReadOnlyInterface);
858#endif
859 return QPropertyBinding<T>();
860 }
861#ifndef Q_QDOC
862 template <typename Functor>
863 QPropertyBinding<T> setBinding(Functor &&f,
864 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
865 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
866 {
867 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
868 }
869#else
870 template <typename Functor>
871 QPropertyBinding<T> setBinding(Functor f);
872#endif
873
874 T value() const
875 {
876 if (iface) {
877 T result;
878 iface->getter(data, &result);
879 return result;
880 }
881 return T{};
882 }
883
884 void setValue(const T &value)
885 {
886 if (iface && iface->setter)
887 iface->setter(data, &value);
888 }
889};
890
891#if QT_DEPRECATED_SINCE(6, 6)
892template<typename T>
893class QT_DEPRECATED_VERSION_X_6_6("Class was only meant for internal use, use a QProperty and add a binding to the target")
894QPropertyAlias : public QPropertyObserver
895{
896 Q_DISABLE_COPY_MOVE(QPropertyAlias)
897 const QtPrivate::QBindableInterface *iface = nullptr;
898
899public:
900 QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
901 QPropertyAlias(QProperty<T> *property)
902 : QPropertyObserver(property),
903 iface(&QtPrivate::QBindableInterfaceForProperty<QProperty<T>>::iface)
904 {
905 if (iface)
906 iface->setObserver(aliasedProperty(), this);
907 }
908
909 template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
910 QPropertyAlias(Property *property)
911 : QPropertyObserver(property),
912 iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
913 {
914 if (iface)
915 iface->setObserver(aliasedProperty(), this);
916 }
917
918 QPropertyAlias(QPropertyAlias<T> *alias)
919 : QPropertyObserver(alias->aliasedProperty()),
920 iface(alias->iface)
921 {
922 if (iface)
923 iface->setObserver(aliasedProperty(), this);
924 }
925
926 QPropertyAlias(const QBindable<T> &property)
927 : QPropertyObserver(property.data),
928 iface(property.iface)
929 {
930 if (iface)
931 iface->setObserver(aliasedProperty(), this);
932 }
933
934 T value() const
935 {
936 T t = T();
937 if (auto *p = aliasedProperty())
938 iface->getter(p, &t);
939 return t;
940 }
941
942 operator T() const { return value(); }
943
944 void setValue(const T &newValue)
945 {
946 if (auto *p = aliasedProperty())
947 iface->setter(p, &newValue);
948 }
949
950 QPropertyAlias<T> &operator=(const T &newValue)
951 {
952 setValue(newValue);
953 return *this;
954 }
955
956 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
957 {
958 return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
959 }
960
961 bool setBinding(const QUntypedPropertyBinding &newBinding)
962 {
963 return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
964 }
965
966#ifndef Q_QDOC
967 template <typename Functor>
968 QPropertyBinding<T> setBinding(Functor &&f,
969 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
970 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
971 {
972 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
973 }
974#else
975 template <typename Functor>
976 QPropertyBinding<T> setBinding(Functor f);
977#endif
978
979 bool hasBinding() const
980 {
981 return QBindable<T>(aliasedProperty(), iface).hasBinding();
982 }
983
984 QPropertyBinding<T> binding() const
985 {
986 return QBindable<T>(aliasedProperty(), iface).binding();
987 }
988
989 QPropertyBinding<T> takeBinding()
990 {
991 return QBindable<T>(aliasedProperty(), iface).takeBinding();
992 }
993
994 template<typename Functor>
995 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
996 {
997 return QBindable<T>(aliasedProperty(), iface).onValueChanged(f);
998 }
999
1000 template<typename Functor>
1001 QPropertyChangeHandler<Functor> subscribe(Functor f)
1002 {
1003 return QBindable<T>(aliasedProperty(), iface).subscribe(f);
1004 }
1005
1006 template<typename Functor>
1007 QPropertyNotifier addNotifier(Functor f)
1008 {
1009 return QBindable<T>(aliasedProperty(), iface).addNotifier(f);
1010 }
1011
1012 bool isValid() const
1013 {
1014 return aliasedProperty() != nullptr;
1015 }
1016 QT_WARNING_POP
1017};
1018#endif // QT_DEPRECATED_SINCE(6, 6)
1019
1020template<typename Class, typename T, auto Offset, auto Signal = nullptr>
1021class QObjectBindableProperty : public QPropertyData<T>
1022{
1023 using ThisType = QObjectBindableProperty<Class, T, Offset, Signal>;
1024 static bool constexpr HasSignal = !std::is_same_v<decltype(Signal), std::nullptr_t>;
1025 using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
1026 Class *owner()
1027 {
1028 char *that = reinterpret_cast<char *>(this);
1029 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1030 }
1031 const Class *owner() const
1032 {
1033 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
1034 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1035 }
1036 static void signalCallBack(QUntypedPropertyData *o)
1037 {
1038 QObjectBindableProperty *that = static_cast<QObjectBindableProperty *>(o);
1039 if constexpr (HasSignal) {
1040 if constexpr (SignalTakesValue::value)
1041 (that->owner()->*Signal)(that->valueBypassingBindings());
1042 else
1043 (that->owner()->*Signal)();
1044 }
1045 }
1046public:
1047 using value_type = typename QPropertyData<T>::value_type;
1048 using parameter_type = typename QPropertyData<T>::parameter_type;
1049 using rvalue_ref = typename QPropertyData<T>::rvalue_ref;
1050 using arrow_operator_result = typename QPropertyData<T>::arrow_operator_result;
1051
1052 QObjectBindableProperty() = default;
1053 explicit QObjectBindableProperty(const T &initialValue) : QPropertyData<T>(initialValue) {}
1054 explicit QObjectBindableProperty(T &&initialValue) : QPropertyData<T>(std::move(initialValue)) {}
1055 explicit QObjectBindableProperty(const QPropertyBinding<T> &binding)
1056 : QObjectBindableProperty()
1057 { setBinding(binding); }
1058#ifndef Q_QDOC
1059 template <typename Functor>
1060 explicit QObjectBindableProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
1061 typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
1062 : QObjectBindableProperty(QPropertyBinding<T>(std::forward<Functor>(f), location))
1063 {}
1064#else
1065 template <typename Functor>
1066 explicit QObjectBindableProperty(Functor &&f);
1067#endif
1068
1069 parameter_type value() const
1070 {
1071 qGetBindingStorage(owner())->registerDependency(this);
1072 return this->val;
1073 }
1074
1075 arrow_operator_result operator->() const
1076 {
1077 if constexpr (QTypeTraits::is_dereferenceable_v<T>) {
1078 return value();
1079 } else if constexpr (std::is_pointer_v<T>) {
1080 value();
1081 return this->val;
1082 } else {
1083 return;
1084 }
1085 }
1086
1087 parameter_type operator*() const
1088 {
1089 return value();
1090 }
1091
1092 operator parameter_type() const
1093 {
1094 return value();
1095 }
1096
1097 void setValue(parameter_type t)
1098 {
1099 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1100 if (bd)
1101 bd->removeBinding();
1102 if (this->val == t)
1103 return;
1104 this->val = t;
1105 notify(bd);
1106 }
1107
1108 void notify() {
1109 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1110 notify(bd);
1111 }
1112
1113 void setValue(rvalue_ref t)
1114 {
1115 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1116 if (bd)
1117 bd->removeBinding();
1118 if (this->val == t)
1119 return;
1120 this->val = std::move(t);
1121 notify(bd);
1122 }
1123
1124 QObjectBindableProperty &operator=(rvalue_ref newValue)
1125 {
1126 setValue(std::move(newValue));
1127 return *this;
1128 }
1129
1130 QObjectBindableProperty &operator=(parameter_type newValue)
1131 {
1132 setValue(newValue);
1133 return *this;
1134 }
1135
1136 QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
1137 {
1138 QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
1139 QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, propertyDataPtr: this, staticObserverCallback: HasSignal ? &signalCallBack : nullptr));
1140 return static_cast<QPropertyBinding<T> &>(oldBinding);
1141 }
1142
1143 bool setBinding(const QUntypedPropertyBinding &newBinding)
1144 {
1145 if (!newBinding.isNull() && newBinding.valueMetaType().id() != qMetaTypeId<T>())
1146 return false;
1147 setBinding(static_cast<const QPropertyBinding<T> &>(newBinding));
1148 return true;
1149 }
1150
1151#ifndef Q_QDOC
1152 template <typename Functor>
1153 QPropertyBinding<T> setBinding(Functor &&f,
1154 const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
1155 std::enable_if_t<std::is_invocable_v<Functor>> * = nullptr)
1156 {
1157 return setBinding(Qt::makePropertyBinding(std::forward<Functor>(f), location));
1158 }
1159#else
1160 template <typename Functor>
1161 QPropertyBinding<T> setBinding(Functor f);
1162#endif
1163
1164 bool hasBinding() const
1165 {
1166 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1167 return bd && bd->binding() != nullptr;
1168 }
1169
1170 QPropertyBinding<T> binding() const
1171 {
1172 auto *bd = qGetBindingStorage(owner())->bindingData(this);
1173 return static_cast<QPropertyBinding<T> &&>(QUntypedPropertyBinding(bd ? bd->binding() : nullptr));
1174 }
1175
1176 QPropertyBinding<T> takeBinding()
1177 {
1178 return setBinding(QPropertyBinding<T>());
1179 }
1180
1181 template<typename Functor>
1182 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
1183 {
1184 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1185 return QPropertyChangeHandler<Functor>(*this, f);
1186 }
1187
1188 template<typename Functor>
1189 QPropertyChangeHandler<Functor> subscribe(Functor f)
1190 {
1191 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1192 f();
1193 return onValueChanged(f);
1194 }
1195
1196 template<typename Functor>
1197 QPropertyNotifier addNotifier(Functor f)
1198 {
1199 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1200 return QPropertyNotifier(*this, f);
1201 }
1202
1203 const QtPrivate::QPropertyBindingData &bindingData() const
1204 {
1205 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1206 return *storage->bindingData(const_cast<ThisType *>(this), true);
1207 }
1208private:
1209 void notify(const QtPrivate::QPropertyBindingData *binding)
1210 {
1211 if (binding)
1212 binding->notifyObservers(this, qGetBindingStorage(owner()));
1213 if constexpr (HasSignal) {
1214 if constexpr (SignalTakesValue::value)
1215 (owner()->*Signal)(this->valueBypassingBindings());
1216 else
1217 (owner()->*Signal)();
1218 }
1219 }
1220};
1221
1222#define QT_OBJECT_BINDABLE_PROPERTY_3(Class, Type, name) \
1223 static constexpr size_t _qt_property_##name##_offset() { \
1224 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1225 return offsetof(Class, name); \
1226 QT_WARNING_POP \
1227 } \
1228 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name;
1229
1230#define QT_OBJECT_BINDABLE_PROPERTY_4(Class, Type, name, Signal) \
1231 static constexpr size_t _qt_property_##name##_offset() { \
1232 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1233 return offsetof(Class, name); \
1234 QT_WARNING_POP \
1235 } \
1236 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name;
1237
1238#define Q_OBJECT_BINDABLE_PROPERTY(...) \
1239 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1240 QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY, __VA_ARGS__) \
1241 QT_WARNING_POP
1242
1243#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_4(Class, Type, name, value) \
1244 static constexpr size_t _qt_property_##name##_offset() \
1245 { \
1246 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1247 return offsetof(Class, name); \
1248 QT_WARNING_POP \
1249 } \
1250 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name = \
1251 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr>( \
1252 value);
1253
1254#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_5(Class, Type, name, value, Signal) \
1255 static constexpr size_t _qt_property_##name##_offset() \
1256 { \
1257 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1258 return offsetof(Class, name); \
1259 QT_WARNING_POP \
1260 } \
1261 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name = \
1262 QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal>( \
1263 value);
1264
1265#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(...) \
1266 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1267 QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) \
1268 QT_WARNING_POP
1269
1270template<typename Class, typename T, auto Offset, auto Getter>
1271class QObjectComputedProperty : public QUntypedPropertyData
1272{
1273 Class *owner()
1274 {
1275 char *that = reinterpret_cast<char *>(this);
1276 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1277 }
1278 const Class *owner() const
1279 {
1280 char *that = const_cast<char *>(reinterpret_cast<const char *>(this));
1281 return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset));
1282 }
1283
1284public:
1285 using value_type = T;
1286 using parameter_type = T;
1287
1288 QObjectComputedProperty() = default;
1289
1290 parameter_type value() const
1291 {
1292 qGetBindingStorage(owner())->registerDependency(this);
1293 return (owner()->*Getter)();
1294 }
1295
1296 std::conditional_t<QTypeTraits::is_dereferenceable_v<T>, parameter_type, void>
1297 operator->() const
1298 {
1299 if constexpr (QTypeTraits::is_dereferenceable_v<T>)
1300 return value();
1301 else
1302 return;
1303 }
1304
1305 parameter_type operator*() const
1306 {
1307 return value();
1308 }
1309
1310 operator parameter_type() const
1311 {
1312 return value();
1313 }
1314
1315 constexpr bool hasBinding() const { return false; }
1316
1317 template<typename Functor>
1318 QPropertyChangeHandler<Functor> onValueChanged(Functor f)
1319 {
1320 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1321 return QPropertyChangeHandler<Functor>(*this, f);
1322 }
1323
1324 template<typename Functor>
1325 QPropertyChangeHandler<Functor> subscribe(Functor f)
1326 {
1327 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1328 f();
1329 return onValueChanged(f);
1330 }
1331
1332 template<typename Functor>
1333 QPropertyNotifier addNotifier(Functor f)
1334 {
1335 static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
1336 return QPropertyNotifier(*this, f);
1337 }
1338
1339 QtPrivate::QPropertyBindingData &bindingData() const
1340 {
1341 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1342 return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
1343 }
1344
1345 void notify() {
1346 // computed property can't store a binding, so there's nothing to mark
1347 auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
1348 auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
1349 if (bd)
1350 bd->notifyObservers(this, qGetBindingStorage(owner()));
1351 }
1352};
1353
1354#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
1355 static constexpr size_t _qt_property_##name##_offset() { \
1356 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
1357 return offsetof(Class, name); \
1358 QT_WARNING_POP \
1359 } \
1360 QObjectComputedProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
1361
1362#undef QT_SOURCE_LOCATION_NAMESPACE
1363
1364QT_END_NAMESPACE
1365
1366#endif // QPROPERTY_H
1367

source code of qtbase/src/corelib/kernel/qproperty.h