QMetaObject: add a new, variadic invoke/invokeMethod/newInstance

[ChangeLog][QtCore][Meta Objects] The QMetaObject::invokeMethod() taking
a method name by string, QMetaObject::newInstance(), and
QMetaMethod::invoke() now support more than 10 arguments.

[ChangeLog][QtCore][Meta Objects] The use of the Q_ARG and Q_RETURN_ARG
macros is now optional with QMetaObject::invokeMethod(),
QMetaObject::newInstance(), and QMetaMethod::invoke(): the type name
will be obtained from the C++ type (the same as QMetaType). The function
qReturnArg() can be used in place of the Q_RETURN_ARG macro. The macros
are still useful in rare conditions where the type was typedef'ed from
its original name.

Change-Id: I36b24183fbd041179f2ffffd17022a2b48c7639b
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Thiago Macieira 2022-08-01 11:12:20 -07:00
parent 191419e980
commit fe92b08065
6 changed files with 890 additions and 60 deletions

View File

@ -223,22 +223,13 @@ QObject *QMetaObject::newInstance(QGenericArgument val0,
QGenericArgument val8,
QGenericArgument val9) const
{
if (!inherits(&QObject::staticMetaObject))
{
qWarning("QMetaObject::newInstance: type %s does not inherit QObject", className());
return nullptr;
}
QObject *returnValue = nullptr;
QMetaType returnValueMetaType = QMetaType::fromType<decltype(returnValue)>();
const char *typeNames[] = {
returnValueMetaType.name(),
nullptr,
val0.name(), val1.name(), val2.name(), val3.name(), val4.name(),
val5.name(), val6.name(), val7.name(), val8.name(), val9.name()
};
const void *parameters[] = {
&returnValue,
nullptr,
val0.data(), val1.data(), val2.data(), val3.data(), val4.data(),
val5.data(), val6.data(), val7.data(), val8.data(), val9.data()
};
@ -250,10 +241,34 @@ QObject *QMetaObject::newInstance(QGenericArgument val0,
break;
}
return newInstanceImpl(this, paramCount, parameters, typeNames);
}
QObject *QMetaObject::newInstanceImpl(const QMetaObject *mobj, qsizetype paramCount,
const void **parameters, const char **typeNames)
{
if (!mobj->inherits(&QObject::staticMetaObject)) {
qWarning("QMetaObject::newInstance: type %s does not inherit QObject", mobj->className());
return nullptr;
}
QT_WARNING_PUSH
#if Q_CC_GNU >= 1200
QT_WARNING_DISABLE_GCC("-Wdangling-pointer")
#endif
// set the return type
QObject *returnValue = nullptr;
QMetaType returnValueMetaType = QMetaType::fromType<decltype(returnValue)>();
parameters[0] = &returnValue;
typeNames[0] = returnValueMetaType.name();
QT_WARNING_POP
// find the constructor
auto priv = QMetaObjectPrivate::get(this);
auto priv = QMetaObjectPrivate::get(mobj);
for (int i = 0; i < priv->constructorCount; ++i) {
QMetaMethod m = QMetaMethod::fromRelativeConstructorIndex(this, i);
QMetaMethod m = QMetaMethod::fromRelativeConstructorIndex(mobj, i);
if (m.parameterCount() != (paramCount - 1))
continue;
@ -1457,6 +1472,19 @@ bool QMetaObject::invokeMethod(QObject *obj,
if (qstrlen(typeNames[paramCount]) <= 0)
break;
}
return invokeMethodImpl(obj, member, type, paramCount, parameters, typeNames);
}
bool QMetaObject::invokeMethodImpl(QObject *obj, const char *member, Qt::ConnectionType type,
qsizetype paramCount, const void * const *parameters,
const char * const *typeNames)
{
if (!obj)
return false;
Q_ASSERT(paramCount >= 1); // includes the return type
Q_ASSERT(parameters);
Q_ASSERT(typeNames);
// find the method
QLatin1StringView name(member);
@ -2382,9 +2410,17 @@ bool QMetaMethod::invoke(QObject *object,
if (qstrlen(typeNames[paramCount]) <= 0)
break;
}
return invokeImpl(*this, object, connectionType, paramCount, param, typeNames);
}
bool QMetaMethod::invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType connectionType,
qsizetype paramCount, const void *const *parameters,
const char *const *typeNames)
{
if (!target || !self.mobj)
return false;
QMetaMethodPrivate::InvokeFailReason r =
QMetaMethodPrivate::invokeImpl(*this, object, connectionType, paramCount, param, typeNames);
QMetaMethodPrivate::invokeImpl(self, target, connectionType, paramCount, parameters, typeNames);
if (Q_LIKELY(r == QMetaMethodPrivate::InvokeFailReason::None))
return true;
@ -2392,11 +2428,11 @@ bool QMetaMethod::invoke(QObject *object,
if (int(r) >= int(QMetaMethodPrivate::InvokeFailReason::FormalParameterMismatch)) {
int n = int(r) - int(QMetaMethodPrivate::InvokeFailReason::FormalParameterMismatch);
qWarning("QMetaMethod::invoke: cannot convert formal parameter %d from %s in call to %s::%s",
n, typeNames[n + 1], mobj->className(), methodSignature().constData());
n, typeNames[n + 1], self.mobj->className(), self.methodSignature().constData());
}
if (r == QMetaMethodPrivate::InvokeFailReason::TooFewArguments) {
qWarning("QMetaMethod::invoke: too few arguments (%d) in call to %s::%s",
int(paramCount), mobj->className(), methodSignature().constData());
int(paramCount), self.mobj->className(), self.methodSignature().constData());
}
return false;
}
@ -2412,7 +2448,8 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
Q_ASSERT(priv->mobj);
Q_ASSERT(self.methodType() == Constructor || object);
Q_ASSERT(self.methodType() == Constructor || priv->mobj->cast(object));
Q_ASSERT(self.methodType() == Constructor || connectionType == Qt::ConnectionType(-1) ||
priv->mobj->cast(object));
Q_ASSERT(paramCount >= 1); // includes the return type
Q_ASSERT(parameters);
Q_ASSERT(typeNames);
@ -2477,18 +2514,23 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
}
}
Qt::HANDLE currentThreadId = QThread::currentThreadId();
QThread *objectThread = object->thread();
bool receiverInSameThread = false;
if (objectThread)
receiverInSameThread = currentThreadId == QThreadData::get2(objectThread)->threadId.loadRelaxed();
Qt::HANDLE currentThreadId = nullptr;
QThread *objectThread = nullptr;
auto receiverInSameThread = [&]() {
if (!currentThreadId) {
currentThreadId = QThread::currentThreadId();
objectThread = object->thread();
}
if (objectThread)
return currentThreadId == QThreadData::get2(objectThread)->threadId.loadRelaxed();
return false;
};
// check connection type
if (connectionType == Qt::AutoConnection) {
connectionType = receiverInSameThread
? Qt::DirectConnection
: Qt::QueuedConnection;
}
if (connectionType == Qt::AutoConnection)
connectionType = receiverInSameThread() ? Qt::DirectConnection : Qt::QueuedConnection;
else if (connectionType == Qt::ConnectionType(-1))
connectionType = Qt::DirectConnection;
#if !QT_CONFIG(thread)
if (connectionType == Qt::BlockingQueuedConnection) {
@ -2536,7 +2578,7 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
QCoreApplication::postEvent(object, event.release());
} else { // blocking queued connection
#if QT_CONFIG(thread)
if (receiverInSameThread) {
if (receiverInSameThread()) {
qWarning("QMetaMethod::invoke: Dead lock detected in BlockingQueuedConnection: "
"Receiver is %s(%p)", priv->mobj->className(), object);
return InvokeFailReason::DeadLockDetected;

View File

@ -45,6 +45,7 @@ public:
inline const QMetaObject *enclosingMetaObject() const { return mobj; }
#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
bool invoke(QObject *object,
Qt::ConnectionType connectionType,
QGenericReturnArgument returnValue,
@ -76,7 +77,7 @@ public:
}
inline bool invoke(QObject *object,
Qt::ConnectionType connectionType,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@ -91,7 +92,7 @@ public:
val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
inline bool invoke(QObject *object,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@ -118,7 +119,7 @@ public:
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const;
inline bool invokeOnGadget(void *gadget,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@ -132,6 +133,48 @@ public:
return invokeOnGadget(gadget, QGenericReturnArgument(),
val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
#endif
template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invoke(QObject *obj, Qt::ConnectionType c, QMetaMethodReturnArgument r,
Args &&... arguments) const
{
auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
return invokeImpl(*this, obj, c, h.parameterCount(), h.parameters.data(),
h.typeNames.data());
}
template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invoke(QObject *obj, Qt::ConnectionType c, Args &&... arguments) const
{
return invoke(obj, c, QMetaMethodReturnArgument{}, std::forward<Args>(arguments)...);
}
template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invoke(QObject *obj, QMetaMethodReturnArgument r, Args &&... arguments) const
{
return invoke(obj, Qt::AutoConnection, r, std::forward<Args>(arguments)...);
}
template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invoke(QObject *obj, Args &&... arguments) const
{
return invoke(obj, Qt::AutoConnection, std::forward<Args>(arguments)...);
}
template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invokeOnGadget(void *gadget, QMetaMethodReturnArgument r, Args &&... arguments) const
{
auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
return invokeImpl(*this, gadget, Qt::ConnectionType(-1), h.parameterCount(),
h.parameters.data(), h.typeNames.data());
}
template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invokeOnGadget(void *gadget, Args &&... arguments) const
{
return invokeOnGadget(gadget, QMetaMethodReturnArgument{}, std::forward<Args>(arguments)...);
}
inline bool isValid() const { return mobj != nullptr; }
@ -146,6 +189,8 @@ public:
}
private:
static bool invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType, qsizetype paramCount,
const void *const *parameters, const char *const *typeNames);
static QMetaMethod fromSignalImpl(const QMetaObject *, void **);
static QMetaMethod fromRelativeMethodIndex(const QMetaObject *mobj, int index);
static QMetaMethod fromRelativeConstructorIndex(const QMetaObject *mobj, int index);

View File

@ -59,8 +59,8 @@ Q_CORE_EXPORT const char *qFlagLocation(const char *method);
# endif
#endif // QT_NO_META_MACROS
#define Q_ARG(type, data) QArgument<type >(#type, data)
#define Q_RETURN_ARG(type, data) QReturnArgument<type >(#type, data)
#define Q_ARG(Type, data) QtPrivate::Invoke::argument<Type>(QT_STRINGIFY(Type), data)
#define Q_RETURN_ARG(Type, data) QtPrivate::Invoke::returnArgument<Type>(QT_STRINGIFY(Type), data)
class QObject;
class QMetaMethod;
@ -70,6 +70,7 @@ class QMetaClassInfo;
namespace QtPrivate {
class QMetaTypeInterface;
template<typename T> constexpr auto typenameHelper();
}
struct QMethodRawArguments
@ -77,6 +78,7 @@ struct QMethodRawArguments
void **arguments;
};
#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
class Q_CORE_EXPORT QGenericArgument
{
public:
@ -124,6 +126,91 @@ public:
: QGenericReturnArgument(aName, static_cast<void *>(&aData))
{}
};
#endif
struct QMetaMethodArgument
{
const char *name;
const void *data;
};
struct QMetaMethodReturnArgument
{
const char *name;
void *data;
};
namespace QtPrivate {
namespace Invoke {
#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
template <typename... Args> struct AreOldStyleArgs :
std::disjunction<std::is_base_of<QGenericArgument, Args>...>
{};
template <typename T, typename... Args> using IfNotOldStyleArgs =
std::enable_if_t<!AreOldStyleArgs<Args...>::value, T>;
#else
template <typename T, typename... Args> using IfNotOldStyleArgs = T;
#endif
template <typename T> inline QMetaMethodArgument argument(const char *name, const T &t)
{
return { name, std::addressof(t) };
}
template <typename T> inline QMetaMethodReturnArgument returnArgument(const char *name, T &t)
{
return { name, std::addressof(t) };
}
template <typename T> inline const char *typenameHelper(const T &)
{
// duplicated from the QMetaTypeInterface, FIXME
static constexpr auto name = QtPrivate::typenameHelper<T>();
return name.data();
}
template <typename T> inline const void *dataHelper(const T &t)
{
return std::addressof(t);
}
inline const char *typenameHelper(QMetaMethodArgument a)
{ return a.name; }
inline const void *dataHelper(QMetaMethodArgument a)
{ return a.data; }
inline const char *typenameHelper(const char *) = delete;
template <typename T> inline const void *dataHelper(const char *) = delete;
inline const char *typenameHelper(const char16_t *) = delete;
template <typename T> inline const void *dataHelper(const char16_t *) = delete;
} // namespace QtPrivate::Invoke
template <typename... Args> inline auto invokeMethodHelper(QMetaMethodReturnArgument r, Args &&... arguments)
{
std::array params = { const_cast<const void *>(r.data), Invoke::dataHelper(arguments)... };
std::array names = { r.name, Invoke::typenameHelper(arguments)... };
static_assert(params.size() == names.size());
struct R {
decltype(params) parameters;
decltype(names) typeNames;
constexpr qsizetype parameterCount() const { return qsizetype(parameters.size()); }
};
return R { params, names };
}
} // namespace QtPrivate
template <typename T> inline QMetaMethodReturnArgument qReturnArg(T &&) = delete;
template <typename T> inline QMetaMethodReturnArgument qReturnArg(T &data)
{
if constexpr (std::is_same_v<T, const char *>) {
// need to go around the = delete above
return QtPrivate::Invoke::returnArgument("const char *", data);
} else {
const char *name = QtPrivate::Invoke::typenameHelper(data);
return QtPrivate::Invoke::returnArgument(name, data);
}
}
struct Q_CORE_EXPORT QMetaObject
{
@ -191,6 +278,7 @@ struct Q_CORE_EXPORT QMetaObject
static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
static void activate(QObject *sender, int signal_offset, int local_signal_index, void **argv);
#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
static bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType,
QGenericReturnArgument ret,
@ -224,7 +312,7 @@ struct Q_CORE_EXPORT QMetaObject
static inline bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType type,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@ -240,7 +328,7 @@ struct Q_CORE_EXPORT QMetaObject
}
static inline bool invokeMethod(QObject *obj, const char *member,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@ -254,6 +342,41 @@ struct Q_CORE_EXPORT QMetaObject
return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
#endif // Qt < 7.0
template <typename... Args> static
QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invokeMethod(QObject *obj, const char *member, Qt::ConnectionType c,
QMetaMethodReturnArgument r, Args &&... arguments)
{
auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
return invokeMethodImpl(obj, member, c, h.parameterCount(), h.parameters.data(),
h.typeNames.data());
}
template <typename... Args> static
QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invokeMethod(QObject *obj, const char *member, Qt::ConnectionType c, Args &&... arguments)
{
QMetaMethodReturnArgument r = {};
return invokeMethod(obj, member, c, r, std::forward<Args>(arguments)...);
}
template <typename... Args> static
QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invokeMethod(QObject *obj, const char *member, QMetaMethodReturnArgument r,
Args &&... arguments)
{
return invokeMethod(obj, member, Qt::AutoConnection, r, std::forward<Args>(arguments)...);
}
template <typename... Args> static
QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
invokeMethod(QObject *obj, const char *member, Args &&... arguments)
{
QMetaMethodReturnArgument r = {};
return invokeMethod(obj, member, Qt::AutoConnection, r, std::forward<Args>(arguments)...);
}
#ifdef Q_CLANG_QDOC
template<typename Functor, typename FunctorReturnType>
@ -336,7 +459,8 @@ struct Q_CORE_EXPORT QMetaObject
#endif
QObject *newInstance(QGenericArgument val0 = QGenericArgument(nullptr),
#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
QObject *newInstance(QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@ -346,6 +470,14 @@ struct Q_CORE_EXPORT QMetaObject
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const;
#endif
template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<QObject *, Args...>
newInstance(Args &&... arguments) const
{
auto h = QtPrivate::invokeMethodHelper(QMetaMethodReturnArgument{}, std::forward<Args>(arguments)...);
return newInstanceImpl(this, h.parameterCount(), h.parameters.data(), h.typeNames.data());
}
enum Call {
InvokeMetaMethod,
@ -405,7 +537,11 @@ struct Q_CORE_EXPORT QMetaObject
} d;
private:
static bool invokeMethodImpl(QObject *object, const char *member, Qt::ConnectionType type,
qsizetype parameterCount, const void *const *parameters, const char *const *names);
static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret);
static QObject *newInstanceImpl(const QMetaObject *mobj, qsizetype parameterCount,
const void **parameters, const char **typeNames);
friend class QTimer;
};

View File

@ -732,12 +732,12 @@ void tst_qmessagehandler::qMessagePattern_data()
// Q_OBJECT macro hence the ?helper? frame
"[MyClass::myFunction|MyClass::mySlot1|?" BACKTRACE_HELPER_NAME "?|",
// QMetaObject::invokeMethod calls internal function
// QMetaObject::invokeMethodImpl calls internal function
// (QMetaMethodPrivate::invokeImpl, at the tims of this writing), which
// will usually show only as ?libQt6Core.so? or equivalent, so we skip
// end of backtrace, actual message
"|" QT_NAMESPACE_STR "QMetaObject::invokeMethod] from_a_function 34"
"|" QT_NAMESPACE_STR "QMetaObject::invokeMethodImpl] from_a_function 34"
};
QTest::newRow("backtrace") << "[%{backtrace}] %{message}" << true << expectedBacktrace;
#endif

View File

@ -20,3 +20,11 @@ qt_internal_add_test(tst_qmetaobject
Qt::CorePrivate
)
qt_internal_add_test(tst_qmetaobject_compat
SOURCES
${tst_qmetaobject_SOURCES}
DEFINES
USE_COMPAT_Q_ARG=1
PUBLIC_LIBRARIES
Qt::CorePrivate
)

View File

@ -14,6 +14,23 @@ Q_DECLARE_METATYPE(const QMetaObject *)
#include "forwarddeclared.h"
#ifdef USE_COMPAT_Q_ARG
# define tst_QMetaObject tst_QMetaObject_CompatQArg
# if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
# error "This is a Qt 6 compatibility check test"
# endif
# undef Q_ARG
# undef Q_RETURN_ARG
# define Q_ARG(type, data) QArgument<type >(#type, data)
# define Q_RETURN_ARG(type, data) QReturnArgument<type >(#type, data)
# define Q_NO_ARG , QGenericArgument()
#else
// This macro is used to force the overload selection to the compat
// (non-variadic) code above
# define Q_NO_ARG
#endif
#ifdef QEASINGCURVE_H
# error "Please make sure qeasingcurve.h is not #include'd here! " \
"We need QEasingCurve to be only forward-declared."
@ -279,13 +296,17 @@ public:
private slots:
void connectSlotsByName();
void invokeMetaMember();
void invokeMetaMemberNoMacros();
void invokePointer();
void invokeQueuedMetaMember();
void invokeQueuedMetaMemberNoMacro();
void invokeQueuedPointer();
void invokeBlockingQueuedMetaMember();
void invokeBlockingQueuedMetaMemberNoMacros();
void invokeBlockingQueuedPointer();
void invokeCustomTypes();
void invokeMetaConstructor();
void invokeMetaConstructorNoMacro();
void invokeTypedefTypes();
void invokeException();
void invokeQueuedAutoRegister();
@ -307,6 +328,7 @@ private slots:
void classInfo();
void metaMethod();
void metaMethodNoMacro();
void indexOfMethod_data();
void indexOfMethod();
@ -651,6 +673,7 @@ void QtTestObject::staticFunction0()
qint64 QtTestObject::staticFunction1()
{ staticResult = "staticFunction1"; return Q_INT64_C(123456789)*123456789; }
// this test is duplicated below
void tst_QMetaObject::invokeMetaMember()
{
QtTestObject obj;
@ -661,17 +684,17 @@ void tst_QMetaObject::invokeMetaMember()
// Test nullptr
char *nullCharArray = nullptr;
const char *nullConstCharArray = nullptr;
QVERIFY(!QMetaObject::invokeMethod(nullptr, nullCharArray));
QVERIFY(!QMetaObject::invokeMethod(nullptr, nullConstCharArray));
QVERIFY(!QMetaObject::invokeMethod(nullptr, "sl0"));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection));
QVERIFY(!QMetaObject::invokeMethod(nullptr, nullCharArray Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(nullptr, nullConstCharArray Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(nullptr, "sl0" Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection, QGenericReturnArgument()));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection, QGenericReturnArgument()));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl0" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Q_ARG(QString, t1)));
@ -710,17 +733,19 @@ void tst_QMetaObject::invokeMetaMember()
Q_ARG(QString, t7), Q_ARG(QString, t8), Q_ARG(QString, t9)));
QCOMPARE(obj.slotResult, QString("sl9:123456789"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("sl11"));
QVERIFY(QMetaObject::invokeMethod(&obj, "testSender"));
QVERIFY(QMetaObject::invokeMethod(&obj, "testSender" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("0x0"));
QString refStr("whatever");
#ifdef USE_COMPAT_Q_ARG
QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", QGenericArgument("QString&", &refStr)));
QCOMPARE(obj.slotResult, QString("testReference:whatever"));
QCOMPARE(refStr, QString("gotcha"));
obj.slotResult.clear();
#endif
refStr = "whatever";
QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Q_ARG(QString&, refStr)));
QCOMPARE(obj.slotResult, QString("testReference:whatever"));
@ -800,7 +825,7 @@ void tst_QMetaObject::invokeMetaMember()
QCOMPARE(obj.slotResult, "sl17");
// test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Q_ARG(int, 1)));
QCOMPARE(obj.slotResult, QString("overloadedSlot:1"));
@ -808,7 +833,7 @@ void tst_QMetaObject::invokeMetaMember()
QCOMPARE(obj.slotResult, QString("overloadedSlot:1,42"));
//test signals
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Q_ARG(QString, "baba")));
@ -838,6 +863,177 @@ void tst_QMetaObject::invokeMetaMember()
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
}
// this is a copy-paste-adapt of the above
void tst_QMetaObject::invokeMetaMemberNoMacros()
{
QtTestObject obj;
QString t1("1"); QString t2("2"); QString t3("3"); QString t4("4"); QString t5("5");
QString t6("6"); QString t7("7"); QString t8("8"); QString t9("9"); QString t10("X");
QVERIFY(QMetaObject::invokeMethod(&obj, "sl0"));
QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", t1));
QCOMPARE(obj.slotResult, QString("sl1:1"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl2", qAsConst(t1), t2));
QCOMPARE(obj.slotResult, QString("sl2:12"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl3", t1, t2, t3));
QCOMPARE(obj.slotResult, QString("sl3:123"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl4", t1, t2, t3,
t4));
QCOMPARE(obj.slotResult, QString("sl4:1234"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl5", t1, t2, t3,
t4, QStringLiteral("5")));
QCOMPARE(obj.slotResult, QString("sl5:12345"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl6", t1, t2, t3,
t4, t5, t6));
QCOMPARE(obj.slotResult, QString("sl6:123456"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl7", t1, t2, t3,
t4, t5, t6,
t7));
QCOMPARE(obj.slotResult, QString("sl7:1234567"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl8", t1, t2, t3,
t4, t5, t6,
t7, t8));
QCOMPARE(obj.slotResult, QString("sl8:12345678"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl9", t1, t2, t3,
t4, t5, t6,
t7, t8, t9));
QCOMPARE(obj.slotResult, QString("sl9:123456789"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11"));
QCOMPARE(obj.slotResult, QString("sl11"));
QVERIFY(QMetaObject::invokeMethod(&obj, "testSender"));
QCOMPARE(obj.slotResult, QString("0x0"));
// this is not working for now
// QString refStr("whatever");
// QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", refStr));
// QCOMPARE(obj.slotResult, QString("testReference:whatever"));
// QCOMPARE(refStr, QString("gotcha"));
qint64 ll1 = -1;
quint64 ll2 = 0;
QVERIFY(QMetaObject::invokeMethod(&obj,
"testLongLong",
ll1,
ll2));
QCOMPARE(obj.slotResult, QString("testLongLong:-1,0"));
QString exp;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", qReturnArg(exp), QStringLiteral("bubu")));
QCOMPARE(exp, QString("yessir"));
QCOMPARE(obj.slotResult, QString("sl1:bubu"));
QObject *ptr = nullptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", qReturnArg(ptr)));
QCOMPARE(ptr, (QObject *)&obj);
QCOMPARE(obj.slotResult, QString("sl11"));
// try again with a space:
ptr = nullptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", qReturnArg(ptr)));
QCOMPARE(ptr, (QObject *)&obj);
QCOMPARE(obj.slotResult, QString("sl11"));
const char *ptr2 = nullptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl12", qReturnArg(ptr2)));
QVERIFY(ptr2 != nullptr);
QCOMPARE(obj.slotResult, QString("sl12"));
// try again with a space:
ptr2 = nullptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl12", qReturnArg(ptr2)));
QVERIFY(ptr2 != nullptr);
QCOMPARE(obj.slotResult, QString("sl12"));
// test w/ template args
QList<QString> returnValue, argument;
argument << QString("one") << QString("two") << QString("three");
QVERIFY(QMetaObject::invokeMethod(&obj, "sl13",
qReturnArg(returnValue),
argument));
QCOMPARE(returnValue, argument);
QCOMPARE(obj.slotResult, QString("sl13"));
// return qint64
qint64 return64;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl14",
qReturnArg(return64)));
QCOMPARE(return64, Q_INT64_C(123456789)*123456789);
QCOMPARE(obj.slotResult, QString("sl14"));
// pointers
QVERIFY(QMetaObject::invokeMethod(&obj, "sl15", &return64));
QCOMPARE(obj.slotResult, QString("sl15"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl16", getForwardDeclaredPointer()));
QCOMPARE(obj.slotResult, QString("sl16:notnull"));
obj.slotResult.clear();
qint64 *return64Ptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl15", qReturnArg(return64Ptr), &return64));
QCOMPARE(return64Ptr, &return64);
QCOMPARE(obj.slotResult, QString("sl15"));
obj.slotResult.clear();
MyForwardDeclaredType *forwardPtr = nullptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl16", qReturnArg(forwardPtr),
forwardPtr));
QCOMPARE(forwardPtr, getForwardDeclaredPointer());
QCOMPARE(obj.slotResult, QString("sl16:null"));
// forward-declared builtin
obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", getEasingCurve()));
QCOMPARE(obj.slotResult, "sl17");
// test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot"));
QCOMPARE(obj.slotResult, QString("overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", 1));
QCOMPARE(obj.slotResult, QString("overloadedSlot:1"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", 1, 42));
QCOMPARE(obj.slotResult, QString("overloadedSlot:1,42"));
//test signals
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0"));
QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", QStringLiteral("baba")));
QCOMPARE(obj.slotResult, QString("sl1:baba"));
exp.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", qReturnArg(exp), QStringLiteral("hehe")));
QCOMPARE(exp, QString("yessir"));
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::doesNotExist()");
QVERIFY(!QMetaObject::invokeMethod(&obj, "doesNotExist"));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString)(QString)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1(QString)", QStringLiteral("arg")));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)\n"
"Candidates are:\n sl3(QString,QString,QString)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl3", QStringLiteral("arg")));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString,QString,QString)\n"
"Candidates are:\n sl1(QString)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1", QStringLiteral("arg"), QStringLiteral("arg"), QStringLiteral("arg")));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::testReference(QString)\n"
"Candidates are:\n testReference(QString&)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "testReference", exp));
//should not have changed since last test.
QCOMPARE(exp, QString("yessir"));
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
}
void testFunction(){}
@ -908,7 +1104,7 @@ void tst_QMetaObject::invokeQueuedMetaMember()
{
QtTestObject obj;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl0", Qt::QueuedConnection));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl0", Qt::QueuedConnection Q_NO_ARG));
QVERIFY(obj.slotResult.isEmpty());
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl0"));
@ -942,7 +1138,7 @@ void tst_QMetaObject::invokeQueuedMetaMember()
QCOMPARE(obj.slotResult, "sl17");
// test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection Q_NO_ARG));
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection, Q_ARG(int, 1)));
@ -955,7 +1151,7 @@ void tst_QMetaObject::invokeQueuedMetaMember()
// signals
obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::QueuedConnection));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::QueuedConnection Q_NO_ARG));
QVERIFY(obj.slotResult.isEmpty());
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl0"));
@ -1013,6 +1209,119 @@ void tst_QMetaObject::invokeQueuedMetaMember()
QVERIFY(obj.slotResult.isEmpty());
}
// this is a copy-paste-adapt of the above
void tst_QMetaObject::invokeQueuedMetaMemberNoMacro()
{
QtTestObject obj;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl0", Qt::QueuedConnection));
QVERIFY(obj.slotResult.isEmpty());
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl0"));
obj.slotResult = QString();
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Qt::QueuedConnection, QString("hallo")));
QVERIFY(obj.slotResult.isEmpty());
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl1:hallo"));
obj.slotResult = QString();
QVERIFY(QMetaObject::invokeMethod(&obj, "sl9", Qt::QueuedConnection, QStringLiteral("1"), QStringLiteral("2"),
QStringLiteral("3"), QStringLiteral("4"), QStringLiteral("5"),
QStringLiteral("6"), QStringLiteral("7"), QStringLiteral("8"),
QStringLiteral("9")));
QVERIFY(obj.slotResult.isEmpty());
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl9:123456789"));
// pointers
qint64 return64;
obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sl15", Qt::QueuedConnection, &return64));
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl15"));
// forward-declared builtin
obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", Qt::QueuedConnection, getEasingCurve()));
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, "sl17");
// test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection));
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection, 1));
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("overloadedSlot:1"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection, 1, 42));
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("overloadedSlot:1,42"));
// signals
obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::QueuedConnection));
QVERIFY(obj.slotResult.isEmpty());
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Qt::QueuedConnection, QStringLiteral("gogo")));
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl1:gogo"));
QString exp;
QTest::ignoreMessage(QtWarningMsg, "QMetaMethod::invoke: Unable to invoke methods with return values in queued connections");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sig1", Qt::QueuedConnection, qReturnArg(exp),
QStringLiteral("nono")));
qint64 ll1 = -1;
quint64 ll2 = 0;
QVERIFY(QMetaObject::invokeMethod(&obj,
"testLongLong",
Qt::QueuedConnection,
ll1,
ll2));
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("testLongLong:-1,0"));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::testReference(QString)\n"
"Candidates are:\n testReference(QString&)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "testReference", exp));
QCOMPARE(obj.slotResult, QString("testLongLong:-1,0"));
QVERIFY(exp.isEmpty());
// this doesn't work yet
// QString refStr = "whatever";
// QTest::ignoreMessage(QtWarningMsg, "QMetaMethod::invoke: Unable to handle unregistered datatype 'QString&'");
// QVERIFY(!QMetaObject::invokeMethod(&obj, "testReference", Qt::QueuedConnection, Q_ARG(QString&, refStr)));
// QCOMPARE(refStr, "whatever");
obj.slotResult.clear();
{
const MyForwardDeclaredType &t = getForwardDeclaredType();
QTest::ignoreMessage(QtWarningMsg, "QMetaMethod::invoke: Unable to handle unregistered datatype 'MyForwardDeclaredType'");
QVERIFY(!QMetaObject::invokeMethod(&obj, "slotWithUnregisteredParameterType", Qt::QueuedConnection, t));
QVERIFY(obj.slotResult.isEmpty());
}
obj.slotResult.clear();
{
QString a1("Cannot happen");
const MyForwardDeclaredType &t = getForwardDeclaredType();
QTest::ignoreMessage(QtWarningMsg, "QMetaMethod::invoke: Unable to handle unregistered datatype 'MyForwardDeclaredType'");
QVERIFY(!QMetaObject::invokeMethod(&obj, "slotWithOneUnregisteredParameterType", Qt::QueuedConnection,
a1, t));
QVERIFY(obj.slotResult.isEmpty());
}
obj.slotResult.clear();
QTest::ignoreMessage(QtWarningMsg, "QMetaMethod::invoke: Unable to handle unregistered datatype 'MyForwardDeclaredType*'");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl16", Qt::QueuedConnection, getForwardDeclaredPointer()));
qApp->processEvents(QEventLoop::AllEvents);
QVERIFY(obj.slotResult.isEmpty());
}
void tst_QMetaObject::invokeQueuedPointer()
{
QtTestObject obj;
@ -1062,7 +1371,7 @@ void tst_QMetaObject::invokeQueuedPointer()
QCOMPARE(countedStructObjectsCount, 0);
}
// this test is duplicated below
void tst_QMetaObject::invokeBlockingQueuedMetaMember()
{
QThread t;
@ -1109,17 +1418,19 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
Q_ARG(QString, t7), Q_ARG(QString, t8), Q_ARG(QString, t9)));
QCOMPARE(obj.slotResult, QString("sl9:123456789"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Qt::BlockingQueuedConnection));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Qt::BlockingQueuedConnection Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("sl11"));
QVERIFY(QMetaObject::invokeMethod(&obj, "testSender", Qt::BlockingQueuedConnection));
QVERIFY(QMetaObject::invokeMethod(&obj, "testSender", Qt::BlockingQueuedConnection Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("0x0"));
QString refStr("whatever");
#ifdef USE_COMPAT_Q_ARG
QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Qt::BlockingQueuedConnection, QGenericArgument("QString&", &refStr)));
QCOMPARE(obj.slotResult, QString("testReference:whatever"));
QCOMPARE(refStr, QString("gotcha"));
obj.slotResult.clear();
#endif
refStr = "whatever";
QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Qt::BlockingQueuedConnection, Q_ARG(QString&, refStr)));
QCOMPARE(obj.slotResult, QString("testReference:whatever"));
@ -1200,7 +1511,7 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
QCOMPARE(obj.slotResult, "sl17");
// test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection, Q_ARG(int, 1)));
QCOMPARE(obj.slotResult, QString("overloadedSlot:1"));
@ -1208,7 +1519,7 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
QCOMPARE(obj.slotResult, QString("overloadedSlot:1,42"));
//test signals
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::BlockingQueuedConnection));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::BlockingQueuedConnection Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Qt::BlockingQueuedConnection, Q_ARG(QString, "baba")));
@ -1220,7 +1531,7 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::doesNotExist()");
QVERIFY(!QMetaObject::invokeMethod(&obj, "doesNotExist", Qt::BlockingQueuedConnection));
QVERIFY(!QMetaObject::invokeMethod(&obj, "doesNotExist", Qt::BlockingQueuedConnection Q_NO_ARG));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString)(QString)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1(QString)", Qt::BlockingQueuedConnection, Q_ARG(QString, "arg")));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)\n"
@ -1240,7 +1551,182 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
QVERIFY(QMetaObject::invokeMethod(&obj, "moveToThread", Qt::BlockingQueuedConnection, Q_ARG(QThread*, QThread::currentThread())));
t.quit();
QVERIFY(t.wait());
}
// this is a copy-paste-adapt of the above
void tst_QMetaObject::invokeBlockingQueuedMetaMemberNoMacros()
{
QThread t;
t.start();
QtTestObject obj;
obj.moveToThread(&t);
QString t1("1"); QString t2("2"); QString t3("3"); QString t4("4"); QString t5("5");
QString t6("6"); QString t7("7"); QString t8("8"); QString t9("9"); QString t10("X");
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Qt::BlockingQueuedConnection, t1));
QCOMPARE(obj.slotResult, QString("sl1:1"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl2", Qt::BlockingQueuedConnection, t1, t2));
QCOMPARE(obj.slotResult, QString("sl2:12"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl3", Qt::BlockingQueuedConnection, t1, t2, t3));
QCOMPARE(obj.slotResult, QString("sl3:123"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl4", Qt::BlockingQueuedConnection, t1, t2,
t3, t4));
QCOMPARE(obj.slotResult, QString("sl4:1234"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl5", Qt::BlockingQueuedConnection, t1, t2,
t3, t4, QStringLiteral("5")));
QCOMPARE(obj.slotResult, QString("sl5:12345"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl6", Qt::BlockingQueuedConnection, t1, t2,
t3, t4, t5, t6));
QCOMPARE(obj.slotResult, QString("sl6:123456"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl7", Qt::BlockingQueuedConnection, t1, t2,
t3, t4, t5, t6,
t7));
QCOMPARE(obj.slotResult, QString("sl7:1234567"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl8", Qt::BlockingQueuedConnection, t1, t2,
t3, t4, t5, t6,
t7, t8));
QCOMPARE(obj.slotResult, QString("sl8:12345678"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl9", Qt::BlockingQueuedConnection, t1, t2,
t3, t4, t5, t6,
t7, t8, t9));
QCOMPARE(obj.slotResult, QString("sl9:123456789"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Qt::BlockingQueuedConnection));
QCOMPARE(obj.slotResult, QString("sl11"));
QVERIFY(QMetaObject::invokeMethod(&obj, "testSender", Qt::BlockingQueuedConnection));
QCOMPARE(obj.slotResult, QString("0x0"));
// this is not working
// QString refStr("whatever");
// QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Qt::BlockingQueuedConnection, refStr));
// QCOMPARE(obj.slotResult, QString("testReference:whatever"));
// QCOMPARE(refStr, QString("gotcha"));
qint64 ll1 = -1;
quint64 ll2 = 0;
QVERIFY(QMetaObject::invokeMethod(&obj,
"testLongLong",
Qt::BlockingQueuedConnection,
ll1,
ll2));
QCOMPARE(obj.slotResult, QString("testLongLong:-1,0"));
QString exp;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Qt::BlockingQueuedConnection, qReturnArg(exp), QStringLiteral("bubu")));
QCOMPARE(exp, QString("yessir"));
QCOMPARE(obj.slotResult, QString("sl1:bubu"));
QObject *ptr = nullptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Qt::BlockingQueuedConnection, qReturnArg(ptr)));
QCOMPARE(ptr, (QObject *)&obj);
QCOMPARE(obj.slotResult, QString("sl11"));
// try again with a space:
ptr = nullptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11", Qt::BlockingQueuedConnection, qReturnArg(ptr)));
QCOMPARE(ptr, (QObject *)&obj);
QCOMPARE(obj.slotResult, QString("sl11"));
const char *ptr2 = 0;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl12", Qt::BlockingQueuedConnection, qReturnArg(ptr2)));
QVERIFY(ptr2 != 0);
QCOMPARE(obj.slotResult, QString("sl12"));
// try again with a space:
ptr2 = 0;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl12", Qt::BlockingQueuedConnection, qReturnArg(ptr2)));
QVERIFY(ptr2 != 0);
QCOMPARE(obj.slotResult, QString("sl12"));
// test w/ template args
QList<QString> returnValue, argument;
argument << QString("one") << QString("two") << QString("three");
QVERIFY(QMetaObject::invokeMethod(&obj, "sl13", Qt::BlockingQueuedConnection,
qReturnArg(returnValue),
argument));
QCOMPARE(returnValue, argument);
QCOMPARE(obj.slotResult, QString("sl13"));
// return qint64
qint64 return64;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl14", Qt::BlockingQueuedConnection,
qReturnArg(return64)));
QCOMPARE(return64, Q_INT64_C(123456789)*123456789);
QCOMPARE(obj.slotResult, QString("sl14"));
// pointers
QVERIFY(QMetaObject::invokeMethod(&obj, "sl15", Qt::BlockingQueuedConnection, &return64));
QCOMPARE(obj.slotResult, QString("sl15"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl16", Qt::BlockingQueuedConnection, getForwardDeclaredPointer()));
QCOMPARE(obj.slotResult, QString("sl16:notnull"));
obj.slotResult.clear();
qint64 *return64Ptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl15", Qt::BlockingQueuedConnection, qReturnArg(return64Ptr), &return64));
QCOMPARE(return64Ptr, &return64);
QCOMPARE(obj.slotResult, QString("sl15"));
obj.slotResult.clear();
MyForwardDeclaredType *forwardPtr = nullptr;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl16", Qt::BlockingQueuedConnection, qReturnArg(forwardPtr),
forwardPtr));
QCOMPARE(forwardPtr, getForwardDeclaredPointer());
QCOMPARE(obj.slotResult, QString("sl16:null"));
// forward-declared builtin
obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", Qt::BlockingQueuedConnection, getEasingCurve()));
QCOMPARE(obj.slotResult, "sl17");
// test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection));
QCOMPARE(obj.slotResult, QString("overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection, 1));
QCOMPARE(obj.slotResult, QString("overloadedSlot:1"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection, 1, 42));
QCOMPARE(obj.slotResult, QString("overloadedSlot:1,42"));
//test signals
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::BlockingQueuedConnection));
QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Qt::BlockingQueuedConnection, QStringLiteral("baba")));
QCOMPARE(obj.slotResult, QString("sl1:baba"));
exp.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Qt::BlockingQueuedConnection, qReturnArg(exp), QStringLiteral("hehe")));
QCOMPARE(exp, QString("yessir"));
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::doesNotExist()");
QVERIFY(!QMetaObject::invokeMethod(&obj, "doesNotExist", Qt::BlockingQueuedConnection));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString)(QString)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1(QString)", Qt::BlockingQueuedConnection, QStringLiteral("arg")));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)\n"
"Candidates are:\n sl3(QString,QString,QString)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl3", Qt::BlockingQueuedConnection, QStringLiteral("arg")));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString,QString,QString)\n"
"Candidates are:\n sl1(QString)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1", Qt::BlockingQueuedConnection, QStringLiteral("arg"), QStringLiteral("arg"), QStringLiteral("arg")));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::testReference(QString)\n"
"Candidates are:\n testReference(QString&)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "testReference", Qt::BlockingQueuedConnection, exp));
//should not have changed since last test.
QCOMPARE(exp, QString("yessir"));
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
QVERIFY(QMetaObject::invokeMethod(&obj, "moveToThread", Qt::BlockingQueuedConnection, QThread::currentThread()));
t.quit();
QVERIFY(t.wait());
}
void tst_QMetaObject::invokeBlockingQueuedPointer()
@ -1366,6 +1852,10 @@ void tst_QMetaObject::invokeCustomTypes()
QCOMPARE(obj.sum, 0);
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Q_ARG(MyType, tp)));
QCOMPARE(obj.sum, 3);
obj.sum = 0;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", tp));
QCOMPARE(obj.sum, 3);
}
namespace NamespaceWithConstructibleClass
@ -1381,13 +1871,16 @@ public:
}
// this test is duplicated below
void tst_QMetaObject::invokeMetaConstructor()
{
const QMetaObject *mo = &QtTestObject::staticMetaObject;
#ifdef USE_COMPAT_Q_ARG
{
QObject *obj = mo->newInstance();
QObject *obj = mo->newInstance(QGenericArgument());
QVERIFY(!obj);
}
#endif
{
QtTestObject obj;
QObject *obj2 = mo->newInstance(Q_ARG(QObject*, &obj));
@ -1430,6 +1923,38 @@ void tst_QMetaObject::invokeMetaConstructor()
}
}
// this is a copy-paste-adapt of the above
void tst_QMetaObject::invokeMetaConstructorNoMacro()
{
const QMetaObject *mo = &QtTestObject::staticMetaObject;
{
QObject *obj = mo->newInstance();
QVERIFY(!obj);
}
{
QtTestObject obj;
QObject *obj2 = mo->newInstance(static_cast<QObject *>(&obj));
QVERIFY(obj2 != 0);
QCOMPARE(obj2->parent(), (QObject*)&obj);
QVERIFY(qobject_cast<QtTestObject*>(obj2) != 0);
}
// class in namespace
const QMetaObject *nsmo = &NamespaceWithConstructibleClass::ConstructibleClass::staticMetaObject;
{
QtTestObject obj;
QObject *obj2 = nsmo->newInstance(static_cast<QObject *>(&obj));
QVERIFY(obj2 != 0);
QCOMPARE(obj2->parent(), (QObject*)&obj);
QVERIFY(qobject_cast<NamespaceWithConstructibleClass::ConstructibleClass*>(obj2) != 0);
}
// gadget shouldn't return a valid pointer
{
QCOMPARE(MyGadget::staticMetaObject.constructorCount(), 1);
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::newInstance: type MyGadget does not inherit QObject");
QVERIFY(!MyGadget::staticMetaObject.newInstance());
}
}
void tst_QMetaObject::invokeTypedefTypes()
{
qRegisterMetaType<CustomString>("CustomString");
@ -1457,6 +1982,14 @@ void tst_QMetaObject::invokeException()
QFAIL("Did not throw");
} catch(ObjectException &) {}
QCOMPARE(countedStructObjectsCount, 0);
try {
CountedStruct s;
QVERIFY(QMetaObject::invokeMethod(&obj, "throwingSlot", qReturnArg(s),
s, s));
QFAIL("Did not throw");
} catch(ObjectException &) {}
QCOMPARE(countedStructObjectsCount, 0);
#else
QSKIP("Needs exceptions");
#endif
@ -1478,6 +2011,18 @@ void tst_QMetaObject::invokeQueuedAutoRegister()
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult,
QString("slotWithRegistrableArgument:myShared-myShared-myShared-myShared-00"));
obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(
&obj, "slotWithRegistrableArgument", Qt::QueuedConnection,
shared.data(), QPointer<QtTestObject>(shared.data()),
QSharedPointer<QtTestObject>(shared), QWeakPointer<QtTestObject>(shared),
QList<QtTestObject *>(),
QList<QtTestObject *>()));
QVERIFY(obj.slotResult.isEmpty());
qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult,
QString("slotWithRegistrableArgument:myShared-myShared-myShared-myShared-00"));
}
void tst_QMetaObject::normalizedSignature_data()
@ -1811,6 +2356,7 @@ void tst_QMetaObject::classInfo()
QCOMPARE(QLatin1String(b.metaObject()->classInfo(index).value()), QLatin1String("Christopher Pike"));
}
// this test is duplicated below
void tst_QMetaObject::metaMethod()
{
QString str("foo");
@ -1863,6 +2409,59 @@ void tst_QMetaObject::metaMethod()
QCOMPARE(obj.slotResult, QString("sl13"));
}
// this is a copy-paste-adapt of the above
void tst_QMetaObject::metaMethodNoMacro()
{
QString str("foo");
QString ret("bar");
QMetaMethod method;
QVERIFY(!method.invoke(this));
QVERIFY(!method.invoke(this, str));
QVERIFY(!method.invoke(this, qReturnArg(ret), str));
QCOMPARE(str, QString("foo"));
QCOMPARE(ret, QString("bar"));
QtTestObject obj;
QString t1("1"); QString t2("2"); QString t3("3"); QString t4("4"); QString t5("5");
QString t6("6"); QString t7("7"); QString t8("8"); QString t9("9"); QString t10("X");
int index = QtTestObject::staticMetaObject.indexOfMethod("sl5(QString,QString,QString,QString,QString)");
QVERIFY(index > 0);
method = QtTestObject::staticMetaObject.method(index);
//wrong args
QTest::ignoreMessage(QtWarningMsg, "QMetaMethod::invoke: too few arguments (5) in call to QtTestObject::sl5(QString,QString,QString,QString,QString)");
QVERIFY(!method.invoke(&obj, QStringLiteral("1"), QStringLiteral("2"), QStringLiteral("3"), QStringLiteral("4")));
//QVERIFY(!method.invoke(&obj, "1", "2", "3", "4", "5", "6"));
//QVERIFY(!method.invoke(&obj, "1", "2", "3", "4", 5));
QTest::ignoreMessage(QtWarningMsg, "QMetaMethod::invokeMethod: return type mismatch for method "
"QtTestObject::sl5(QString,QString,QString,QString,QString): "
"cannot convert from void to QString during invocation");
QVERIFY(!method.invoke(&obj, qReturnArg(ret), QStringLiteral("1"), QStringLiteral("2"), QStringLiteral("3"), QStringLiteral("4"), QStringLiteral("5")));
//wrong object
//QVERIFY(!method.invoke(this, "1", "2", "3", "4", "5"));
QVERIFY(!method.invoke(0, QStringLiteral("1"), QStringLiteral("2"), QStringLiteral("3"), QStringLiteral("4"), QStringLiteral("5")));
QCOMPARE(ret, QString("bar"));
QCOMPARE(obj.slotResult, QString());
QVERIFY(method.invoke(&obj, QStringLiteral("1"), QStringLiteral("2"), QStringLiteral("3"), QStringLiteral("4"), QStringLiteral("5")));
QCOMPARE(obj.slotResult, QString("sl5:12345"));
index = QtTestObject::staticMetaObject.indexOfMethod("sl13(QList<QString>)");
QVERIFY(index > 0);
QMetaMethod sl13 = QtTestObject::staticMetaObject.method(index);
QList<QString> returnValue, argument;
argument << QString("one") << QString("two") << QString("three");
//wrong object
//QVERIFY(!sl13.invoke(this, qReturnArg(returnValue), argument));
QVERIFY(!sl13.invoke(0, qReturnArg(returnValue), argument));
QVERIFY(returnValue.isEmpty());
QVERIFY(sl13.invoke(&obj, qReturnArg(returnValue), argument));
QCOMPARE(returnValue, argument);
QCOMPARE(obj.slotResult, QString("sl13"));
}
void tst_QMetaObject::indexOfMethod_data()
{
QTest::addColumn<QObject *>("object");