QMetaObject: pass the QMetaTypes in variadic invoke/newInstance

[ChangeLog][QtCore][Meta Object] QMetaMethod::invoke(),
QMetaObject::invokeMethod(), and QMetaObject::newInstance() are no
longer limited to 10 arguments.

[ChangeLog][QtCore][Meta Object] The use of the Q_ARG macro is no longer
necessary when using QMetaMethod::invoke(), QMetaObject::invokeMethod(),
and QMetaObject::newInstance(). Types may now be passed
directly. Similarly, Q_RETURN_ARG can be replaced by the free function
qReturnArg().

[ChangeLog][Potentially Source-Incompatible Changes]
QMetaMethod::invoke(), QMetaObject::invokeMethod(), and
QMetaObject::newInstance() no longer support passing forward-declared
types in the argument list (it was possible to pass them by
const-ref). From Qt 6.5 onwards, all types in the argument list must be
fully defined.

[ChangeLog][Potentially Source-Incompatible Changes] Attempting to use
the internal types QArgument, QReturnArgument, QGenericArgument, or
QGenericReturnArgument directly with QMetaMethod::invoke(),
QMetaObject::invokeMethod() or QMetaObject::newInstance() may fail to
compile. Those are internal types that were never meant to be used
directly and will be removed in Qt 7. If really necessary, ensure all
arguments passed to those functions are directly using those classes and
not mixed with Q_ARG and Q_RETURN_ARG. Implementations of bindings to
other languages should contact the Qt development mailing list to
discuss options.

Change-Id: I36b24183fbd041179f2ffffd1701e3e8e47e0fba
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Thiago Macieira 2022-07-15 19:01:00 -07:00
parent ba8c4b4ac6
commit 0380dd5051
7 changed files with 133 additions and 124 deletions

View File

