summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2023-05-11 00:54:13 +0200
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2023-05-11 19:36:55 +0200
commit9c9c5d98281983578560ace7e8a3e3c684289177 (patch)
treefa3eb7ab00f77b1fc8ee375000d1c31d46538ab8
parentaf8e75f54f54252cbb662eeb77697bdd8ec07296 (diff)
downloadqtbase-9c9c5d98281983578560ace7e8a3e3c684289177.tar.gz
Fix QMetaObject::invokeMethod for free functions and std::bind
Amends 3bf5b5f8944dd417530b09dd6f1cd568717c8cc1, after which free functions and std::bind could no longer be used as callables in QMetaMethod::invokeMethod. For free functions to work we need to decay to function pointers when choosing what type QtPrivate::Callable aliases. And std::bind has operator() overloads and the return type cannot be deduced. So simplify the definition of the ZeroArgFunctor - we know the function prototype if we know the return type. Add testcase for calling std::bind and free function, and remove the now unneeded helpers for functor argument and return type deduction. Change-Id: I54aac5cb6d660267e6b2f5ab05d583e8826cdf9a Reviewed-by: Zoltan Gera <zoltan.gera@qt.io> Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
-rw-r--r--src/corelib/kernel/qobjectdefs.h2
-rw-r--r--src/corelib/kernel/qobjectdefs_impl.h24
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp45
3 files changed, 52 insertions, 19 deletions
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index 1b074cea44..5c1d2fd8ae 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -419,6 +419,8 @@ struct Q_CORE_EXPORT QMetaObject
Qt::ConnectionType type = Qt::AutoConnection,
typename QtPrivate::Callable<Func>::ReturnType *ret = nullptr)
{
+ static_assert(QtPrivate::Callable<Func>::ArgumentCount <= 0,
+ "QMetaObject::invokeMethod cannot call functions with arguments!");
using Prototype = typename QtPrivate::Callable<Func>::Function;
return invokeMethodImpl(object, QtPrivate::makeCallableObject<Prototype>(std::forward<Func>(function)), type, ret);
}
diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h
index 72193cc9da..33df3624d2 100644
--- a/src/corelib/kernel/qobjectdefs_impl.h
+++ b/src/corelib/kernel/qobjectdefs_impl.h
@@ -330,20 +330,6 @@ namespace QtPrivate {
typedef decltype(std::declval<Functor>().operator()((std::declval<ArgList>())...)) Value;
};
- // Get the function prototype for a functor. There can only be one call operator
- // in a functor, otherwise we get errors from ambiguity. But that's good enough.
- template <typename Ret, typename... Args>
- using FunctionTypeForTypes = Ret(*)(Args...);
-
- template <typename Ret, typename Obj, typename... Args>
- FunctionTypeForTypes<Ret, Args...> FunctorPrototype(Ret(Obj::*)(Args...) const) { return nullptr; }
- template <typename Ret, typename Obj, typename... Args>
- FunctionTypeForTypes<Ret, Args...> FunctorPrototype(Ret(Obj::*)(Args...)) { return nullptr; }
- template <typename Ret, typename Obj, typename... Args>
- FunctionTypeForTypes<Ret, Args...> FunctorPrototype(Ret(Obj::*)(Args...) const noexcept) { return nullptr; }
- template <typename Ret, typename Obj, typename... Args>
- FunctionTypeForTypes<Ret, Args...> FunctorPrototype(Ret(Obj::*)(Args...) noexcept) { return nullptr; }
-
template<typename Function, int N> struct Functor
{
template <typename SignalArgs, typename R>
@@ -355,16 +341,16 @@ namespace QtPrivate {
template<typename Func>
struct ZeroArgFunctor : Functor<Func, 0>
{
- using Function = decltype(FunctorPrototype(&std::decay_t<Func>::operator()));
+ using ReturnType = decltype(std::declval<Func>()());
+ using Function = ReturnType(*)();
enum {ArgumentCount = 0};
using Arguments = QtPrivate::List<>;
- using ReturnType = typename FunctionPointer<Function>::ReturnType;
};
template<typename Func>
- using Callable = std::conditional_t<FunctionPointer<Func>::ArgumentCount == -1,
- ZeroArgFunctor<Func>,
- FunctionPointer<Func>
+ using Callable = std::conditional_t<FunctionPointer<std::decay_t<Func>>::ArgumentCount == -1,
+ ZeroArgFunctor<std::decay_t<Func>>,
+ FunctionPointer<std::decay_t<Func>>
>;
/*
diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
index 4c3bb8532b..926f3d1d60 100644
--- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
+++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
@@ -302,6 +302,8 @@ private slots:
void invokeTypedefTypes();
void invokeException();
void invokeQueuedAutoRegister();
+ void invokeFreeFunction();
+ void invokeBind();
void qtMetaObjectInheritance();
void normalizedSignature_data();
void normalizedSignature();
@@ -1997,6 +1999,49 @@ void tst_QMetaObject::invokeQueuedAutoRegister()
QString("slotWithRegistrableArgument:myShared-myShared-myShared-myShared-00"));
}
+namespace FunctionTest {
+ static void function0() {}
+ static int functionNoExcept() noexcept
+ {
+ return 42;
+ }
+}
+
+void tst_QMetaObject::invokeFreeFunction()
+{
+ using namespace FunctionTest;
+ QtTestObject obj;
+ QMetaObject::invokeMethod(&obj, function0);
+ int retInt = -1;
+ QMetaObject::invokeMethod(&obj, functionNoExcept, &retInt);
+ QCOMPARE(retInt, functionNoExcept());
+}
+
+void tst_QMetaObject::invokeBind()
+{
+ QtTestObject obj;
+
+ struct {
+ int number;
+ QString string;
+ } results;
+
+ const auto function = [&results](int number, const QString &string) -> bool {
+ results.number = number;
+ results.string = string;
+ return true;
+ };
+
+ const int number = 42;
+ const QString string("Test");
+ const auto binding = std::bind(function, number, string);
+ bool ret = false;
+ QMetaObject::invokeMethod(&obj, binding, &ret);
+ QVERIFY(ret);
+ QCOMPARE(results.number, number);
+ QCOMPARE(results.string, string);
+}
+
void tst_QMetaObject::normalizedSignature_data()
{
QTest::addColumn<QString>("signature");