diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2020-10-16 18:03:28 +0200 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2020-11-03 13:06:14 +0100 |
commit | c1c991c3190ec3e9ba5fae9c5ae40385686d289d (patch) | |
tree | 466ef8af8fe0ef6f6336b1cf503f67d6ede6f633 /src/corelib/kernel/qproperty_p.h | |
parent | a95ddcf97bcb3c5a6727fcaf6b3b74c05051ac4f (diff) |
Remove std::function from QProperty interface
std::function as a type is rather unfortunate for us, as its SSO buffer
makes it rather large, and we can ensure that the function is never
empty.
Considering that we do need to allocate memory for
QPropertyBindingPrivate anyway, we can get rid of the SSO buffer and
instead coalesce the allocations (similar to how std::make_shared works).
The memory looks then like
[--QPropertyBindingPrivate--][Functor]
and QPropertyBindingPrivate can get a pointer to the functor via
reinterpret_cast<std::byte>(this)+sizeof(QPropertyBindingPrivate).
To actually do anything with the functor, we do however need a "vtable"
which describes how we can call, destroy and move the functor. This is
done by creating a constexpr struct of function pointers, and storing a
pointer to it in QPropertyBindingPrivate.
As a consequence of those changes, we cannot use QESDP anymore, as we
now have to carefully deallocate the buffer we used for both the
QPropertyBindingPrivate and the functor. We introduce a custom
refcounting pointer for that. While we're at it, we make the refcount
non-atomic, as bindings do not work across threads to begin with.
Moreover, we can now make the class non-virtual, as that was only needed
to hack around limitations of QESDP in the context of exported symbols.
Change-Id: Idc5507e4c120e28df5bd5aea717fe69f15e540dc
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/corelib/kernel/qproperty_p.h')
-rw-r--r-- | src/corelib/kernel/qproperty_p.h | 42 |
1 files changed, 31 insertions, 11 deletions
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index fdb5f14ccd..99a145c65a 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -54,11 +54,9 @@ #include <qglobal.h> #include <qproperty.h> -#include <qvarlengtharray.h> #include <qscopedpointer.h> #include <vector> - QT_BEGIN_NAMESPACE // Keep all classes related to QProperty in one compilation unit. Performance of this code is crucial and @@ -156,14 +154,15 @@ struct QBindingStatus QtPrivate::CurrentCompatProperty *currentCompatProperty = nullptr; }; -class Q_CORE_EXPORT QPropertyBindingPrivate : public QSharedData +class Q_CORE_EXPORT QPropertyBindingPrivate : public QtPrivate::RefCounted { private: friend struct QPropertyBindingDataPointer; + friend class QPropertyBindingPrivatePtr; using ObserverArray = std::array<QPropertyObserver, 4>; - // QSharedData is 4 bytes. Use the padding for the bools as we need 8 byte alignment below. +private: // a dependent property has changed, and the binding needs to be reevaluated on access bool dirty = false; @@ -174,7 +173,7 @@ private: // used to detect binding loops for eagerly evaluated properties bool eagerlyUpdating:1; - QUntypedPropertyBinding::BindingEvaluationFunction evaluationFunction; + const QtPrivate::BindingFunctionVTable *vtable; union { QtPrivate::QPropertyObserverCallback staticObserverCallback = nullptr; @@ -193,19 +192,29 @@ private: QMetaType metaType; public: + static constexpr size_t getSizeEnsuringAlignment() { + constexpr auto align = alignof (std::max_align_t) - 1; + constexpr size_t sizeEnsuringAlignment = (sizeof(QPropertyBindingPrivate) + align) & ~align; + static_assert (sizeEnsuringAlignment % alignof (std::max_align_t) == 0, + "Required for placement new'ing the function behind it."); + return sizeEnsuringAlignment; + } + + // public because the auto-tests access it, too. size_t dependencyObserverCount = 0; - QPropertyBindingPrivate(QMetaType metaType, QUntypedPropertyBinding::BindingEvaluationFunction evaluationFunction, + QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable, const QPropertyBindingSourceLocation &location) : hasBindingWrapper(false) , eagerlyUpdating(false) - , evaluationFunction(std::move(evaluationFunction)) + , vtable(vtable) , inlineDependencyObservers() // Explicit initialization required because of union , location(location) , metaType(metaType) {} - virtual ~QPropertyBindingPrivate(); + ~QPropertyBindingPrivate(); + void setDirty(bool d) { dirty = d; } void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; } @@ -277,7 +286,7 @@ public: bool evaluateIfDirtyAndReturnTrueIfValueChanged(const QUntypedPropertyData *data); static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding) - { return binding.d.data(); } + { return static_cast<QPropertyBindingPrivate *>(binding.d.data()); } void setError(QPropertyBindingError &&e) { error = std::move(e); } @@ -293,6 +302,17 @@ public: bool requiresEagerEvaluation() const { return hasBindingWrapper; } static QPropertyBindingPrivate *currentlyEvaluatingBinding(); + + static void destroyAndFreeMemory(QPropertyBindingPrivate *priv) { + if (priv->vtable->size == 0) { + // special hack for QQmlPropertyBinding which has a + // different memory layout than normal QPropertyBindings + priv->vtable->destroy(priv); + } else{ + priv->~QPropertyBindingPrivate(); + delete[] reinterpret_cast<std::byte *>(priv); + } + } }; inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer) @@ -337,11 +357,11 @@ class QObjectCompatProperty : public QPropertyData<T> char *that = const_cast<char *>(reinterpret_cast<const char *>(this)); return reinterpret_cast<Class *>(that - QtPrivate::detail::getOffset(Offset)); } - static bool bindingWrapper(QMetaType type, QUntypedPropertyData *dataPtr, const QtPrivate::QPropertyBindingFunction &binding) + static bool bindingWrapper(QMetaType type, QUntypedPropertyData *dataPtr, QtPrivate::QPropertyBindingFunction binding) { auto *thisData = static_cast<ThisType *>(dataPtr); QPropertyData<T> copy; - binding(type, ©); + binding.vtable->call(type, ©, binding.functor); if constexpr (QTypeTraits::has_operator_equal_v<T>) if (copy.valueBypassingBindings() == thisData->valueBypassingBindings()) return false; |