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,
|
||||
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);
|
||||
}
|
||||
|
@ -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>>
|
||||
>;
|
||||
|
||||
/*
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user