invokeMethod: enable passing parameters to overload taking functors
This was missing for a while, and there is nothing fundamentally missing for it to work. With the more recent work around slot objects and invokeMethod in general, it is a good time to add support for this. In this patch, when connecting to a functor, it automatically deduces the overload to call based on the arguments passed to invokeMethod. Sharing code with QObject::connect could be done, but they have a key difference that makes it harder: With signal emissions we throw away trailing arguments that are not used: i.e. `signal(int, int)` can be connected to `slot(int)` or `slot()`. With invokeMethod that's not a thing. So we will need a way to toggle that behavior during resolution. [ChangeLog][QtCore][QMetaObject] Added support for passing parameters to the overload of QMetaObject::invokeMethod that takes a functor. These new overloads must have the return-value passed through qReturnArg(). Change-Id: If4fcbb75515b19e72fab80115c109efa37e6626e Reviewed-by: Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
777a1ed191
commit
a7d2855b3c
@ -635,6 +635,13 @@ QString QLocale::bcp47Name() const
|
|||||||
return bcp47Name(TagSeparator::Dash);
|
return bcp47Name(TagSeparator::Dash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "qobjectdefs.h"
|
||||||
|
|
||||||
|
bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret)
|
||||||
|
{
|
||||||
|
return invokeMethodImpl(object, slot, type, 1, &ret, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
#include "qurl.h"
|
#include "qurl.h"
|
||||||
|
|
||||||
QUrl QUrl::fromEncoded(const QByteArray &input, ParsingMode mode)
|
QUrl QUrl::fromEncoded(const QByteArray &input, ParsingMode mode)
|
||||||
|
@ -1612,12 +1612,16 @@ bool QMetaObject::invokeMethodImpl(QObject *obj, const char *member, Qt::Connect
|
|||||||
return printMethodNotFoundWarning(obj->metaObject(), name, paramCount, typeNames, metaTypes);
|
return printMethodNotFoundWarning(obj->metaObject(), name, paramCount, typeNames, metaTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slotObj,
|
bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
|
||||||
Qt::ConnectionType type, void *ret)
|
qsizetype parameterCount, const void *const *params, const char *const *names,
|
||||||
|
const QtPrivate::QMetaTypeInterface * const *metaTypes)
|
||||||
{
|
{
|
||||||
|
// We don't need this now but maybe we want it later, or we may be able to
|
||||||
|
// share more code between the two invokeMethodImpl() overloads:
|
||||||
|
Q_UNUSED(names);
|
||||||
auto slot = QtPrivate::SlotObjUniquePtr(slotObj);
|
auto slot = QtPrivate::SlotObjUniquePtr(slotObj);
|
||||||
|
|
||||||
if (! object)
|
if (! object) // ### only if the slot requires the object + not queued?
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Qt::HANDLE currentThreadId = QThread::currentThreadId();
|
Qt::HANDLE currentThreadId = QThread::currentThreadId();
|
||||||
@ -1629,8 +1633,7 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
|
|||||||
if (type == Qt::AutoConnection)
|
if (type == Qt::AutoConnection)
|
||||||
type = receiverInSameThread ? Qt::DirectConnection : Qt::QueuedConnection;
|
type = receiverInSameThread ? Qt::DirectConnection : Qt::QueuedConnection;
|
||||||
|
|
||||||
void *argv[] = { ret };
|
void **argv = const_cast<void **>(params);
|
||||||
|
|
||||||
if (type == Qt::DirectConnection) {
|
if (type == Qt::DirectConnection) {
|
||||||
slot->call(object, argv);
|
slot->call(object, argv);
|
||||||
} else if (type == Qt::QueuedConnection) {
|
} else if (type == Qt::QueuedConnection) {
|
||||||
@ -1639,8 +1642,16 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
|
|||||||
"queued connections");
|
"queued connections");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
auto event = std::make_unique<QMetaCallEvent>(std::move(slot), nullptr, -1, parameterCount);
|
||||||
|
void **args = event->args();
|
||||||
|
QMetaType *types = event->types();
|
||||||
|
|
||||||
QCoreApplication::postEvent(object, new QMetaCallEvent(std::move(slot), nullptr, -1, 1));
|
for (int i = 1; i < parameterCount; ++i) {
|
||||||
|
types[i] = QMetaType(metaTypes[i]);
|
||||||
|
args[i] = types[i].create(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCoreApplication::postEvent(object, event.release());
|
||||||
} else if (type == Qt::BlockingQueuedConnection) {
|
} else if (type == Qt::BlockingQueuedConnection) {
|
||||||
#if QT_CONFIG(thread)
|
#if QT_CONFIG(thread)
|
||||||
if (receiverInSameThread)
|
if (receiverInSameThread)
|
||||||
@ -1733,6 +1744,31 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
|
|||||||
Qt::AutoConnection will be used.
|
Qt::AutoConnection will be used.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn template<typename Functor, typename FunctorReturnType, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments)
|
||||||
|
\fn template<typename Functor, typename FunctorReturnType, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments)
|
||||||
|
\fn template<typename Functor, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, Args &&...arguments)
|
||||||
|
\fn template<typename Functor, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Args &&...arguments)
|
||||||
|
|
||||||
|
\since 6.7
|
||||||
|
\threadsafe
|
||||||
|
|
||||||
|
Invokes the \a function with \a arguments in the event loop of \a context.
|
||||||
|
\a function can be a functor or a pointer to a member function. Returns
|
||||||
|
\c true if the function could be invoked. The return value of the
|
||||||
|
function call is placed in \a ret. The object used for the \a ret argument
|
||||||
|
should be obtained by passing your object to qReturnArg(). For example:
|
||||||
|
|
||||||
|
\badcode
|
||||||
|
MyClass *obj = ...;
|
||||||
|
int result = 0;
|
||||||
|
QMetaObject::invokeMethod(obj, &MyClass::myMethod, qReturnArg(result), parameter);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
If \a type is set, then the function is invoked using that connection type.
|
||||||
|
Otherwise, Qt::AutoConnection will be used.
|
||||||
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn QMetaObject::Connection &QMetaObject::Connection::operator=(Connection &&other)
|
\fn QMetaObject::Connection &QMetaObject::Connection::operator=(Connection &&other)
|
||||||
|
|
||||||
|
@ -416,33 +416,101 @@ struct Q_CORE_EXPORT QMetaObject
|
|||||||
static bool invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr);
|
static bool invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr);
|
||||||
template<typename Functor, typename FunctorReturnType>
|
template<typename Functor, typename FunctorReturnType>
|
||||||
static bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret);
|
static bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret);
|
||||||
|
|
||||||
|
template<typename Functor, typename FunctorReturnType, typename... Args>
|
||||||
|
static bool invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments);
|
||||||
|
template<typename Functor, typename FunctorReturnType, typename... Args>
|
||||||
|
static bool invokeMethod(QObject *context, Functor &&function, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments);
|
||||||
|
template<typename Functor, typename... Args>
|
||||||
|
static bool invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, Args &&...arguments);
|
||||||
|
template<typename Functor, typename... Args>
|
||||||
|
static bool invokeMethod(QObject *context, Functor &&function, Args &&...arguments);
|
||||||
#else
|
#else
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
|
static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
|
||||||
QtPrivate::Invoke::AreOldStyleArgs<Func>>,
|
QtPrivate::Invoke::AreOldStyleArgs<Func>>,
|
||||||
bool>
|
bool>
|
||||||
invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
|
invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
|
||||||
Func &&function,
|
Func &&function, Qt::ConnectionType type,
|
||||||
Qt::ConnectionType type = Qt::AutoConnection,
|
typename QtPrivate::Callable<Func>::ReturnType *ret)
|
||||||
typename QtPrivate::Callable<Func>::ReturnType *ret = nullptr)
|
|
||||||
{
|
{
|
||||||
static_assert(QtPrivate::Callable<Func>::ArgumentCount <= 0,
|
using R = typename QtPrivate::Callable<Func>::ReturnType;
|
||||||
"QMetaObject::invokeMethod cannot call functions with arguments!");
|
const auto getReturnArg = [ret]() -> QTemplatedMetaMethodReturnArgument<R> {
|
||||||
using Prototype = typename QtPrivate::Callable<Func>::Function;
|
if constexpr (std::is_void_v<R>)
|
||||||
return invokeMethodImpl(object, QtPrivate::makeCallableObject<Prototype>(std::forward<Func>(function)), type, ret);
|
return {};
|
||||||
|
else
|
||||||
|
return ret ? qReturnArg(*ret) : QTemplatedMetaMethodReturnArgument<R>{};
|
||||||
|
};
|
||||||
|
return invokeMethod(object, std::forward<Func>(function), type, getReturnArg());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
|
static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
|
||||||
QtPrivate::Invoke::AreOldStyleArgs<Func>>,
|
QtPrivate::Invoke::AreOldStyleArgs<Func>>,
|
||||||
bool>
|
bool>
|
||||||
invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
|
invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
|
||||||
Func &&function,
|
Func &&function, typename QtPrivate::Callable<Func>::ReturnType *ret)
|
||||||
typename QtPrivate::Callable<Func>::ReturnType *ret)
|
|
||||||
{
|
{
|
||||||
return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, ret);
|
return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
|
||||||
|
QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
|
||||||
|
bool>
|
||||||
|
invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
|
||||||
|
Func &&function, Qt::ConnectionType type,
|
||||||
|
QTemplatedMetaMethodReturnArgument<
|
||||||
|
typename QtPrivate::Callable<Func, Args...>::ReturnType>
|
||||||
|
ret,
|
||||||
|
Args &&...args)
|
||||||
|
{
|
||||||
|
return invokeMethodCallableHelper(object, std::forward<Func>(function), type, ret,
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
|
||||||
|
QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
|
||||||
|
bool>
|
||||||
|
invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
|
||||||
|
Func &&function, Qt::ConnectionType type, Args &&...args)
|
||||||
|
{
|
||||||
|
using R = typename QtPrivate::Callable<Func, Args...>::ReturnType;
|
||||||
|
QTemplatedMetaMethodReturnArgument<R> r{ QtPrivate::qMetaTypeInterfaceForType<R>(), nullptr,
|
||||||
|
nullptr };
|
||||||
|
return invokeMethod(object, std::forward<Func>(function), type, r,
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
|
||||||
|
QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
|
||||||
|
bool>
|
||||||
|
invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
|
||||||
|
Func &&function,
|
||||||
|
QTemplatedMetaMethodReturnArgument<
|
||||||
|
typename QtPrivate::Callable<Func, Args...>::ReturnType>
|
||||||
|
ret,
|
||||||
|
Args &&...args)
|
||||||
|
{
|
||||||
|
return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, ret,
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
|
||||||
|
QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
|
||||||
|
bool>
|
||||||
|
invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
|
||||||
|
Func &&function, Args &&...args)
|
||||||
|
{
|
||||||
|
using R = typename QtPrivate::Callable<Func, Args...>::ReturnType;
|
||||||
|
QTemplatedMetaMethodReturnArgument<R> r{ QtPrivate::qMetaTypeInterfaceForType<R>(), nullptr,
|
||||||
|
nullptr };
|
||||||
|
return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, r,
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||||
@ -530,10 +598,40 @@ struct Q_CORE_EXPORT QMetaObject
|
|||||||
} d;
|
} d;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Just need to have this here with a separate name so the other inline
|
||||||
|
// functions can call this without any ambiguity
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
static bool
|
||||||
|
invokeMethodCallableHelper(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
|
||||||
|
Func &&function, Qt::ConnectionType type, const QMetaMethodReturnArgument &ret,
|
||||||
|
Args &&...args)
|
||||||
|
{
|
||||||
|
using Callable = QtPrivate::Callable<Func, Args...>;
|
||||||
|
using ExpectedArguments = typename Callable::Arguments;
|
||||||
|
static_assert(sizeof...(Args) <= ExpectedArguments::size, "Too many arguments");
|
||||||
|
using ActualArguments = QtPrivate::List<Args...>;
|
||||||
|
static_assert(QtPrivate::CheckCompatibleArguments<ActualArguments,
|
||||||
|
ExpectedArguments>::value,
|
||||||
|
"Incompatible arguments");
|
||||||
|
|
||||||
|
auto h = QtPrivate::invokeMethodHelper(ret, args...);
|
||||||
|
|
||||||
|
auto callable = new QtPrivate::QCallableObject<std::decay_t<Func>, ActualArguments,
|
||||||
|
typename Callable::ReturnType>(std::forward<Func>(function));
|
||||||
|
return invokeMethodImpl(object, callable, type, h.parameterCount(), h.parameters.data(),
|
||||||
|
h.typeNames.data(), h.metaTypes.data());
|
||||||
|
}
|
||||||
|
|
||||||
static bool invokeMethodImpl(QObject *object, const char *member, Qt::ConnectionType type,
|
static bool invokeMethodImpl(QObject *object, const char *member, Qt::ConnectionType type,
|
||||||
qsizetype parameterCount, const void *const *parameters, const char *const *names,
|
qsizetype parameterCount, const void *const *parameters, const char *const *names,
|
||||||
const QtPrivate::QMetaTypeInterface * const *metaTypes);
|
const QtPrivate::QMetaTypeInterface * const *metaTypes);
|
||||||
|
static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slotObj,
|
||||||
|
Qt::ConnectionType type, qsizetype parameterCount,
|
||||||
|
const void *const *params, const char *const *names,
|
||||||
|
const QtPrivate::QMetaTypeInterface *const *metaTypes);
|
||||||
|
#if QT_CORE_REMOVED_SINCE(6, 7)
|
||||||
static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret);
|
static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret);
|
||||||
|
#endif
|
||||||
static QObject *newInstanceImpl(const QMetaObject *mobj, qsizetype parameterCount,
|
static QObject *newInstanceImpl(const QMetaObject *mobj, qsizetype parameterCount,
|
||||||
const void **parameters, const char **typeNames,
|
const void **parameters, const char **typeNames,
|
||||||
const QtPrivate::QMetaTypeInterface **metaTypes);
|
const QtPrivate::QMetaTypeInterface **metaTypes);
|
||||||
|
@ -340,20 +340,55 @@ namespace QtPrivate {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Func>
|
template<typename Func, typename... Args>
|
||||||
struct ZeroArgFunctor : Functor<Func, 0>
|
struct FunctorCallable : Functor<Func, sizeof...(Args)>
|
||||||
{
|
{
|
||||||
using ReturnType = decltype(std::declval<Func>()());
|
using ReturnType = decltype(std::declval<Func>()(std::declval<Args>()...));
|
||||||
using Function = ReturnType(*)();
|
using Function = ReturnType(*)(Args...);
|
||||||
enum {ArgumentCount = 0};
|
enum {ArgumentCount = sizeof...(Args)};
|
||||||
using Arguments = QtPrivate::List<>;
|
using Arguments = QtPrivate::List<Args...>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Func>
|
template <typename Functor, typename... Args>
|
||||||
using Callable = std::conditional_t<FunctionPointer<std::decay_t<Func>>::ArgumentCount == -1,
|
struct HasCallOperatorAcceptingArgs
|
||||||
ZeroArgFunctor<std::decay_t<Func>>,
|
{
|
||||||
FunctionPointer<std::decay_t<Func>>
|
private:
|
||||||
>;
|
template <typename F, typename = void>
|
||||||
|
struct Test : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
// We explicitly use .operator() to not return true for pointers to free/static function
|
||||||
|
template <typename F>
|
||||||
|
struct Test<F, std::void_t<decltype(std::declval<F>().operator()(std::declval<Args>()...))>>
|
||||||
|
: std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Type = Test<Functor>;
|
||||||
|
static constexpr bool value = Type::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Functor, typename... Args>
|
||||||
|
constexpr bool
|
||||||
|
HasCallOperatorAcceptingArgs_v = HasCallOperatorAcceptingArgs<Functor, Args...>::value;
|
||||||
|
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
struct CallableHelper
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Could've been std::conditional_t, but that requires all branches to
|
||||||
|
// be valid
|
||||||
|
static auto Resolve(std::true_type CallOperator) -> FunctorCallable<Func, Args...>;
|
||||||
|
static auto Resolve(std::false_type CallOperator) -> FunctionPointer<std::decay_t<Func>>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Type = decltype(Resolve(typename HasCallOperatorAcceptingArgs<std::decay_t<Func>,
|
||||||
|
Args...>::Type{}));
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Func, typename... Args>
|
||||||
|
using Callable = typename CallableHelper<Func, Args...>::Type;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Wrapper around ComputeFunctorArgumentCount and CheckCompatibleArgument,
|
Wrapper around ComputeFunctorArgumentCount and CheckCompatibleArgument,
|
||||||
|
@ -366,8 +366,10 @@ void QTimer::singleShotImpl(int msec, Qt::TimerType timerType,
|
|||||||
deleteReceiver = true;
|
deleteReceiver = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto h = QtPrivate::invokeMethodHelper({});
|
||||||
QMetaObject::invokeMethodImpl(const_cast<QObject *>(receiver), slotObj,
|
QMetaObject::invokeMethodImpl(const_cast<QObject *>(receiver), slotObj,
|
||||||
Qt::QueuedConnection, nullptr);
|
Qt::QueuedConnection, h.parameterCount(), h.parameters.data(), h.typeNames.data(),
|
||||||
|
h.metaTypes.data());
|
||||||
|
|
||||||
if (deleteReceiver)
|
if (deleteReceiver)
|
||||||
const_cast<QObject *>(receiver)->deleteLater();
|
const_cast<QObject *>(receiver)->deleteLater();
|
||||||
|
@ -31,6 +31,8 @@ Q_DECLARE_METATYPE(const QMetaObject *)
|
|||||||
# define Q_NO_ARG
|
# define Q_NO_ARG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
struct MyStruct
|
struct MyStruct
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -1081,6 +1083,98 @@ void tst_QMetaObject::invokePointer()
|
|||||||
QCOMPARE(obj.slotResult, QString("sl1:1"));
|
QCOMPARE(obj.slotResult, QString("sl1:1"));
|
||||||
}
|
}
|
||||||
QCOMPARE(countedStructObjectsCount, 0);
|
QCOMPARE(countedStructObjectsCount, 0);
|
||||||
|
|
||||||
|
// Invoking with parameters
|
||||||
|
QString result;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, qReturnArg(result), u"bubu"_s));
|
||||||
|
QCOMPARE(obj.slotResult, u"sl1:bubu");
|
||||||
|
QCOMPARE(result, u"yessir");
|
||||||
|
|
||||||
|
// without taking return value
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, u"bubu"_s));
|
||||||
|
QCOMPARE(obj.slotResult, u"sl1:bubu");
|
||||||
|
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, Qt::DirectConnection, qReturnArg(result),
|
||||||
|
u"byebye"_s));
|
||||||
|
QCOMPARE(obj.slotResult, u"sl1:byebye");
|
||||||
|
QCOMPARE(result, u"yessir");
|
||||||
|
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, qOverload<int, int>(&QtTestObject::overloadedSlot), 1, 2));
|
||||||
|
QCOMPARE(obj.slotResult, u"overloadedSlot:1,2");
|
||||||
|
|
||||||
|
// non-const ref parameter
|
||||||
|
QString original = u"bubu"_s;
|
||||||
|
QString &ref = original;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, qReturnArg(result), ref));
|
||||||
|
QCOMPARE(obj.slotResult, u"sl1:bubu");
|
||||||
|
QCOMPARE(result, u"yessir");
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
bool operator()(int) { return true; }
|
||||||
|
int operator()(char) { return 15; }
|
||||||
|
int operator()(QString = {}, int = {}, int = {}) { return 242; }
|
||||||
|
} r;
|
||||||
|
|
||||||
|
// Test figuring out which operator() to call:
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), 1));
|
||||||
|
QCOMPARE(res, true);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), 'c'));
|
||||||
|
QCOMPARE(res, 15);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res)));
|
||||||
|
QCOMPARE(res, 242);
|
||||||
|
res = 0;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), u"bu"_s));
|
||||||
|
QCOMPARE(res, 242);
|
||||||
|
res = 0;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), u"bu"_s, 1));
|
||||||
|
QCOMPARE(res, 242);
|
||||||
|
res = 0;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), u"bu"_s, 1, 2));
|
||||||
|
QCOMPARE(res, 242);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto lambda = [](const QString &s) { return s + s; };
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result), u"bu"_s));
|
||||||
|
QCOMPARE(result, u"bubu");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto lambda = [](const QString &s = u"bu"_s) { return s + s; };
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result)));
|
||||||
|
QCOMPARE(result, u"bubu");
|
||||||
|
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result), u"bye"_s));
|
||||||
|
QCOMPARE(result, u"byebye");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto lambda = [](const QString &s, qint64 a, qint16 b, qint8 c) {
|
||||||
|
return s + QString::number(a) + QString::number(b) + QString::number(c);
|
||||||
|
};
|
||||||
|
// Testing mismatching argument (int for qint64). The other arguments
|
||||||
|
// would static_assert for potential truncation if they were ints.
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result), u"bu"_s, 1, qint16(2), qint8(3)));
|
||||||
|
QCOMPARE(result, u"bu123");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Testing deduction
|
||||||
|
auto lambda = [](const QString &s, auto a) { return s + a; };
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result), u"bu"_s, "bu"_L1));
|
||||||
|
QCOMPARE(result, u"bubu");
|
||||||
|
|
||||||
|
auto variadic = [](const QString &s, auto... a) { return s + (QString::number(a) + ...); };
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, variadic, qReturnArg(result), u"bu"_s, 1, 2, 3, 4, 5, 6));
|
||||||
|
QCOMPARE(result, u"bu123456");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QMetaObject::invokeQueuedMetaMember()
|
void tst_QMetaObject::invokeQueuedMetaMember()
|
||||||
@ -1345,6 +1439,12 @@ void tst_QMetaObject::invokeQueuedPointer()
|
|||||||
QCOMPARE(var, 0);
|
QCOMPARE(var, 0);
|
||||||
}
|
}
|
||||||
QCOMPARE(countedStructObjectsCount, 0);
|
QCOMPARE(countedStructObjectsCount, 0);
|
||||||
|
|
||||||
|
// Invoking with parameters
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, Qt::QueuedConnection, u"bubu"_s));
|
||||||
|
qApp->processEvents(QEventLoop::AllEvents);
|
||||||
|
QCOMPARE(obj.slotResult, u"sl1:bubu");
|
||||||
}
|
}
|
||||||
|
|
||||||
// this test is duplicated below
|
// this test is duplicated below
|
||||||
@ -1764,6 +1864,18 @@ void tst_QMetaObject::invokeBlockingQueuedPointer()
|
|||||||
Qt::BlockingQueuedConnection));
|
Qt::BlockingQueuedConnection));
|
||||||
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
|
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test with parameters
|
||||||
|
QString result;
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, Qt::BlockingQueuedConnection,
|
||||||
|
qReturnArg(result), u"bubu"_s));
|
||||||
|
QCOMPARE(result, u"yessir");
|
||||||
|
QCOMPARE(obj.slotResult, u"sl1:bubu");
|
||||||
|
|
||||||
|
QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl2, Qt::BlockingQueuedConnection,
|
||||||
|
u"bubu"_s, u"baba"_s));
|
||||||
|
QCOMPARE(obj.slotResult, u"sl2:bububaba");
|
||||||
|
|
||||||
QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.moveToThread(QThread::currentThread());}, Qt::BlockingQueuedConnection));
|
QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.moveToThread(QThread::currentThread());}, Qt::BlockingQueuedConnection));
|
||||||
t.quit();
|
t.quit();
|
||||||
QVERIFY(t.wait());
|
QVERIFY(t.wait());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user