diff options
Diffstat (limited to 'src/corelib/kernel/qpropertyprivate.h')
-rw-r--r-- | src/corelib/kernel/qpropertyprivate.h | 428 |
1 files changed, 304 insertions, 124 deletions
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h index 145f2c66b9..86dc08a6bc 100644 --- a/src/corelib/kernel/qpropertyprivate.h +++ b/src/corelib/kernel/qpropertyprivate.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPROPERTYPRIVATE_H #define QPROPERTYPRIVATE_H @@ -44,132 +8,331 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // #include <QtCore/qglobal.h> -#include <QtCore/QExplicitlySharedDataPointer> #include <QtCore/qtaggedpointer.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qcontainerfwd.h> + +#include <functional> QT_BEGIN_NAMESPACE -class QUntypedPropertyBinding; -class QPropertyBindingPrivate; -using QPropertyBindingPrivatePtr = QExplicitlySharedDataPointer<QPropertyBindingPrivate>; -struct QPropertyBasePointer; +class QBindingStorage; + +template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter> +class QObjectCompatProperty; + +struct QBindingObserverPtr; +using PendingBindingObserverList = QVarLengthArray<QBindingObserverPtr>; namespace QtPrivate { +// QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline +// the constructor and copy constructor +struct RefCounted { -class Q_CORE_EXPORT QPropertyBase + int refCount() const { return ref; } + void addRef() { ++ref; } + bool deref() { return --ref != 0; } + +private: + int ref = 0; +}; +} + +class QQmlPropertyBinding; +class QPropertyBindingPrivate; +class QPropertyBindingPrivatePtr { - // Mutable because the address of the observer of the currently evaluating binding is stored here, for - // notification later when the value changes. - mutable quintptr d_ptr = 0; - friend struct QT_PREPEND_NAMESPACE(QPropertyBasePointer); public: - QPropertyBase() = default; - Q_DISABLE_COPY(QPropertyBase) - QPropertyBase(QPropertyBase &&other) = delete; - QPropertyBase(QPropertyBase &&other, void *propertyDataPtr); - QPropertyBase &operator=(QPropertyBase &&other) = delete; - ~QPropertyBase(); + using T = QtPrivate::RefCounted; + T &operator*() const { return *d; } + T *operator->() noexcept { return d; } + T *operator->() const noexcept { return d; } + explicit operator T *() { return d; } + explicit operator const T *() const noexcept { return d; } + T *data() const noexcept { return d; } + T *get() const noexcept { return d; } + const T *constData() const noexcept { return d; } + T *take() noexcept { T *x = d; d = nullptr; return x; } + + QPropertyBindingPrivatePtr() noexcept : d(nullptr) { } + ~QPropertyBindingPrivatePtr() + { + if (d && !d->deref()) + destroyAndFreeMemory(); + } + Q_CORE_EXPORT void destroyAndFreeMemory(); - void moveAssign(QPropertyBase &&other, void *propertyDataPtr); + explicit QPropertyBindingPrivatePtr(T *data) noexcept : d(data) { if (d) d->addRef(); } + QPropertyBindingPrivatePtr(const QPropertyBindingPrivatePtr &o) noexcept + : d(o.d) { if (d) d->addRef(); } - bool hasBinding() const { return d_ptr & BindingBit; } + void reset(T *ptr = nullptr) noexcept; - QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding, - void *propertyDataPtr, void *staticObserver = nullptr, - void (*staticObserverCallback)(void *, void *) = nullptr, - bool (*guardCallback)(void *, void *) = nullptr); - QPropertyBindingPrivate *binding(); + QPropertyBindingPrivatePtr &operator=(const QPropertyBindingPrivatePtr &o) noexcept + { + reset(o.d); + return *this; + } + QPropertyBindingPrivatePtr &operator=(T *o) noexcept + { + reset(o); + return *this; + } + QPropertyBindingPrivatePtr(QPropertyBindingPrivatePtr &&o) noexcept : d(std::exchange(o.d, nullptr)) {} + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPropertyBindingPrivatePtr) + + operator bool () const noexcept { return d != nullptr; } + bool operator!() const noexcept { return d == nullptr; } + + void swap(QPropertyBindingPrivatePtr &other) noexcept + { qt_ptr_swap(d, other.d); } + + friend bool operator==(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept + { return p1.d == p2.d; } + friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept + { return p1.d != p2.d; } + friend bool operator==(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept + { return p1.d == ptr; } + friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept + { return p1.d != ptr; } + friend bool operator==(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept + { return ptr == p2.d; } + friend bool operator!=(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept + { return ptr != p2.d; } + friend bool operator==(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept + { return !p1; } + friend bool operator!=(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept + { return p1; } + friend bool operator==(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept + { return !p2; } + friend bool operator!=(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept + { return p2; } + +private: + QtPrivate::RefCounted *d; +}; - void evaluateIfDirty(); - void removeBinding(); +class QUntypedPropertyBinding; +class QPropertyBindingPrivate; +struct QPropertyBindingDataPointer; +class QPropertyObserver; +struct QPropertyObserverPointer; - void registerWithCurrentlyEvaluatingBinding() const; - void notifyObservers(void *propertyDataPtr); +class QUntypedPropertyData +{ +}; + +namespace QtPrivate { +template <typename T> +using IsUntypedPropertyData = std::enable_if_t<std::is_base_of_v<QUntypedPropertyData, T>, bool>; +} + +template <typename T> +class QPropertyData; + +// Used for grouped property evaluations +namespace QtPrivate { +class QPropertyBindingData; +} +struct QPropertyDelayedNotifications; +struct QPropertyProxyBindingData +{ + // acts as QPropertyBindingData::d_ptr + quintptr d_ptr; + /* + The two members below store the original binding data and property + data pointer of the property which gets proxied. + They are set in QPropertyDelayedNotifications::addProperty + */ + const QtPrivate::QPropertyBindingData *originalBindingData; + QUntypedPropertyData *propertyData; +}; - void setExtraBit(bool b) +namespace QtPrivate { +struct BindingEvaluationState; + +/* used in BindingFunctionVTable::createFor; on all other compilers, void would work, but on + MSVC this causes C2182 when compiling in C++20 mode. As we only need to provide some default + value which gets ignored, we introduce this dummy type. +*/ +struct MSVCWorkAround {}; + +struct BindingFunctionVTable +{ + using CallFn = bool(*)(QMetaType, QUntypedPropertyData *, void *); + using DtorFn = void(*)(void *); + using MoveCtrFn = void(*)(void *, void *); + const CallFn call; + const DtorFn destroy; + const MoveCtrFn moveConstruct; + const qsizetype size; + + template<typename Callable, typename PropertyType=MSVCWorkAround> + static constexpr BindingFunctionVTable createFor() { - if (b) - d_ptr |= ExtraBit; - else - d_ptr &= ~ExtraBit; + static_assert (alignof(Callable) <= alignof(std::max_align_t), "Bindings do not support overaligned functors!"); + return { + /*call=*/[](QMetaType metaType, QUntypedPropertyData *dataPtr, void *f){ + if constexpr (!std::is_invocable_v<Callable>) { + // we got an untyped callable + static_assert (std::is_invocable_r_v<bool, Callable, QMetaType, QUntypedPropertyData *> ); + auto untypedEvaluationFunction = static_cast<Callable *>(f); + return std::invoke(*untypedEvaluationFunction, metaType, dataPtr); + } else if constexpr (!std::is_same_v<PropertyType, MSVCWorkAround>) { + Q_UNUSED(metaType); + QPropertyData<PropertyType> *propertyPtr = static_cast<QPropertyData<PropertyType> *>(dataPtr); + // That is allowed by POSIX even if Callable is a function pointer + auto evaluationFunction = static_cast<Callable *>(f); + PropertyType newValue = std::invoke(*evaluationFunction); + if constexpr (QTypeTraits::has_operator_equal_v<PropertyType>) { + if (newValue == propertyPtr->valueBypassingBindings()) + return false; + } + propertyPtr->setValueBypassingBindings(std::move(newValue)); + return true; + } else { + // Our code will never instantiate this + Q_UNREACHABLE_RETURN(false); + } + }, + /*destroy*/[](void *f){ static_cast<Callable *>(f)->~Callable(); }, + /*moveConstruct*/[](void *addr, void *other){ + new (addr) Callable(std::move(*static_cast<Callable *>(other))); + }, + /*size*/sizeof(Callable) + }; } +}; - bool extraBit() const { return d_ptr & ExtraBit; } +template<typename Callable, typename PropertyType=MSVCWorkAround> +inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>(); - static const quintptr ExtraBit = 0x1; // Used for QProperty<bool> specialization - static const quintptr BindingBit = 0x2; // Is d_ptr pointing to a binding (1) or list of notifiers (0)? - static const quintptr FlagMask = BindingBit | ExtraBit; + +// writes binding result into dataPtr +struct QPropertyBindingFunction { + const QtPrivate::BindingFunctionVTable *vtable; + void *functor; }; -template <typename T> -struct QPropertyValueStorage +using QPropertyObserverCallback = void (*)(QUntypedPropertyData *); +using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction); + +/*! + \internal + A property normally consists of the actual property value and metadata for the binding system. + QPropertyBindingData is the latter part. It stores a pointer to either + - a (potentially empty) linked list of notifiers, in case there is no binding set, + - an actual QUntypedPropertyBinding when the property has a binding, + - or a pointer to QPropertyProxyBindingData when notifications occur inside a grouped update. + + \sa QPropertyDelayedNotifications, beginPropertyUpdateGroup + */ +class Q_CORE_EXPORT QPropertyBindingData { -private: - T value; + // Mutable because the address of the observer of the currently evaluating binding is stored here, for + // notification later when the value changes. + mutable quintptr d_ptr = 0; + friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer); + friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding); + friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications); + + template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter> + friend class QT_PREPEND_NAMESPACE(QObjectCompatProperty); + + Q_DISABLE_COPY(QPropertyBindingData) public: - QPropertyBase priv; + QPropertyBindingData() = default; + QPropertyBindingData(QPropertyBindingData &&other); + QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete; + ~QPropertyBindingData(); - QPropertyValueStorage() : value() {} - Q_DISABLE_COPY(QPropertyValueStorage) - explicit QPropertyValueStorage(const T &initialValue) : value(initialValue) {} - QPropertyValueStorage &operator=(const T &newValue) { value = newValue; return *this; } - explicit QPropertyValueStorage(T &&initialValue) : value(std::move(initialValue)) {} - QPropertyValueStorage &operator=(T &&newValue) { value = std::move(newValue); return *this; } - QPropertyValueStorage(QPropertyValueStorage &&other) : value(std::move(other.value)), priv(std::move(other.priv), this) {} - QPropertyValueStorage &operator=(QPropertyValueStorage &&other) { value = std::move(other.value); priv.moveAssign(std::move(other.priv), &value); return *this; } + // Is d_ptr pointing to a binding (1) or list of notifiers (0)? + static inline constexpr quintptr BindingBit = 0x1; + // Is d_ptr pointing to QPropertyProxyBindingData (1) or to an actual binding/list of notifiers? + static inline constexpr quintptr DelayedNotificationBit = 0x2; - T getValue() const { return value; } - bool setValueAndReturnTrueIfChanged(T &&v) - { - if (v == value) - return false; - value = std::move(v); - return true; - } - bool setValueAndReturnTrueIfChanged(const T &v) + bool hasBinding() const { return d_ptr & BindingBit; } + bool isNotificationDelayed() const { return d_ptr & DelayedNotificationBit; } + + QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding, + QUntypedPropertyData *propertyDataPtr, + QPropertyObserverCallback staticObserverCallback = nullptr, + QPropertyBindingWrapper bindingWrapper = nullptr); + + QPropertyBindingPrivate *binding() const { - if (v == value) - return false; - value = v; - return true; + quintptr dd = d(); + if (dd & BindingBit) + return reinterpret_cast<QPropertyBindingPrivate*>(dd - BindingBit); + return nullptr; + } -}; -template<> -struct QPropertyValueStorage<bool> -{ - QPropertyBase priv; + void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused - QPropertyValueStorage() = default; - Q_DISABLE_COPY(QPropertyValueStorage) - explicit QPropertyValueStorage(bool initialValue) { priv.setExtraBit(initialValue); } - QPropertyValueStorage &operator=(bool newValue) { priv.setExtraBit(newValue); return *this; } - QPropertyValueStorage(QPropertyValueStorage &&other) : priv(std::move(other.priv), this) {} - QPropertyValueStorage &operator=(QPropertyValueStorage &&other) { priv.moveAssign(std::move(other.priv), this); return *this; } + void removeBinding() + { + if (hasBinding()) + removeBinding_helper(); + } - bool getValue() const { return priv.extraBit(); } - bool setValueAndReturnTrueIfChanged(bool v) + void registerWithCurrentlyEvaluatingBinding(QtPrivate::BindingEvaluationState *currentBinding) const + { + if (!currentBinding) + return; + registerWithCurrentlyEvaluatingBinding_helper(currentBinding); + } + void registerWithCurrentlyEvaluatingBinding() const; + void notifyObservers(QUntypedPropertyData *propertyDataPtr) const; + void notifyObservers(QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage) const; +private: + /*! + \internal + Returns a reference to d_ptr, except when d_ptr points to a proxy. + In that case, a reference to proxy->d_ptr is returned instead. + + To properly support proxying, direct access to d_ptr only occurs when + - a function actually deals with proxying (e.g. + QPropertyDelayedNotifications::addProperty), + - only the tag value is accessed (e.g. hasBinding) or + - inside a constructor. + */ + quintptr &d_ref() const { - if (v == priv.extraBit()) - return false; - priv.setExtraBit(v); - return true; + quintptr &d = d_ptr; + if (isNotificationDelayed()) + return proxyData()->d_ptr; + return d; } + quintptr d() const { return d_ref(); } + QPropertyProxyBindingData *proxyData() const + { + Q_ASSERT(isNotificationDelayed()); + return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit)); + } + void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const; + void removeBinding_helper(); + + enum NotificationResult { Delayed, Evaluated }; + NotificationResult notifyObserver_helper( + QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage, + QPropertyObserverPointer observer, + PendingBindingObserverList &bindingObservers) const; }; template <typename T, typename Tag> class QTagPreservingPointerToPointer { public: - QTagPreservingPointerToPointer() = default; + constexpr QTagPreservingPointerToPointer() = default; QTagPreservingPointerToPointer(T **ptr) : d(reinterpret_cast<quintptr*>(ptr)) @@ -177,13 +340,13 @@ public: QTagPreservingPointerToPointer<T, Tag> &operator=(T **ptr) { - d = reinterpret_cast<quintptr*>(ptr); + d = reinterpret_cast<quintptr *>(ptr); return *this; } QTagPreservingPointerToPointer<T, Tag> &operator=(QTaggedPointer<T, Tag> *ptr) { - d = reinterpret_cast<quintptr*>(ptr); + d = reinterpret_cast<quintptr *>(ptr); return *this; } @@ -194,7 +357,7 @@ public: void setPointer(T *ptr) { - *d = (reinterpret_cast<quintptr>(ptr) & QTaggedPointer<T, Tag>::pointerMask()) | (*d & QTaggedPointer<T, Tag>::tagMask()); + *d = reinterpret_cast<quintptr>(ptr) | (*d & QTaggedPointer<T, Tag>::tagMask()); } T *get() const @@ -211,6 +374,23 @@ private: quintptr *d = nullptr; }; +namespace detail { + template <typename F> + struct ExtractClassFromFunctionPointer; + + template<typename T, typename C> + struct ExtractClassFromFunctionPointer<T C::*> { using Class = C; }; + + constexpr size_t getOffset(size_t o) + { + return o; + } + constexpr size_t getOffset(size_t (*offsetFn)()) + { + return offsetFn(); + } +} + } // namespace QtPrivate QT_END_NAMESPACE |