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>
This commit is contained in:
parent
af8e75f54f
commit
9c9c5d9828
@ -419,6 +419,8 @@ struct Q_CORE_EXPORT QMetaObject
|
|||||||
Qt::ConnectionType type = Qt::AutoConnection,
|
Qt::ConnectionType type = Qt::AutoConnection,
|
||||||
typename QtPrivate::Callable<Func>::ReturnType *ret = nullptr)
|
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;
|
using Prototype = typename QtPrivate::Callable<Func>::Function;
|
||||||
return invokeMethodImpl(object, QtPrivate::makeCallableObject<Prototype>(std::forward<Func>(function)), type, ret);
|
return invokeMethodImpl(object, QtPrivate::makeCallableObject<Prototype>(std::forward<Func>(function)), type, ret);
|
||||||
}
|
}
|
||||||
|
@ -330,20 +330,6 @@ namespace QtPrivate {
|
|||||||
typedef decltype(std::declval<Functor>().operator()((std::declval<ArgList>())...)) Value;
|
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 Function, int N> struct Functor
|
||||||
{
|
{
|
||||||
template <typename SignalArgs, typename R>
|
template <typename SignalArgs, typename R>
|
||||||
@ -355,16 +341,16 @@ namespace QtPrivate {
|
|||||||
template<typename Func>
|
template<typename Func>
|
||||||
struct ZeroArgFunctor : Functor<Func, 0>
|
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};
|
enum {ArgumentCount = 0};
|
||||||
using Arguments = QtPrivate::List<>;
|
using Arguments = QtPrivate::List<>;
|
||||||
using ReturnType = typename FunctionPointer<Function>::ReturnType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Func>
|
template<typename Func>
|
||||||
using Callable = std::conditional_t<FunctionPointer<Func>::ArgumentCount == -1,
|
using Callable = std::conditional_t<FunctionPointer<std::decay_t<Func>>::ArgumentCount == -1,
|
||||||
ZeroArgFunctor<Func>,
|
ZeroArgFunctor<std::decay_t<Func>>,
|
||||||
FunctionPointer<Func>
|
FunctionPointer<std::decay_t<Func>>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -302,6 +302,8 @@ private slots:
|
|||||||
void invokeTypedefTypes();
|
void invokeTypedefTypes();
|
||||||
void invokeException();
|
void invokeException();
|
||||||
void invokeQueuedAutoRegister();
|
void invokeQueuedAutoRegister();
|
||||||
|
void invokeFreeFunction();
|
||||||
|
void invokeBind();
|
||||||
void qtMetaObjectInheritance();
|
void qtMetaObjectInheritance();
|
||||||
void normalizedSignature_data();
|
void normalizedSignature_data();
|
||||||
void normalizedSignature();
|
void normalizedSignature();
|
||||||
@ -1997,6 +1999,49 @@ void tst_QMetaObject::invokeQueuedAutoRegister()
|
|||||||
QString("slotWithRegistrableArgument:myShared-myShared-myShared-myShared-00"));
|
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()
|
void tst_QMetaObject::normalizedSignature_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("signature");
|
QTest::addColumn<QString>("signature");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user