@ -241,11 +241,12 @@ QObject *QMetaObject::newInstance(QGenericArgument val0,
break; break;
} }
return newInstanceImpl(this, paramCount, parameters, typeNames); return newInstanceImpl(this, paramCount, parameters, typeNames, nullptr);
} }
QObject *QMetaObject::newInstanceImpl(const QMetaObject *mobj, qsizetype paramCount, QObject *QMetaObject::newInstanceImpl(const QMetaObject *mobj, qsizetype paramCount,
const void **parameters, const char **typeNames) const void **parameters, const char **typeNames,
const QtPrivate::QMetaTypeInterface **metaTypes)
{ {
if (!mobj->inherits(&QObject::staticMetaObject)) { if (!mobj->inherits(&QObject::staticMetaObject)) {
qWarning("QMetaObject::newInstance: type %s does not inherit QObject", mobj->className()); qWarning("QMetaObject::newInstance: type %s does not inherit QObject", mobj->className());
@ -262,6 +263,8 @@ QT_WARNING_DISABLE_GCC("-Wdangling-pointer")
QMetaType returnValueMetaType = QMetaType::fromType<decltype(returnValue)>(); QMetaType returnValueMetaType = QMetaType::fromType<decltype(returnValue)>();
parameters[0] = &returnValue; parameters[0] = &returnValue;
typeNames[0] = returnValueMetaType.name(); typeNames[0] = returnValueMetaType.name();
if (metaTypes)
metaTypes[0] = returnValueMetaType.iface();
QT_WARNING_POP QT_WARNING_POP
@ -275,7 +278,7 @@ QT_WARNING_POP
// attempt to call // attempt to call
QMetaMethodPrivate::InvokeFailReason r = QMetaMethodPrivate::InvokeFailReason r =
QMetaMethodPrivate::invokeImpl(m, nullptr, Qt::DirectConnection, paramCount, QMetaMethodPrivate::invokeImpl(m, nullptr, Qt::DirectConnection, paramCount,
parameters, typeNames); parameters, typeNames, metaTypes);
if (r == QMetaMethodPrivate::InvokeFailReason::None) if (r == QMetaMethodPrivate::InvokeFailReason::None)
return returnValue; return returnValue;
if (int(r) < 0) if (int(r) < 0)
@ -1346,7 +1349,8 @@ QByteArray QMetaObject::normalizedSignature(const char *method)
Q_DECL_COLD_FUNCTION static inline bool Q_DECL_COLD_FUNCTION static inline bool
printMethodNotFoundWarning(const QMetaObject *meta, QLatin1StringView name, qsizetype paramCount, printMethodNotFoundWarning(const QMetaObject *meta, QLatin1StringView name, qsizetype paramCount,
const char *const *names) const char *const *names,
const QtPrivate::QMetaTypeInterface * const *metaTypes)
{ {
// now find the candidates we couldn't use // now find the candidates we couldn't use
QByteArray candidateMessage; QByteArray candidateMessage;
@ -1362,7 +1366,10 @@ printMethodNotFoundWarning(const QMetaObject *meta, QLatin1StringView name, qsiz
QVarLengthArray<char, 512> sig; QVarLengthArray<char, 512> sig;
for (qsizetype i = 1; i < paramCount; ++i) { for (qsizetype i = 1; i < paramCount; ++i) {
if (names[i])
sig.append(names[i], qstrlen(names[i])); sig.append(names[i], qstrlen(names[i]));
else
sig.append(metaTypes[i]->name, qstrlen(metaTypes[i]->name));
sig.append(','); sig.append(',');
} }
if (paramCount != 1) if (paramCount != 1)
@ -1472,12 +1479,13 @@ 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); return invokeMethodImpl(obj, member, type, paramCount, parameters, typeNames, nullptr);
} }
bool QMetaObject::invokeMethodImpl(QObject *obj, const char *member, Qt::ConnectionType type, bool QMetaObject::invokeMethodImpl(QObject *obj, const char *member, Qt::ConnectionType type,
qsizetype paramCount, const void * const *parameters, qsizetype paramCount, const void * const *parameters,
const char * const *typeNames) const char * const *typeNames,
const QtPrivate::QMetaTypeInterface * const *metaTypes)
{ {
if (!obj) if (!obj)
return false; return false;
@ -1503,14 +1511,15 @@ bool QMetaObject::invokeMethodImpl(QObject *obj, const char *member, Qt::Connect
// attempt to call // attempt to call
QMetaMethodPrivate::InvokeFailReason r = QMetaMethodPrivate::InvokeFailReason r =
QMetaMethodPrivate::invokeImpl(m, obj, type, paramCount, parameters, typeNames); QMetaMethodPrivate::invokeImpl(m, obj, type, paramCount, parameters,
typeNames, metaTypes);
if (int(r) <= 0) if (int(r) <= 0)
return r == QMetaMethodPrivate::InvokeFailReason::None; return r == QMetaMethodPrivate::InvokeFailReason::None;
} }
} }
// This method doesn't belong to us; print out a nice warning with candidates. // This method doesn't belong to us; print out a nice warning with candidates.
return printMethodNotFoundWarning(obj->metaObject(), name, paramCount, typeNames); return printMethodNotFoundWarning(obj->metaObject(), name, paramCount, typeNames, metaTypes);
} }
bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret) bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret)
@ -2410,25 +2419,27 @@ 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); return invokeImpl(*this, object, connectionType, paramCount, param, typeNames, nullptr);
} }
bool QMetaMethod::invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType connectionType, bool QMetaMethod::invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType connectionType,
qsizetype paramCount, const void *const *parameters, qsizetype paramCount, const void *const *parameters,
const char *const *typeNames) const char *const *typeNames,
const QtPrivate::QMetaTypeInterface *const *metaTypes)
{ {
if (!target || !self.mobj) if (!target || !self.mobj)
return false; return false;
QMetaMethodPrivate::InvokeFailReason r = QMetaMethodPrivate::InvokeFailReason r =
QMetaMethodPrivate::invokeImpl(self, target, connectionType, paramCount, parameters, typeNames); QMetaMethodPrivate::invokeImpl(self, target, connectionType, paramCount, parameters,
typeNames, metaTypes);
if (Q_LIKELY(r == QMetaMethodPrivate::InvokeFailReason::None)) if (Q_LIKELY(r == QMetaMethodPrivate::InvokeFailReason::None))
return true; return true;
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], self.mobj->className(), self.methodSignature().constData()); n, typeNames[n + 1] ? typeNames[n + 1] : metaTypes[n + 1]->name,
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",
@ -2440,10 +2451,13 @@ bool QMetaMethod::invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType
auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target, auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
Qt::ConnectionType connectionType, Qt::ConnectionType connectionType,
qsizetype paramCount, const void *const *parameters, qsizetype paramCount, const void *const *parameters,
const char *const *typeNames) -> InvokeFailReason const char *const *typeNames,
const QtPrivate::QMetaTypeInterface *const *metaTypes) -> InvokeFailReason
{ {
auto object = static_cast<QObject *>(target); auto object = static_cast<QObject *>(target);
auto priv = QMetaMethodPrivate::get(&self); auto priv = QMetaMethodPrivate::get(&self);
constexpr bool MetaTypesAreOptional = QT_VERSION < QT_VERSION_CHECK(7, 0, 0);
auto methodMetaTypes = priv->parameterMetaTypeInterfaces();
auto param = const_cast<void **>(parameters); auto param = const_cast<void **>(parameters);
Q_ASSERT(priv->mobj); Q_ASSERT(priv->mobj);
@ -2453,6 +2467,7 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
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);
Q_ASSERT(MetaTypesAreOptional || metaTypes);
if ((paramCount - 1) < qsizetype(priv->data.argc())) if ((paramCount - 1) < qsizetype(priv->data.argc()))
return InvokeFailReason::TooFewArguments; return InvokeFailReason::TooFewArguments;
@ -2460,23 +2475,45 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
// 0 is the return type, 1 is the first formal parameter // 0 is the return type, 1 is the first formal parameter
auto checkTypesAreCompatible = [=](int idx) { auto checkTypesAreCompatible = [=](int idx) {
uint typeInfo = priv->parameterTypeInfo(idx - 1); uint typeInfo = priv->parameterTypeInfo(idx - 1);
QLatin1StringView userTypeName(typeNames[idx]); QLatin1StringView userTypeName(typeNames[idx] ? typeNames[idx] : metaTypes[idx]->name);
if ((typeInfo & IsUnresolvedType) == 0) { if ((typeInfo & IsUnresolvedType) == 0) {
// this is a built-in type // this is a built-in type
if (MetaTypesAreOptional && !metaTypes)
return int(typeInfo) == QMetaType::fromName(userTypeName).id(); return int(typeInfo) == QMetaType::fromName(userTypeName).id();
return int(typeInfo) == metaTypes[idx]->typeId;
} }
// compare strings
QLatin1StringView methodTypeName = stringDataView(priv->mobj, typeInfo & TypeNameIndexMask); QLatin1StringView methodTypeName = stringDataView(priv->mobj, typeInfo & TypeNameIndexMask);
if ((MetaTypesAreOptional && !metaTypes) || !metaTypes[idx]) {
// compatibility call, compare strings
if (methodTypeName == userTypeName) if (methodTypeName == userTypeName)
return true; return true;
// maybe the user type needs normalization // maybe the user type needs normalization
QByteArray normalized = normalizeTypeInternal(userTypeName.begin(), userTypeName.end()); QByteArray normalized = normalizeTypeInternal(userTypeName.begin(), userTypeName.end());
return methodTypeName == QLatin1StringView(normalized); return methodTypeName == QLatin1StringView(normalized);
}
QMetaType userType(metaTypes[idx]);
Q_ASSERT(userType.isValid());
if (QMetaType(methodMetaTypes[idx - 1]) == userType)
return true;
// if the parameter type was NOT only forward-declared, it MUST have
// matched
if (methodMetaTypes[idx - 1])
return false;
// resolve from the name moc stored for us
QMetaType resolved = QMetaType::fromName(methodTypeName);
return resolved == userType;
}; };
// force all types to be registered, just in case
for (qsizetype i = 0; metaTypes && i < paramCount; ++i)
QMetaType(metaTypes[i]).registerType();
// check formal parameters first (overload set) // check formal parameters first (overload set)
for (qsizetype i = 1; i < paramCount; ++i) { for (qsizetype i = 1; i < paramCount; ++i) {
if (!checkTypesAreCompatible(i)) if (!checkTypesAreCompatible(i))
@ -2497,6 +2534,14 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
return InvokeFailReason::ConstructorCallWithoutResult; return InvokeFailReason::ConstructorCallWithoutResult;
} }
if (!MetaTypesAreOptional || metaTypes) {
if (metaTypes[0]->typeId != QMetaType::QObjectStar) {
qWarning("QMetaMethod::invokeMethod: cannot convert QObject* to %s on constructor call %s",
metaTypes[0]->name, self.methodSignature().constData());
return InvokeFailReason::ReturnTypeMismatch;
}
}
int idx = priv->ownConstructorMethodIndex(); int idx = priv->ownConstructorMethodIndex();
if (priv->mobj->static_metacall(QMetaObject::CreateInstance, idx, param) >= 0) if (priv->mobj->static_metacall(QMetaObject::CreateInstance, idx, param) >= 0)
return InvokeFailReason::ConstructorCallFailed; return InvokeFailReason::ConstructorCallFailed;
@ -2506,10 +2551,11 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
// regular type - check return type // regular type - check return type
if (parameters[0]) { if (parameters[0]) {
if (!checkTypesAreCompatible(0)) { if (!checkTypesAreCompatible(0)) {
const char *retType = typeNames[0] ? typeNames[0] : metaTypes[0]->name;
qWarning("QMetaMethod::invokeMethod: return type mismatch for method %s::%s:" qWarning("QMetaMethod::invokeMethod: return type mismatch for method %s::%s:"
" cannot convert from %s to %s during invocation", " cannot convert from %s to %s during invocation",
priv->mobj->className(), priv->methodSignature().constData(), priv->mobj->className(), priv->methodSignature().constData(),
priv->rawReturnTypeName(), typeNames[0]); priv->rawReturnTypeName(), retType);
return InvokeFailReason::ReturnTypeMismatch; return InvokeFailReason::ReturnTypeMismatch;
} }
} }
@ -2561,8 +2607,12 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
// fill in the meta types first // fill in the meta types first
for (int i = 1; i < paramCount; ++i) { for (int i = 1; i < paramCount; ++i) {
types[i] = priv->parameterMetaType(i - 1); types[i] = QMetaType(methodMetaTypes[i - 1]);
if (!types[i].iface() && (!MetaTypesAreOptional || metaTypes))
types[i] = QMetaType(metaTypes[i]);
if (!types[i].iface()) if (!types[i].iface())
types[i] = priv->parameterMetaType(i - 1);
if (!types[i].iface() && typeNames[i])
types[i] = QMetaType::fromName(typeNames[i]); types[i] = QMetaType::fromName(typeNames[i]);
if (!types[i].iface()) { if (!types[i].iface()) {
qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'", qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",

View File

@ -141,7 +141,7 @@ public:
{ {
auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...); auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
return invokeImpl(*this, obj, c, h.parameterCount(), h.parameters.data(), return invokeImpl(*this, obj, c, h.parameterCount(), h.parameters.data(),
h.typeNames.data()); h.typeNames.data(), h.metaTypes.data());
} }
template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...> template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
@ -167,7 +167,7 @@ public:
{ {
auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...); auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
return invokeImpl(*this, gadget, Qt::ConnectionType(-1), h.parameterCount(), return invokeImpl(*this, gadget, Qt::ConnectionType(-1), h.parameterCount(),
h.parameters.data(), h.typeNames.data()); h.parameters.data(), h.typeNames.data(), h.metaTypes.data());
} }
template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...> template <typename... Args> QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
@ -190,7 +190,8 @@ public:
private: private:
static bool invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType, qsizetype paramCount, static bool invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType, qsizetype paramCount,
const void *const *parameters, const char *const *typeNames); const void *const *parameters, const char *const *typeNames,
const QtPrivate::QMetaTypeInterface *const *metaTypes);
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

@ -160,7 +160,8 @@ public:
// shadows the public function // shadows the public function
static InvokeFailReason Q_CORE_EXPORT static InvokeFailReason Q_CORE_EXPORT
invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType, qsizetype paramCount, invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType, qsizetype paramCount,
const void *const *parameters, const char *const *typeNames); const void *const *parameters, const char *const *typeNames,
const QtPrivate::QMetaTypeInterface *const *metaTypes);
}; };
struct QMetaObjectPrivate struct QMetaObjectPrivate

