summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2023-04-27 14:53:40 +0200
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2023-04-28 07:45:22 +0200
commitbd69821074d62a6e8b5eca56d7b9307e1b3e8645 (patch)
treed4cb51f5b9f97d6e06d94aad01a30a6e33abaa66 /tests/auto/corelib/kernel/qobject/tst_qobject.cpp
parent10a850f584f2a3276737ca7621f380ebfd29b4ed (diff)
Support free functions and const functors as callbacks
Amend 207aae5560aa2865ec55ddb9ecbb50048060c0c0, as code checker complained that we std::move'd a potential lvalue. This warning was valid if the public API did not accept the functor parameter by value. Fix this by consistently std::forward'ing the parameters through the call stack, and add a compile-time test. Writing that test revealed that the helper API didn't work with free functions, so fix that as well. It also revealed that QFunctorSlotObject couldn't work with a const functor, which is also fixed by this change. We cannot support move-only functors with that change, as it requires a change to QFunctorSlotObject that breaks the QMetaObject test. Change-Id: Iafd747baf4cb0213ecedb391ed46b4595388182b Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'tests/auto/corelib/kernel/qobject/tst_qobject.cpp')
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp148
1 files changed, 148 insertions, 0 deletions
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index 0877637a50..9c18a2a0f6 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -149,6 +149,7 @@ private slots:
void objectNameBinding();
void emitToDestroyedClass();
void declarativeData();
+ void asyncCallbackHelper();
};
struct QObjectCreatedOnShutdown
@@ -8355,5 +8356,152 @@ void tst_QObject::declarativeData()
#endif
}
+/*
+ Compile-time test for the helpers in qobjectdefs_impl.h.
+*/
+class AsyncCaller : public QObject
+{
+ Q_OBJECT
+public:
+ void callback0() {}
+ void callback1(const QString &) {}
+ void callbackInt(int) {}
+ int returnInt() const { return 0; }
+
+ static void staticCallback0() {}
+ static void staticCallback1(const QString &) {}
+
+ using Prototype0 = void(*)();
+ using Prototype1 = void(*)(QString);
+
+ template<typename Functor>
+ bool callMe0(const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *, Functor &&func)
+ {
+ auto *slotObject = QtPrivate::makeSlotObject<Prototype0>(std::forward<Functor>(func));
+ slotObject->destroyIfLastRef();
+ return true;
+ }
+
+ template<typename Functor>
+ bool callMe0(Functor &&func)
+ {
+ return callMe0(nullptr, std::forward<Functor>(func));
+ }
+
+ template<typename Functor>
+ bool callMe1(const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *, Functor &&func)
+ {
+ auto *slotObject = QtPrivate::makeSlotObject<Prototype1>(std::forward<Functor>(func));
+ slotObject->destroyIfLastRef();
+ return true;
+ }
+
+ template<typename Functor>
+ bool callMe1(Functor &&func)
+ {
+ return callMe1(nullptr, std::forward<Functor>(func));
+ }
+};
+
+static void freeFunction0() {}
+static void freeFunction1(QString) {}
+
+template<typename Prototype, typename Functor, typename = void>
+struct AreFunctionsCompatible : std::false_type {};
+template<typename Prototype, typename Functor>
+struct AreFunctionsCompatible<Prototype, Functor, std::enable_if_t<
+ std::is_same_v<decltype(QtPrivate::makeSlotObject<Prototype>(std::forward<Functor>(std::declval<Functor>()))),
+ QtPrivate::QSlotObjectBase *>>
+> : std::true_type {};
+
+template<typename Prototype, typename Functor>
+inline constexpr bool compiles(Functor &&) {
+ return QtPrivate::AreFunctionsCompatible<Prototype, Functor>::value;
+}
+
+void tst_QObject::asyncCallbackHelper()
+{
+ auto lambda0 = []{};
+ auto lambda1 = [](const QString &) {};
+ auto lambda2 = [](const QString &, int) {};
+ const auto constLambda = [](const QString &) {};
+ auto moveOnlyLambda = [u = std::unique_ptr<int>()]{};
+
+ SlotFunctor functor0;
+ SlotFunctorString functor1;
+
+ // no parameters provided or needed
+ static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::callback0));
+ static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::staticCallback0));
+ static_assert(compiles<AsyncCaller::Prototype0>(lambda0));
+ static_assert(compiles<AsyncCaller::Prototype0>(freeFunction0));
+ static_assert(compiles<AsyncCaller::Prototype0>(functor0));
+
+ // more parameters than needed
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::callback0));
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::staticCallback0));
+ static_assert(compiles<AsyncCaller::Prototype1>(lambda0));
+ static_assert(compiles<AsyncCaller::Prototype1>(freeFunction0));
+ static_assert(compiles<AsyncCaller::Prototype1>(functor0));
+
+ // matching parameter
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::callback1));
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::staticCallback1));
+ static_assert(compiles<AsyncCaller::Prototype1>(lambda1));
+ static_assert(compiles<AsyncCaller::Prototype1>(constLambda));
+ static_assert(compiles<AsyncCaller::Prototype1>(freeFunction1));
+ static_assert(compiles<AsyncCaller::Prototype1>(functor1));
+
+ // not enough parameters
+ static_assert(!compiles<AsyncCaller::Prototype0>(&AsyncCaller::callback1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(&AsyncCaller::staticCallback1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(lambda1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(constLambda));
+ static_assert(!compiles<AsyncCaller::Prototype0>(lambda2));
+ static_assert(!compiles<AsyncCaller::Prototype0>(freeFunction1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(functor1));
+
+ // move-only functor - should work, but doesn't because QFunctorSlotObject requires
+ // the functor to be of a copyable type!
+ static_assert(!compiles<AsyncCaller::Prototype0>(moveOnlyLambda));
+ static_assert(!compiles<AsyncCaller::Prototype1>(moveOnlyLambda));
+
+ // wrong parameter type
+ static_assert(!compiles<AsyncCaller::Prototype1>(&AsyncCaller::callbackInt));
+
+ // old-style slot name
+ static_assert(!compiles<AsyncCaller::Prototype0>("callback1"));
+
+ // slot with return value is ok, we just don't pass
+ // the return value through to anything.
+ static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::returnInt));
+
+ AsyncCaller caller;
+ // with context
+ QVERIFY(caller.callMe0(&caller, &AsyncCaller::callback0));
+ QVERIFY(caller.callMe0(&caller, &AsyncCaller::returnInt));
+ QVERIFY(caller.callMe0(&caller, &AsyncCaller::staticCallback0));
+ QVERIFY(caller.callMe0(&caller, lambda0));
+ QVERIFY(caller.callMe0(&caller, freeFunction0));
+// QVERIFY(caller.callMe0(&caller, moveOnlyLambda));
+
+ QVERIFY(caller.callMe1(&caller, &AsyncCaller::callback1));
+ QVERIFY(caller.callMe1(&caller, &AsyncCaller::staticCallback1));
+ QVERIFY(caller.callMe1(&caller, lambda1));
+ QVERIFY(caller.callMe1(&caller, freeFunction1));
+ QVERIFY(caller.callMe1(&caller, constLambda));
+
+ // without context
+ QVERIFY(caller.callMe0(&AsyncCaller::staticCallback0));
+ QVERIFY(caller.callMe0(lambda0));
+ QVERIFY(caller.callMe0(freeFunction0));
+// QVERIFY(caller.callMe0(moveOnlyLambda));
+
+ QVERIFY(caller.callMe1(&AsyncCaller::staticCallback1));
+ QVERIFY(caller.callMe1(lambda1));
+ QVERIFY(caller.callMe1(constLambda));
+ QVERIFY(caller.callMe1(freeFunction1));
+}
+
QTEST_MAIN(tst_QObject)
#include "tst_qobject.moc"