summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/kernel/qproperty.h32
-rw-r--r--src/corelib/kernel/qpropertybinding.cpp6
-rw-r--r--src/corelib/kernel/qpropertybinding_p.h58
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp47
4 files changed, 129 insertions, 14 deletions
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index e2a6172d84..f0156fac02 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -590,6 +590,17 @@ public:
{
setSource(property);
}
+
+ template <typename PropertyType, typename Class, void(Class::*Callback)()>
+ QPropertyChangeHandler(const QNotifiedProperty<PropertyType, Callback> &property, Functor handler)
+ : QPropertyObserver([](QPropertyObserver *self, void *) {
+ auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
+ This->m_handler();
+ })
+ , m_handler(handler)
+ {
+ setSource(property);
+ }
};
template <typename T>
@@ -613,6 +624,27 @@ QPropertyChangeHandler<Functor> QProperty<T>::subscribe(Functor f)
return onValueChanged(f);
}
+template <typename T, typename Class, void(Class::*Callback)()>
+template<typename Functor>
+QPropertyChangeHandler<Functor> QNotifiedProperty<T, Callback>::onValueChanged(Functor f)
+{
+#if defined(__cpp_lib_is_invocable) && (__cpp_lib_is_invocable >= 201703L)
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+#endif
+ return QPropertyChangeHandler<Functor>(*this, f);
+}
+
+template <typename T, typename Class, void(Class::*Callback)()>
+template<typename Functor>
+QPropertyChangeHandler<Functor> QNotifiedProperty<T, Callback>::subscribe(Functor f)
+{
+#if defined(__cpp_lib_is_invocable) && (__cpp_lib_is_invocable >= 201703L)
+ static_assert(std::is_invocable_v<Functor>, "Functor callback must be callable without any parameters");
+#endif
+ f();
+ return onValueChanged(f);
+}
+
template <auto propertyMember, auto callbackMember>
struct QPropertyMemberChangeHandler;
diff --git a/src/corelib/kernel/qpropertybinding.cpp b/src/corelib/kernel/qpropertybinding.cpp
index 55f2fe913d..9c46d63998 100644
--- a/src/corelib/kernel/qpropertybinding.cpp
+++ b/src/corelib/kernel/qpropertybinding.cpp
@@ -51,6 +51,8 @@ QPropertyBindingPrivate::~QPropertyBindingPrivate()
{
if (firstObserver)
firstObserver.unlink();
+ if (!hasStaticObserver)
+ inlineDependencyObservers.~ObserverArray(); // Explicit because of union.
}
void QPropertyBindingPrivate::unlinkAndDeref()
@@ -63,10 +65,10 @@ void QPropertyBindingPrivate::unlinkAndDeref()
void QPropertyBindingPrivate::markDirtyAndNotifyObservers()
{
dirty = true;
- if (staticObserver)
- staticObserverCallback(staticObserver);
if (firstObserver)
firstObserver.notify(this, propertyDataPtr);
+ if (hasStaticObserver)
+ staticObserverCallback(staticObserver);
}
bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged()
diff --git a/src/corelib/kernel/qpropertybinding_p.h b/src/corelib/kernel/qpropertybinding_p.h
index a2c733abd9..e03a23109a 100644
--- a/src/corelib/kernel/qpropertybinding_p.h
+++ b/src/corelib/kernel/qpropertybinding_p.h
@@ -66,14 +66,24 @@ class Q_CORE_EXPORT QPropertyBindingPrivate : public QSharedData
private:
friend struct QPropertyBasePointer;
+ using ObserverArray = std::array<QPropertyObserver, 4>;
+
+ // QSharedData is 4 bytes. Use the padding for the bools as we need 8 byte alignment below.
+ bool dirty = false;
+ bool updating = false;
+ bool hasStaticObserver = false;
+
QUntypedPropertyBinding::BindingEvaluationFunction evaluationFunction;
QPropertyObserverPointer firstObserver;
- std::array<QPropertyObserver, 4> inlineDependencyObservers;
+ union {
+ ObserverArray inlineDependencyObservers;
+ struct {
+ void *staticObserver;
+ void (*staticObserverCallback)(void*);
+ };
+ };
QScopedPointer<std::vector<QPropertyObserver>> heapObservers;
- // ### merge with inline dependency observer storage
- void *staticObserver = nullptr;
- void (*staticObserverCallback)(void*) = nullptr;
void *propertyDataPtr = nullptr;
@@ -82,9 +92,6 @@ private:
QMetaType metaType;
- bool dirty = false;
- bool updating = false;
-
public:
// public because the auto-tests access it, too.
size_t dependencyObserverCount = 0;
@@ -92,6 +99,7 @@ public:
QPropertyBindingPrivate(const QMetaType &metaType, QUntypedPropertyBinding::BindingEvaluationFunction evaluationFunction,
const QPropertyBindingSourceLocation &location)
: evaluationFunction(std::move(evaluationFunction))
+ , inlineDependencyObservers() // Explicit initialization required because of union
, location(location)
, metaType(metaType)
{}
@@ -99,7 +107,31 @@ public:
void setDirty(bool d) { dirty = d; }
void setProperty(void *propertyPtr) { propertyDataPtr = propertyPtr; }
- void setStaticObserver(void *observer, void (*callback)(void*)) { staticObserver = observer; staticObserverCallback = callback; }
+ void setStaticObserver(void *observer, void (*callback)(void*))
+ {
+ if (observer) {
+ if (!hasStaticObserver) {
+ if (dependencyObserverCount > 0) {
+ if (!heapObservers)
+ heapObservers.reset(new std::vector<QPropertyObserver>());
+ for (int i = 0, end = qMin(dependencyObserverCount, inlineDependencyObservers.size()); i < end; ++i)
+ heapObservers->push_back(std::move(inlineDependencyObservers[i]));
+ }
+ inlineDependencyObservers.~ObserverArray();
+ }
+
+ hasStaticObserver = true;
+ staticObserver = observer;
+ staticObserverCallback = callback;
+ } else if (hasStaticObserver) {
+ hasStaticObserver = false;
+ new (&inlineDependencyObservers) ObserverArray();
+ for (int i = 0, end = qMin(dependencyObserverCount, inlineDependencyObservers.size()); i < end; ++i) {
+ inlineDependencyObservers[i] = std::move(heapObservers->back());
+ heapObservers->pop_back();
+ }
+ }
+ }
void prependObserver(QPropertyObserverPointer observer) {
observer.ptr->prev = const_cast<QPropertyObserver **>(&firstObserver.ptr);
firstObserver = observer;
@@ -113,16 +145,18 @@ public:
}
void clearDependencyObservers() {
- for (size_t i = 0; i < inlineDependencyObservers.size(); ++i) {
- QPropertyObserver empty;
- qSwap(inlineDependencyObservers[i], empty);
+ if (!hasStaticObserver) {
+ for (size_t i = 0; i < inlineDependencyObservers.size(); ++i) {
+ QPropertyObserver empty;
+ qSwap(inlineDependencyObservers[i], empty);
+ }
}
if (heapObservers)
heapObservers->clear();
dependencyObserverCount = 0;
}
QPropertyObserverPointer allocateDependencyObserver() {
- if (dependencyObserverCount < inlineDependencyObservers.size()) {
+ if (!hasStaticObserver && dependencyObserverCount < inlineDependencyObservers.size()) {
++dependencyObserverCount;
return {&inlineDependencyObservers[dependencyObserverCount - 1]};
}
diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
index 394438ab88..e606d121ec 100644
--- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
+++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
@@ -782,29 +782,76 @@ struct ClassWithNotifiedProperty
void tst_QProperty::notifiedProperty()
{
ClassWithNotifiedProperty instance;
+ std::array<QProperty<int>, 5> otherProperties = {
+ QProperty<int>([&]() { return instance.property + 1; }),
+ QProperty<int>([&]() { return instance.property + 2; }),
+ QProperty<int>([&]() { return instance.property + 3; }),
+ QProperty<int>([&]() { return instance.property + 4; }),
+ QProperty<int>([&]() { return instance.property + 5; }),
+ };
+
+ auto check = [&] {
+ const int val = instance.property.value();
+ for (int i = 0; i < int(otherProperties.size()); ++i)
+ QCOMPARE(otherProperties[i].value(), val + i + 1);
+ };
+
QVERIFY(instance.recordedValues.isEmpty());
+ check();
instance.property.setValue(&instance, 42);
QCOMPARE(instance.recordedValues.count(), 1);
QCOMPARE(instance.recordedValues.at(0), 42);
instance.recordedValues.clear();
+ check();
instance.property.setValue(&instance, 42);
QVERIFY(instance.recordedValues.isEmpty());
+ check();
+ int subscribedCount = 0;
QProperty<int> injectedValue(100);
instance.property.setBinding(&instance, [&injectedValue]() { return injectedValue.value(); });
+ auto subscriber = [&] { ++subscribedCount; };
+ std::array<QPropertyChangeHandler<decltype (subscriber)>, 10> subscribers = {
+ instance.property.subscribe(subscriber),
+ instance.property.subscribe(subscriber),
+ instance.property.subscribe(subscriber),
+ instance.property.subscribe(subscriber),
+ instance.property.subscribe(subscriber),
+ instance.property.subscribe(subscriber),
+ instance.property.subscribe(subscriber),
+ instance.property.subscribe(subscriber),
+ instance.property.subscribe(subscriber),
+ instance.property.subscribe(subscriber)
+ };
+
+ QCOMPARE(subscribedCount, 10);
+ subscribedCount = 0;
QCOMPARE(instance.property.value(), 100);
QCOMPARE(instance.recordedValues.count(), 1);
QCOMPARE(instance.recordedValues.at(0), 100);
instance.recordedValues.clear();
+ check();
+ QCOMPARE(subscribedCount, 0);
injectedValue = 200;
QCOMPARE(instance.property.value(), 200);
QCOMPARE(instance.recordedValues.count(), 1);
QCOMPARE(instance.recordedValues.at(0), 200);
instance.recordedValues.clear();
+ check();
+ QCOMPARE(subscribedCount, 10);
+ subscribedCount = 0;
+
+ injectedValue = 400;
+ QCOMPARE(instance.property.value(), 400);
+ QCOMPARE(instance.recordedValues.count(), 1);
+ QCOMPARE(instance.recordedValues.at(0), 400);
+ instance.recordedValues.clear();
+ check();
+ QCOMPARE(subscribedCount, 10);
}
QTEST_MAIN(tst_QProperty);