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/qpropertyprivate.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/qpropertyprivate.h')
-rw-r--r-- | src/corelib/kernel/qpropertyprivate.h | 138 |
1 files changed, 135 insertions, 3 deletions
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h index a0dfbd97c9..df12c1fdb3 100644 --- a/src/corelib/kernel/qpropertyprivate.h +++ b/src/corelib/kernel/qpropertyprivate.h @@ -60,9 +60,87 @@ QT_BEGIN_NAMESPACE +namespace QtPrivate { +// QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline +// the constructor and copy constructor +struct RefCounted { + int ref = 0; + void addRef() {++ref;} + bool deref() {--ref; return ref;} +}; +} + +class QPropertyBindingPrivate; +class QPropertyBindingPrivatePtr +{ +public: + 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) { } + Q_CORE_EXPORT ~QPropertyBindingPrivatePtr(); + + explicit QPropertyBindingPrivatePtr(T *data) noexcept : d(data) { if (d) d->addRef(); } + QPropertyBindingPrivatePtr(const QPropertyBindingPrivatePtr &o) noexcept + : d(o.d) { if (d) d->addRef(); } + + void reset(T *ptr = nullptr) noexcept; + + 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(qExchange(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 + { qSwap(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; +}; + + class QUntypedPropertyBinding; class QPropertyBindingPrivate; -using QPropertyBindingPrivatePtr = QExplicitlySharedDataPointer<QPropertyBindingPrivate>; struct QPropertyBindingDataPointer; class QUntypedPropertyData @@ -72,12 +150,66 @@ public: struct InheritsQUntypedPropertyData {}; }; +template <typename T> +class QPropertyData; + namespace QtPrivate { +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=void> + static constexpr BindingFunctionVTable createFor() + { + 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 { + 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; + } + }, + /*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) + }; + } +}; + +template<typename Callable, typename PropertyType=void> +inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>(); + + // writes binding result into dataPtr -using QPropertyBindingFunction = std::function<bool(QMetaType metaType, QUntypedPropertyData *dataPtr)>; +struct QPropertyBindingFunction { + const QtPrivate::BindingFunctionVTable *vtable; + void *functor; +}; + using QPropertyObserverCallback = void (*)(QUntypedPropertyData *); -using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, const QPropertyBindingFunction &); +using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction); class Q_CORE_EXPORT QPropertyBindingData { |