View File

@ -70,7 +70,7 @@ class QMetaClassInfo;
namespace QtPrivate { namespace QtPrivate {
class QMetaTypeInterface; class QMetaTypeInterface;
template<typename T> constexpr auto typenameHelper(); template<typename T> constexpr const QMetaTypeInterface *qMetaTypeInterfaceForType();
} }
struct QMethodRawArguments struct QMethodRawArguments
@ -130,12 +130,14 @@ public:
struct QMetaMethodArgument struct QMetaMethodArgument
{ {
const QtPrivate::QMetaTypeInterface *metaType;
const char *name; const char *name;
const void *data; const void *data;
}; };
struct QMetaMethodReturnArgument struct QMetaMethodReturnArgument
{ {
const QtPrivate::QMetaTypeInterface *metaType;
const char *name; const char *name;
void *data; void *data;
}; };
@ -154,62 +156,70 @@ template <typename T, typename... Args> using IfNotOldStyleArgs = T;
template <typename T> inline QMetaMethodArgument argument(const char *name, const T &t) template <typename T> inline QMetaMethodArgument argument(const char *name, const T &t)
{ {
return { name, std::addressof(t) }; if constexpr ((std::is_lvalue_reference_v<T> && std::is_const_v<std::remove_reference_t<T>>) ||
!std::is_reference_v<T>) {
return { qMetaTypeInterfaceForType<T>(), name, std::addressof(t) };
} else {
return { nullptr, name, std::addressof(t) };
}
} }
template <typename T> inline QMetaMethodReturnArgument returnArgument(const char *name, T &t) template <typename T> inline QMetaMethodReturnArgument returnArgument(const char *name, T &t)
{ {
return { name, std::addressof(t) }; return { qMetaTypeInterfaceForType<T>(), name, std::addressof(t) };
} }
template <typename T> inline const char *typenameHelper(const T &) template <typename T> inline const char *typenameHelper(const T &)
{ {
// duplicated from the QMetaTypeInterface, FIXME return nullptr;
static constexpr auto name = QtPrivate::typenameHelper<T>();
return name.data();
} }
template <typename T> inline const void *dataHelper(const T &t) template <typename T> inline const void *dataHelper(const T &t)
{ {
return std::addressof(t); return std::addressof(t);
} }
template <typename T> inline const QMetaTypeInterface *metaTypeHelper(const T &)
{
return qMetaTypeInterfaceForType<T>();
}
inline const char *typenameHelper(QMetaMethodArgument a) inline const char *typenameHelper(QMetaMethodArgument a)
{ return a.name; } { return a.name; }
inline const void *dataHelper(QMetaMethodArgument a) inline const void *dataHelper(QMetaMethodArgument a)
{ return a.data; } { return a.data; }
inline const QMetaTypeInterface *metaTypeHelper(QMetaMethodArgument a)
{ return a.metaType; }
inline const char *typenameHelper(const char *) = delete; inline const char *typenameHelper(const char *) = delete;
template <typename T> inline const void *dataHelper(const char *) = delete; template <typename T> inline const void *dataHelper(const char *) = delete;
inline const QMetaTypeInterface *metaTypeHelper(const char *) = delete;
inline const char *typenameHelper(const char16_t *) = delete; inline const char *typenameHelper(const char16_t *) = delete;
template <typename T> inline const void *dataHelper(const char16_t *) = delete; template <typename T> inline const void *dataHelper(const char16_t *) = delete;
inline const QMetaTypeInterface *metaTypeHelper(const char16_t *) = delete;
} // namespace QtPrivate::Invoke } // namespace QtPrivate::Invoke
template <typename... Args> inline auto invokeMethodHelper(QMetaMethodReturnArgument r, Args &&... arguments) template <typename... Args> inline auto invokeMethodHelper(QMetaMethodReturnArgument r, const Args &... arguments)
{ {
std::array params = { const_cast<const void *>(r.data), Invoke::dataHelper(arguments)... }; std::array params = { const_cast<const void *>(r.data), Invoke::dataHelper(arguments)... };
std::array names = { r.name, Invoke::typenameHelper(arguments)... }; std::array names = { r.name, Invoke::typenameHelper(arguments)... };
std::array types = { r.metaType, Invoke::metaTypeHelper(arguments)... };
static_assert(params.size() == types.size());
static_assert(params.size() == names.size()); static_assert(params.size() == names.size());
struct R { struct R {
decltype(params) parameters; decltype(params) parameters;
decltype(names) typeNames; decltype(names) typeNames;
decltype(types) metaTypes;
constexpr qsizetype parameterCount() const { return qsizetype(parameters.size()); } constexpr qsizetype parameterCount() const { return qsizetype(parameters.size()); }
}; };
return R { params, names }; return R { params, names, types };
} }
} // namespace QtPrivate } // namespace QtPrivate
template <typename T> inline QMetaMethodReturnArgument qReturnArg(T &&) = delete; template <typename T> inline QMetaMethodReturnArgument qReturnArg(T &&) = delete;
template <typename T> inline QMetaMethodReturnArgument qReturnArg(T &data) template <typename T> inline QMetaMethodReturnArgument qReturnArg(T &data)
{ {
if constexpr (std::is_same_v<T, const char *>) { return QtPrivate::Invoke::returnArgument(nullptr, data);
// 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
@ -351,7 +361,7 @@ struct Q_CORE_EXPORT QMetaObject
{ {
auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...); auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
return invokeMethodImpl(obj, member, c, h.parameterCount(), h.parameters.data(), return invokeMethodImpl(obj, member, c, h.parameterCount(), h.parameters.data(),
h.typeNames.data()); h.typeNames.data(), h.metaTypes.data());
} }
template <typename... Args> static template <typename... Args> static
@ -476,7 +486,8 @@ struct Q_CORE_EXPORT QMetaObject
newInstance(Args &&... arguments) const newInstance(Args &&... arguments) const
{ {
auto h = QtPrivate::invokeMethodHelper(QMetaMethodReturnArgument{}, std::forward<Args>(arguments)...); auto h = QtPrivate::invokeMethodHelper(QMetaMethodReturnArgument{}, std::forward<Args>(arguments)...);
return newInstanceImpl(this, h.parameterCount(), h.parameters.data(), h.typeNames.data()); return newInstanceImpl(this, h.parameterCount(), h.parameters.data(),
h.typeNames.data(), h.metaTypes.data());
} }
enum Call { enum Call {
@ -538,10 +549,12 @@ struct Q_CORE_EXPORT QMetaObject
private: private:
static bool invokeMethodImpl(QObject *object, const char *member, Qt::ConnectionType type, static bool invokeMethodImpl(QObject *object, const char *member, Qt::ConnectionType type,
qsizetype parameterCount, const void *const *parameters, const char *const *names); qsizetype parameterCount, const void *const *parameters, const char *const *names,
const QtPrivate::QMetaTypeInterface * const *metaTypes);
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, static QObject *newInstanceImpl(const QMetaObject *mobj, qsizetype parameterCount,
const void **parameters, const char **typeNames); const void **parameters, const char **typeNames,
const QtPrivate::QMetaTypeInterface **metaTypes);
friend class QTimer; friend class QTimer;
}; };

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "forwarddeclared.h" #include "forwarddeclared.h"
#include "qeasingcurve.h"
struct MyForwardDeclaredType { }; struct MyForwardDeclaredType { };
static MyForwardDeclaredType t; static MyForwardDeclaredType t;
@ -16,18 +15,3 @@ MyForwardDeclaredType *getForwardDeclaredPointer() noexcept
{ {
return &t; return &t;
} }
QT_BEGIN_NAMESPACE
const QEasingCurve &getEasingCurve() noexcept
{
return *getEasingCurvePointer();
}
QEasingCurve *getEasingCurvePointer() noexcept
{
static QEasingCurve curve;
return &curve;
}
QT_END_NAMESPACE

View File

@ -4,18 +4,9 @@
#ifndef FORWARDDECLARED_H #ifndef FORWARDDECLARED_H
#define FORWARDDECLARED_H #define FORWARDDECLARED_H
#include <qglobal.h>
struct MyForwardDeclaredType; // and ONLY forward-declared struct MyForwardDeclaredType; // and ONLY forward-declared
const MyForwardDeclaredType &getForwardDeclaredType() noexcept; const MyForwardDeclaredType &getForwardDeclaredType() noexcept;
MyForwardDeclaredType *getForwardDeclaredPointer() noexcept; MyForwardDeclaredType *getForwardDeclaredPointer() noexcept;
QT_BEGIN_NAMESPACE
class QEasingCurve;
const QEasingCurve &getEasingCurve() noexcept;
QEasingCurve *getEasingCurvePointer() noexcept;
QT_END_NAMESPACE
#endif // FORWARDDECLARED_H #endif // FORWARDDECLARED_H

View File

@ -31,14 +31,6 @@ Q_DECLARE_METATYPE(const QMetaObject *)
# define Q_NO_ARG # define Q_NO_ARG
#endif #endif
#ifdef QEASINGCURVE_H
# error "Please make sure qeasingcurve.h is not #include'd here! " \
"We need QEasingCurve to be only forward-declared."
#endif
QT_BEGIN_NAMESPACE
class QEasingCurve;
QT_END_NAMESPACE
struct MyStruct struct MyStruct
{ {
int i; int i;
@ -519,7 +511,6 @@ public slots:
qint64 sl14(); qint64 sl14();
qlonglong *sl15(qlonglong *); qlonglong *sl15(qlonglong *);
MyForwardDeclaredType *sl16(MyForwardDeclaredType *); MyForwardDeclaredType *sl16(MyForwardDeclaredType *);
void sl17(const QEasingCurve &curve);
void overloadedSlot(); void overloadedSlot();
void overloadedSlot(int, int); void overloadedSlot(int, int);
@ -636,8 +627,6 @@ MyForwardDeclaredType *QtTestObject::sl16(MyForwardDeclaredType *ptr)
slotResult += "null"; slotResult += "null";
return getForwardDeclaredPointer(); return getForwardDeclaredPointer();
} }
void QtTestObject::sl17(const QEasingCurve &)
{ slotResult = "sl17"; }
void QtTestObject::overloadedSlot() void QtTestObject::overloadedSlot()
{ slotResult = "overloadedSlot"; } { slotResult = "overloadedSlot"; }
@ -819,11 +808,8 @@ void tst_QMetaObject::invokeMetaMember()
QCOMPARE(forwardPtr, getForwardDeclaredPointer()); QCOMPARE(forwardPtr, getForwardDeclaredPointer());
QCOMPARE(obj.slotResult, QString("sl16:null")); QCOMPARE(obj.slotResult, QString("sl16:null"));
// forward-declared builtin #ifndef QT_NO_DATA_RELOCATION // this doesn't work with the new API on Windows
obj.slotResult.clear(); #endif
QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", Q_ARG(QEasingCurve, getEasingCurve())));
QCOMPARE(obj.slotResult, "sl17");
// test overloads // test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot" Q_NO_ARG)); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot" Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("overloadedSlot")); QCOMPARE(obj.slotResult, QString("overloadedSlot"));
@ -990,11 +976,6 @@ void tst_QMetaObject::invokeMetaMemberNoMacros()
QCOMPARE(forwardPtr, getForwardDeclaredPointer()); QCOMPARE(forwardPtr, getForwardDeclaredPointer());
QCOMPARE(obj.slotResult, QString("sl16:null")); QCOMPARE(obj.slotResult, QString("sl16:null"));
// forward-declared builtin
obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", getEasingCurve()));
QCOMPARE(obj.slotResult, "sl17");
// test overloads // test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot")); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot"));
QCOMPARE(obj.slotResult, QString("overloadedSlot")); QCOMPARE(obj.slotResult, QString("overloadedSlot"));
@ -1131,12 +1112,14 @@ void tst_QMetaObject::invokeQueuedMetaMember()
qApp->processEvents(QEventLoop::AllEvents); qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl15")); QCOMPARE(obj.slotResult, QString("sl15"));
// forward-declared builtin // since Qt 6.5, this works even for pointers to forward-declared types
obj.slotResult.clear(); obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", Qt::QueuedConnection, Q_ARG(QEasingCurve, getEasingCurve()))); QVERIFY(QMetaObject::invokeMethod(&obj, "sl16", Qt::QueuedConnection, Q_ARG(MyForwardDeclaredType*, getForwardDeclaredPointer())));
qApp->processEvents(QEventLoop::AllEvents); qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, "sl17"); QCOMPARE(obj.slotResult, QString("sl16:notnull"));
#ifndef QT_NO_DATA_RELOCATION // this doesn't work with the new API on Windows
#endif
// test overloads // test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection Q_NO_ARG)); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection Q_NO_ARG));
qApp->processEvents(QEventLoop::AllEvents); qApp->processEvents(QEventLoop::AllEvents);
@ -1184,6 +1167,7 @@ void tst_QMetaObject::invokeQueuedMetaMember()
QVERIFY(!QMetaObject::invokeMethod(&obj, "testReference", Qt::QueuedConnection, Q_ARG(QString&, refStr))); QVERIFY(!QMetaObject::invokeMethod(&obj, "testReference", Qt::QueuedConnection, Q_ARG(QString&, refStr)));
QCOMPARE(refStr, "whatever"); QCOMPARE(refStr, "whatever");
#ifdef USE_COMPAT_Q_ARG // this doesn't compile with the new API
obj.slotResult.clear(); obj.slotResult.clear();
{ {
const MyForwardDeclaredType &t = getForwardDeclaredType(); const MyForwardDeclaredType &t = getForwardDeclaredType();
@ -1201,12 +1185,7 @@ void tst_QMetaObject::invokeQueuedMetaMember()
Q_ARG(QString, a1), Q_ARG(MyForwardDeclaredType, t))); Q_ARG(QString, a1), Q_ARG(MyForwardDeclaredType, t)));
QVERIFY(obj.slotResult.isEmpty()); QVERIFY(obj.slotResult.isEmpty());
} }
#endif
obj.slotResult.clear();
QTest::ignoreMessage(QtWarningMsg, "QMetaMethod::invoke: Unable to handle unregistered datatype 'MyForwardDeclaredType*'");
QVERIFY(!QMetaObject::invokeMethod(&obj, "sl16", Qt::QueuedConnection, Q_ARG(MyForwardDeclaredType*, getForwardDeclaredPointer())));
qApp->processEvents(QEventLoop::AllEvents);
QVERIFY(obj.slotResult.isEmpty());
} }
// this is a copy-paste-adapt of the above // this is a copy-paste-adapt of the above
@ -1241,11 +1220,10 @@ void tst_QMetaObject::invokeQueuedMetaMemberNoMacro()
qApp->processEvents(QEventLoop::AllEvents); qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, QString("sl15")); QCOMPARE(obj.slotResult, QString("sl15"));
// forward-declared builtin
obj.slotResult.clear(); obj.slotResult.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", Qt::QueuedConnection, getEasingCurve())); QVERIFY(QMetaObject::invokeMethod(&obj, "sl16", Qt::QueuedConnection, getForwardDeclaredPointer()));
qApp->processEvents(QEventLoop::AllEvents); qApp->processEvents(QEventLoop::AllEvents);
QCOMPARE(obj.slotResult, "sl17"); QCOMPARE(obj.slotResult, QString("sl16:notnull"));
// test overloads // test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection)); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::QueuedConnection));
@ -1297,6 +1275,7 @@ void tst_QMetaObject::invokeQueuedMetaMemberNoMacro()
// QVERIFY(!QMetaObject::invokeMethod(&obj, "testReference", Qt::QueuedConnection, Q_ARG(QString&, refStr))); // QVERIFY(!QMetaObject::invokeMethod(&obj, "testReference", Qt::QueuedConnection, Q_ARG(QString&, refStr)));
// QCOMPARE(refStr, "whatever"); // QCOMPARE(refStr, "whatever");
#if 0 // this won't even compile any more
obj.slotResult.clear(); obj.slotResult.clear();
{ {
const MyForwardDeclaredType &t = getForwardDeclaredType(); const MyForwardDeclaredType &t = getForwardDeclaredType();
@ -1314,12 +1293,7 @@ void tst_QMetaObject::invokeQueuedMetaMemberNoMacro()
a1, t)); a1, t));
QVERIFY(obj.slotResult.isEmpty()); QVERIFY(obj.slotResult.isEmpty());
} }
#endif
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()
@ -1505,11 +1479,8 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
QCOMPARE(forwardPtr, getForwardDeclaredPointer()); QCOMPARE(forwardPtr, getForwardDeclaredPointer());
QCOMPARE(obj.slotResult, QString("sl16:null")); QCOMPARE(obj.slotResult, QString("sl16:null"));
// forward-declared builtin #ifndef QT_NO_DATA_RELOCATION // this doesn't work with the new API on Windows
obj.slotResult.clear(); #endif
QVERIFY(QMetaObject::invokeMethod(&obj, "sl17", Qt::BlockingQueuedConnection, Q_ARG(QEasingCurve, getEasingCurve())));
QCOMPARE(obj.slotResult, "sl17");
// test overloads // test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection Q_NO_ARG)); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection Q_NO_ARG));
QCOMPARE(obj.slotResult, QString("overloadedSlot")); QCOMPARE(obj.slotResult, QString("overloadedSlot"));
@ -1681,11 +1652,6 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMemberNoMacros()
QCOMPARE(forwardPtr, getForwardDeclaredPointer()); QCOMPARE(forwardPtr, getForwardDeclaredPointer());
QCOMPARE(obj.slotResult, QString("sl16:null")); 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 // test overloads
QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection)); QVERIFY(QMetaObject::invokeMethod(&obj, "overloadedSlot", Qt::BlockingQueuedConnection));
QCOMPARE(obj.slotResult, QString("overloadedSlot")); QCOMPARE(obj.slotResult, QString("overloadedSlot"));
@ -1968,6 +1934,12 @@ void tst_QMetaObject::invokeTypedefTypes()
QCOMPARE(spy.count(), 1); QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).count(), 1); QCOMPARE(spy.at(0).count(), 1);
QCOMPARE(spy.at(0).at(0), QVariant(arg)); QCOMPARE(spy.at(0).at(0), QVariant(arg));
spy.clear();
QVERIFY(QMetaObject::invokeMethod(&obj, "sig_custom", arg));
QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).count(), 1);
QCOMPARE(spy.at(0).at(0), QVariant(arg));
} }
void tst_QMetaObject::invokeException() void tst_QMetaObject::invokeException()
@ -2809,7 +2781,4 @@ void tst_QMetaObject::notifySignalsInParentClass()
} }
QTEST_MAIN(tst_QMetaObject) QTEST_MAIN(tst_QMetaObject)
static_assert(!QtPrivate::is_complete<QEasingCurve, void>::value,
"QEasingCurve must only be forward-declared at this point");
#include "tst_qmetaobject.moc" #include "tst_qmetaobject.moc"