diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2022-08-01 11:12:20 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2022-08-30 22:29:07 -0300 |
commit | fe92b080658f0d8609e2a2a69e5ec2b51dd7bf9d (patch) | |
tree | c2e3db61402091538966b89547dbf680e3948be9 | |
parent | 191419e98040d7aa7c306d3ec2776d5c143d8c2d (diff) |
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>
-rw-r--r-- | src/corelib/kernel/qmetaobject.cpp | 98 | ||||
-rw-r--r-- | src/corelib/kernel/qmetaobject.h | 51 | ||||
-rw-r--r-- | src/corelib/kernel/qobjectdefs.h | 146 | ||||
-rw-r--r-- | tests/auto/corelib/global/qlogging/tst_qlogging.cpp | 4 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt | 8 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp | 643 |
6 files changed, 890 insertions, 60 deletions
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index d007d05050..7142a17050 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -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; diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 0740acef60..735f26caa2 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -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); diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 8309e43d89..7d324e4b8e 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -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; }; diff --git a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp index df0cb5ae77..be27ab65b9 100644 --- a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp +++ b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp @@ -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 diff --git a/tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt b/tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt index 30368e5c52..a4361d4779 100644 --- a/tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt @@ -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 +) diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 005b58fcb8..26945b727e 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -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"); |