From 9c9c5d98281983578560ace7e8a3e3c684289177 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 11 May 2023 00:54:13 +0200 Subject: [PATCH] Fix QMetaObject::invokeMethod for free functions and std::bind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: MÃ¥rten Nordheim --- src/corelib/kernel/qobjectdefs.h | 2 + src/corelib/kernel/qobjectdefs_impl.h | 24 +++------- .../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 1b074cea446..5c1d2fd8aea 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::ReturnType *ret = nullptr) { + static_assert(QtPrivate::Callable::ArgumentCount <= 0, + "QMetaObject::invokeMethod cannot call functions with arguments!"); using Prototype = typename QtPrivate::Callable::Function; return invokeMethodImpl(object, QtPrivate::makeCallableObject(std::forward(function)), type, ret); } diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index 72193cc9da3..33df3624d22 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().operator()((std::declval())...)) 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 - using FunctionTypeForTypes = Ret(*)(Args...); - - template - FunctionTypeForTypes FunctorPrototype(Ret(Obj::*)(Args...) const) { return nullptr; } - template - FunctionTypeForTypes FunctorPrototype(Ret(Obj::*)(Args...)) { return nullptr; } - template - FunctionTypeForTypes FunctorPrototype(Ret(Obj::*)(Args...) const noexcept) { return nullptr; } - template - FunctionTypeForTypes FunctorPrototype(Ret(Obj::*)(Args...) noexcept) { return nullptr; } - template struct Functor { template @@ -355,16 +341,16 @@ namespace QtPrivate { template struct ZeroArgFunctor : Functor { - using Function = decltype(FunctorPrototype(&std::decay_t::operator())); + using ReturnType = decltype(std::declval()()); + using Function = ReturnType(*)(); enum {ArgumentCount = 0}; using Arguments = QtPrivate::List<>; - using ReturnType = typename FunctionPointer::ReturnType; }; template - using Callable = std::conditional_t::ArgumentCount == -1, - ZeroArgFunctor, - FunctionPointer + using Callable = std::conditional_t>::ArgumentCount == -1, + ZeroArgFunctor>, + FunctionPointer> >; /* diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 4c3bb8532b6..926f3d1d60b 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("signature");