diff options
author | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2023-05-11 00:54:13 +0200 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2023-05-11 19:36:55 +0200 |
commit | 9c9c5d98281983578560ace7e8a3e3c684289177 (patch) | |
tree | fa3eb7ab00f77b1fc8ee375000d1c31d46538ab8 | |
parent | af8e75f54f54252cbb662eeb77697bdd8ec07296 (diff) | |
download | qtbase-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.h | 2 | ||||
-rw-r--r-- | src/corelib/kernel/qobjectdefs_impl.h | 24 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp | 45 |
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"); |