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

View File

@ -45,6 +45,7 @@ public:
inline const QMetaObject *enclosingMetaObject() const { return mobj; } inline const QMetaObject *enclosingMetaObject() const { return mobj; }
#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
bool invoke(QObject *object, bool invoke(QObject *object,
Qt::ConnectionType connectionType, Qt::ConnectionType connectionType,
QGenericReturnArgument returnValue, QGenericReturnArgument returnValue,
@ -76,7 +77,7 @@ public:
} }
inline bool invoke(QObject *object, inline bool invoke(QObject *object,
Qt::ConnectionType connectionType, Qt::ConnectionType connectionType,
QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(), QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(),
@ -91,7 +92,7 @@ public:
val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
} }
inline bool invoke(QObject *object, inline bool invoke(QObject *object,
QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(), QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(),
@ -118,7 +119,7 @@ public:
QGenericArgument val8 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const; QGenericArgument val9 = QGenericArgument()) const;
inline bool invokeOnGadget(void *gadget, inline bool invokeOnGadget(void *gadget,
QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(), QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(),
@ -132,6 +133,48 @@ public:
return invokeOnGadget(gadget, QGenericReturnArgument(), return invokeOnGadget(gadget, QGenericReturnArgument(),
val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); 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; } inline bool isValid() const { return mobj != nullptr; }
@ -146,6 +189,8 @@ public:
} }
private: 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 fromSignalImpl(const QMetaObject *, void **);
static QMetaMethod fromRelativeMethodIndex(const QMetaObject *mobj, int index); static QMetaMethod fromRelativeMethodIndex(const QMetaObject *mobj, int index);
static QMetaMethod fromRelativeConstructorIndex(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
#endif // QT_NO_META_MACROS #endif // QT_NO_META_MACROS
#define Q_ARG(type, data) QArgument<type >(#type, data) #define Q_ARG(Type, data) QtPrivate::Invoke::argument<Type>(QT_STRINGIFY(Type), data)
#define Q_RETURN_ARG(type, data) QReturnArgument<type >(#type, data) #define Q_RETURN_ARG(Type, data) QtPrivate::Invoke::returnArgument<Type>(QT_STRINGIFY(Type), data)
class QObject; class QObject;
class QMetaMethod; class QMetaMethod;
@ -70,6 +70,7 @@ class QMetaClassInfo;
namespace QtPrivate { namespace QtPrivate {
class QMetaTypeInterface; class QMetaTypeInterface;
template<typename T> constexpr auto typenameHelper();
} }
struct QMethodRawArguments struct QMethodRawArguments
@ -77,6 +78,7 @@ struct QMethodRawArguments
void **arguments; void **arguments;
}; };
#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
class Q_CORE_EXPORT QGenericArgument class Q_CORE_EXPORT QGenericArgument
{ {
public: public:
@ -124,6 +126,91 @@ public:
: QGenericReturnArgument(aName, static_cast<void *>(&aData)) : 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 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, const QMetaObject *, int local_signal_index, void **argv);
static void activate(QObject *sender, int signal_offset, 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, static bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType, Qt::ConnectionType,
QGenericReturnArgument ret, QGenericReturnArgument ret,
@ -224,7 +312,7 @@ struct Q_CORE_EXPORT QMetaObject
static inline bool invokeMethod(QObject *obj, const char *member, static inline bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType type, Qt::ConnectionType type,
QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(), QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(),
@ -240,7 +328,7 @@ struct Q_CORE_EXPORT QMetaObject
} }
static inline bool invokeMethod(QObject *obj, const char *member, static inline bool invokeMethod(QObject *obj, const char *member,
QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(), QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(),
@ -254,6 +342,41 @@ struct Q_CORE_EXPORT QMetaObject
return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0, return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
val1, val2, val3, val4, val5, val6, val7, val8, val9); 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 #ifdef Q_CLANG_QDOC
template<typename Functor, typename FunctorReturnType> template<typename Functor, typename FunctorReturnType>
@ -336,7 +459,8 @@ struct Q_CORE_EXPORT QMetaObject
#endif #endif
QObject *newInstance(QGenericArgument val0 = QGenericArgument(nullptr), #if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
QObject *newInstance(QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(), QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(),
@ -346,6 +470,14 @@ struct Q_CORE_EXPORT QMetaObject
QGenericArgument val7 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const; 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 { enum Call {
InvokeMetaMethod, InvokeMetaMethod,
@ -405,7 +537,11 @@ struct Q_CORE_EXPORT QMetaObject
} d; } d;
private: 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 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; friend class QTimer;
}; };

View File

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

View File

@ -20,3 +20,11 @@ qt_internal_add_test(tst_qmetaobject
Qt::CorePrivate 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" #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 #ifdef QEASINGCURVE_H
# error "Please make sure qeasingcurve.h is not #include'd here! " \ # error "Please make sure qeasingcurve.h is not #include'd here! " \
"We need QEasingCurve to be only forward-declared." "We need QEasingCurve to be only forward-declared."
@ -279,13 +296,17 @@ public:
private slots: private slots:
void connectSlotsByName(); void connectSlotsByName();
void invokeMetaMember(); void invokeMetaMember();
void invokeMetaMemberNoMacros();
void invokePointer(); void invokePointer();
void invokeQueuedMetaMember(); void invokeQueuedMetaMember();
void invokeQueuedMetaMemberNoMacro();
void invokeQueuedPointer(); void invokeQueuedPointer();
void invokeBlockingQueuedMetaMember(); void invokeBlockingQueuedMetaMember();
void invokeBlockingQueuedMetaMemberNoMacros();
void invokeBlockingQueuedPointer(); void invokeBlockingQueuedPointer();
void invokeCustomTypes(); void invokeCustomTypes();
void invokeMetaConstructor(); void invokeMetaConstructor();
void invokeMetaConstructorNoMacro();
void invokeTypedefTypes(); void invokeTypedefTypes();
void invokeException(); void invokeException();
void invokeQueuedAutoRegister(); void invokeQueuedAutoRegister();
@ -307,6 +328,7 @@ private slots:
void classInfo(); void classInfo();
void metaMethod(); void metaMethod();
void metaMethodNoMacro();
void indexOfMethod_data(); void indexOfMethod_data();
void indexOfMethod(); void indexOfMethod();
@ -651,6 +673,7 @@ void QtTestObject::staticFunction0()
qint64 QtTestObject::staticFunction1() qint64 QtTestObject::staticFunction1()
{ staticResult = "staticFunction1"; return Q_INT64_C(123456789)*123456789; } { staticResult = "staticFunction1"; return Q_INT64_C(123456789)*123456789; }
// this test is duplicated below
void tst_QMetaObject::invokeMetaMember() void tst_QMetaObject::invokeMetaMember()
{ {
QtTestObject obj; QtTestObject obj;
@ -661,17 +684,17 @@ void tst_QMetaObject::invokeMetaMember()
// Test nullptr // Test nullptr
char *nullCharArray = nullptr; char *nullCharArray = nullptr;
const char *nullConstCharArray = nullptr; const char *nullConstCharArray = nullptr;
QVERIFY(!QMetaObject::invokeMethod(nullptr, nullCharArray)); QVERIFY(!QMetaObject::invokeMethod(nullptr, nullCharArray Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(nullptr, nullConstCharArray)); QVERIFY(!QMetaObject::invokeMethod(nullptr, nullConstCharArray Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(nullptr, "sl0")); QVERIFY(!QMetaObject::invokeMethod(nullptr, "sl0" Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray)); QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray)); QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection)); QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection)); QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection Q_NO_ARG));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection, QGenericReturnArgument())); QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection, QGenericReturnArgument()));
QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, 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")); QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Q_ARG(QString, t1))); 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))); Q_ARG(QString, t7), Q_ARG(QString, t8), Q_ARG(QString, t9)));
QCOMPARE(obj.slotResult, QString("sl9:123456789")); QCOMPARE(obj.slotResult, QString("sl9:123456789"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl11")); QVERIFY(QMetaObject::invokeMethod(&obj, "sl11" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("sl11")); QCOMPARE(obj.slotResult, QString("sl11"));
QVERIFY(QMetaObject::invokeMethod(&obj, "testSender")); QVERIFY(QMetaObject::invokeMethod(&obj, "testSender" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("0x0")); QCOMPARE(obj.slotResult, QString("0x0"));
QString refStr("whatever"); QString refStr("whatever");
#ifdef USE_COMPAT_Q_ARG
QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", QGenericArgument("QString&", &refStr))); QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", QGenericArgument("QString&", &refStr)));
QCOMPARE(obj.slotResult, QString("testReference:whatever")); QCOMPARE(obj.slotResult, QString("testReference:whatever"));
QCOMPARE(refStr, QString("gotcha")); QCOMPARE(refStr, QString("gotcha"));
obj.slotResult.clear(); obj.slotResult.clear();
#endif
refStr = "whatever"; refStr = "whatever";
QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Q_ARG(QString&, refStr))); QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Q_ARG(QString&, refStr)));
QCOMPARE(obj.slotResult, QString("testReference:whatever")); QCOMPARE(obj.slotResult, QString("testReference:whatever"));
@ -800,7 +825,7 @@ void tst_QMetaObject::invokeMetaMember()
QCOMPARE(obj.slotResult, "sl17"); QCOMPARE(obj.slotResult, "sl17");
// test overloads // test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot")); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("overloadedSlot")); QCOMPARE(obj.slotResult, QString("overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Q_ARG(int, 1))); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Q_ARG(int, 1)));
QCOMPARE(obj.slotResult, QString("overloadedSlot:1")); QCOMPARE(obj.slotResult, QString("overloadedSlot:1"));
@ -808,7 +833,7 @@ void tst_QMetaObject::invokeMetaMember()
QCOMPARE(obj.slotResult, QString("overloadedSlot:1,42")); QCOMPARE(obj.slotResult, QString("overloadedSlot:1,42"));
//test signals //test signals
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0")); QVERIFY(QMetaObject::invokeMethod(&obj, "sig0" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("sl0")); QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Q_ARG(QString, "baba"))); QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Q_ARG(QString, "baba")));
@ -838,6 +863,177 @@ void tst_QMetaObject::invokeMetaMember()
QCOMPARE(obj.slotResult, QString("sl1:hehe")); 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(){} void testFunction(){}
@ -908,7 +1104,7 @@ void tst_QMetaObject::invokeQueuedMetaMember()
{ {
QtTestObject obj; QtTestObject obj;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl0", Qt::QueuedConnection)); QVERIFY(QMetaObject::invokeMethod(&obj, "sl0", Qt::QueuedConnection Q_NO_ARG));
QVERIFY(obj.slotResult.isEmpty()); QVERIFY(obj.slotResult.isEmpty());
qApp->processEvents(QEventLoop::AllEvents); qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl0")); QCOMPARE(obj.slotResult, QString("sl0"));
@ -942,7 +1138,7 @@ void tst_QMetaObject::invokeQueuedMetaMember()
QCOMPARE(obj.slotResult, "sl17"); QCOMPARE(obj.slotResult, "sl17");
// test overloads // test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection)); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection Q_NO_ARG));
qApp->processEvents(QEventLoop::AllEvents); qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("overloadedSlot")); QCOMPARE(obj.slotResult, QString("overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection, Q_ARG(int, 1))); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection, Q_ARG(int, 1)));
@ -955,7 +1151,7 @@ void tst_QMetaObject::invokeQueuedMetaMember()
// signals // signals
obj.slotResult.clear(); obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::QueuedConnection)); QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::QueuedConnection Q_NO_ARG));
QVERIFY(obj.slotResult.isEmpty()); QVERIFY(obj.slotResult.isEmpty());
qApp->processEvents(QEventLoop::AllEvents); qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl0")); QCOMPARE(obj.slotResult, QString("sl0"));
@ -1013,6 +1209,119 @@ void tst_QMetaObject::invokeQueuedMetaMember()
QVERIFY(obj.slotResult.isEmpty()); 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() void tst_QMetaObject::invokeQueuedPointer()
{ {
QtTestObject obj; QtTestObject obj;
@ -1062,7 +1371,7 @@ void tst_QMetaObject::invokeQueuedPointer()
QCOMPARE(countedStructObjectsCount, 0); QCOMPARE(countedStructObjectsCount, 0);
} }
// this test is duplicated below
void tst_QMetaObject::invokeBlockingQueuedMetaMember() void tst_QMetaObject::invokeBlockingQueuedMetaMember()
{ {
QThread t; QThread t;
@ -1109,17 +1418,19 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
Q_ARG(QString, t7), Q_ARG(QString, t8), Q_ARG(QString, t9))); Q_ARG(QString, t7), Q_ARG(QString, t8), Q_ARG(QString, t9)));
QCOMPARE(obj.slotResult, QString("sl9:123456789")); 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")); 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")); QCOMPARE(obj.slotResult, QString("0x0"));
QString refStr("whatever"); QString refStr("whatever");
#ifdef USE_COMPAT_Q_ARG
QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Qt::BlockingQueuedConnection, QGenericArgument("QString&", &refStr))); QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Qt::BlockingQueuedConnection, QGenericArgument("QString&", &refStr)));
QCOMPARE(obj.slotResult, QString("testReference:whatever")); QCOMPARE(obj.slotResult, QString("testReference:whatever"));
QCOMPARE(refStr, QString("gotcha")); QCOMPARE(refStr, QString("gotcha"));
obj.slotResult.clear(); obj.slotResult.clear();
#endif
refStr = "whatever"; refStr = "whatever";
QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Qt::BlockingQueuedConnection, Q_ARG(QString&, refStr))); QVERIFY(QMetaObject::invokeMethod(&obj, "testReference", Qt::BlockingQueuedConnection, Q_ARG(QString&, refStr)));
QCOMPARE(obj.slotResult, QString("testReference:whatever")); QCOMPARE(obj.slotResult, QString("testReference:whatever"));
@ -1200,7 +1511,7 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
QCOMPARE(obj.slotResult, "sl17"); QCOMPARE(obj.slotResult, "sl17");
// test overloads // test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection)); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("overloadedSlot")); QCOMPARE(obj.slotResult, QString("overloadedSlot"));
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection, Q_ARG(int, 1))); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection, Q_ARG(int, 1)));
QCOMPARE(obj.slotResult, QString("overloadedSlot:1")); QCOMPARE(obj.slotResult, QString("overloadedSlot:1"));
@ -1208,7 +1519,7 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
QCOMPARE(obj.slotResult, QString("overloadedSlot:1,42")); QCOMPARE(obj.slotResult, QString("overloadedSlot:1,42"));
//test signals //test signals
QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::BlockingQueuedConnection)); QVERIFY(QMetaObject::invokeMethod(&obj, "sig0", Qt::BlockingQueuedConnection Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("sl0")); QCOMPARE(obj.slotResult, QString("sl0"));
QVERIFY(QMetaObject::invokeMethod(&obj, "sig1", Qt::BlockingQueuedConnection, Q_ARG(QString, "baba"))); 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")); QCOMPARE(obj.slotResult, QString("sl1:hehe"));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::doesNotExist()"); 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)"); QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl1(QString)(QString)");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1(QString)", Qt::BlockingQueuedConnection, Q_ARG(QString, "arg"))); QVERIFY(!QMetaObject::invokeMethod(&obj, "sl1(QString)", Qt::BlockingQueuedConnection, Q_ARG(QString, "arg")));
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::invokeMethod: No such method QtTestObject::sl3(QString)\n" 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()))); QVERIFY(QMetaObject::invokeMethod(&obj, "moveToThread", Qt::BlockingQueuedConnection, Q_ARG(QThread*, QThread::currentThread())));
t.quit(); t.quit();
QVERIFY(t.wait()); 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() void tst_QMetaObject::invokeBlockingQueuedPointer()
@ -1366,6 +1852,10 @@ void tst_QMetaObject::invokeCustomTypes()
QCOMPARE(obj.sum, 0); QCOMPARE(obj.sum, 0);
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Q_ARG(MyType, tp))); QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", Q_ARG(MyType, tp)));
QCOMPARE(obj.sum, 3); QCOMPARE(obj.sum, 3);
obj.sum = 0;
QVERIFY(QMetaObject::invokeMethod(&obj, "sl1", tp));
QCOMPARE(obj.sum, 3);
} }
namespace NamespaceWithConstructibleClass namespace NamespaceWithConstructibleClass
@ -1381,13 +1871,16 @@ public:
} }
// this test is duplicated below
void tst_QMetaObject::invokeMetaConstructor() void tst_QMetaObject::invokeMetaConstructor()
{ {
const QMetaObject *mo = &QtTestObject::staticMetaObject; const QMetaObject *mo = &QtTestObject::staticMetaObject;
#ifdef USE_COMPAT_Q_ARG
{ {
QObject *obj = mo->newInstance(); QObject *obj = mo->newInstance(QGenericArgument());
QVERIFY(!obj); QVERIFY(!obj);
} }
#endif
{ {
QtTestObject obj; QtTestObject obj;
QObject *obj2 = mo->newInstance(Q_ARG(QObject*, &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() void tst_QMetaObject::invokeTypedefTypes()
{ {
qRegisterMetaType<CustomString>("CustomString"); qRegisterMetaType<CustomString>("CustomString");
@ -1457,6 +1982,14 @@ void tst_QMetaObject::invokeException()
QFAIL("Did not throw"); QFAIL("Did not throw");
} catch(ObjectException &) {} } catch(ObjectException &) {}
QCOMPARE(countedStructObjectsCount, 0); 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 #else
QSKIP("Needs exceptions"); QSKIP("Needs exceptions");
#endif #endif
@ -1478,6 +2011,18 @@ void tst_QMetaObject::invokeQueuedAutoRegister()
qApp->processEvents(QEventLoop::AllEvents); qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QCOMPARE(obj.slotResult,
QString("slotWithRegistrableArgument:myShared-myShared-myShared-myShared-00")); 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() void tst_QMetaObject::normalizedSignature_data()
@ -1811,6 +2356,7 @@ void tst_QMetaObject::classInfo()
QCOMPARE(QLatin1String(b.metaObject()->classInfo(index).value()), QLatin1String("Christopher Pike")); QCOMPARE(QLatin1String(b.metaObject()->classInfo(index).value()), QLatin1String("Christopher Pike"));
} }
// this test is duplicated below
void tst_QMetaObject::metaMethod() void tst_QMetaObject::metaMethod()
{ {
QString str("foo"); QString str("foo");
@ -1863,6 +2409,59 @@ void tst_QMetaObject::metaMethod()
QCOMPARE(obj.slotResult, QString("sl13")); 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() void tst_QMetaObject::indexOfMethod_data()
{ {
QTest::addColumn<QObject *>("object"); QTest::addColumn<QObject *>("object");