summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qmetaobject.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2022-07-15 19:01:00 -0700
committerThiago Macieira <thiago.macieira@intel.com>2022-09-01 21:29:04 -0300
commit0380dd50519e4e1eb5ba5e8f54e858e1d84a1d2f (patch)
tree3b167ececc698382fc3e673fbf5bcd2358cc09b6 /src/corelib/kernel/qmetaobject.cpp
parentba8c4b4ac6df8c4318e3dd8c886aa0135628d33c (diff)
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>
Diffstat (limited to 'src/corelib/kernel/qmetaobject.cpp')
-rw-r--r--src/corelib/kernel/qmetaobject.cpp98
1 files changed, 74 insertions, 24 deletions
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index 7142a17050b..2a4fc7ebea3 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -241,11 +241,12 @@ QObject *QMetaObject::newInstance(QGenericArgument val0,
break;
}
- return newInstanceImpl(this, paramCount, parameters, typeNames);
+ return newInstanceImpl(this, paramCount, parameters, typeNames, nullptr);
}
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)) {
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)>();
parameters[0] = &returnValue;
typeNames[0] = returnValueMetaType.name();
+ if (metaTypes)
+ metaTypes[0] = returnValueMetaType.iface();
QT_WARNING_POP
@@ -275,7 +278,7 @@ QT_WARNING_POP
// attempt to call
QMetaMethodPrivate::InvokeFailReason r =
QMetaMethodPrivate::invokeImpl(m, nullptr, Qt::DirectConnection, paramCount,
- parameters, typeNames);
+ parameters, typeNames, metaTypes);
if (r == QMetaMethodPrivate::InvokeFailReason::None)
return returnValue;
if (int(r) < 0)
@@ -1346,7 +1349,8 @@ QByteArray QMetaObject::normalizedSignature(const char *method)
Q_DECL_COLD_FUNCTION static inline bool
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
QByteArray candidateMessage;
@@ -1362,7 +1366,10 @@ printMethodNotFoundWarning(const QMetaObject *meta, QLatin1StringView name, qsiz
QVarLengthArray<char, 512> sig;
for (qsizetype i = 1; i < paramCount; ++i) {
- sig.append(names[i], qstrlen(names[i]));
+ if (names[i])
+ sig.append(names[i], qstrlen(names[i]));
+ else
+ sig.append(metaTypes[i]->name, qstrlen(metaTypes[i]->name));
sig.append(',');
}
if (paramCount != 1)
@@ -1472,12 +1479,13 @@ bool QMetaObject::invokeMethod(QObject *obj,
if (qstrlen(typeNames[paramCount]) <= 0)
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,
qsizetype paramCount, const void * const *parameters,
- const char * const *typeNames)
+ const char * const *typeNames,
+ const QtPrivate::QMetaTypeInterface * const *metaTypes)
{
if (!obj)
return false;
@@ -1503,14 +1511,15 @@ bool QMetaObject::invokeMethodImpl(QObject *obj, const char *member, Qt::Connect
// attempt to call
QMetaMethodPrivate::InvokeFailReason r =
- QMetaMethodPrivate::invokeImpl(m, obj, type, paramCount, parameters, typeNames);
+ QMetaMethodPrivate::invokeImpl(m, obj, type, paramCount, parameters,
+ typeNames, metaTypes);
if (int(r) <= 0)
return r == QMetaMethodPrivate::InvokeFailReason::None;
}
}
// 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)
@@ -2410,25 +2419,27 @@ bool QMetaMethod::invoke(QObject *object,
if (qstrlen(typeNames[paramCount]) <= 0)
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,
qsizetype paramCount, const void *const *parameters,
- const char *const *typeNames)
+ const char *const *typeNames,
+ const QtPrivate::QMetaTypeInterface *const *metaTypes)
{
if (!target || !self.mobj)
return false;
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))
return true;
if (int(r) >= int(QMetaMethodPrivate::InvokeFailReason::FormalParameterMismatch)) {
int n = int(r) - int(QMetaMethodPrivate::InvokeFailReason::FormalParameterMismatch);
qWarning("QMetaMethod::invoke: cannot convert formal parameter %d from %s in call to %s::%s",
- n, typeNames[n + 1], 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) {
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,
Qt::ConnectionType connectionType,
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 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);
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(parameters);
Q_ASSERT(typeNames);
+ Q_ASSERT(MetaTypesAreOptional || metaTypes);
if ((paramCount - 1) < qsizetype(priv->data.argc()))
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
auto checkTypesAreCompatible = [=](int idx) {
uint typeInfo = priv->parameterTypeInfo(idx - 1);
- QLatin1StringView userTypeName(typeNames[idx]);
+ QLatin1StringView userTypeName(typeNames[idx] ? typeNames[idx] : metaTypes[idx]->name);
if ((typeInfo & IsUnresolvedType) == 0) {
// this is a built-in type
- return int(typeInfo) == QMetaType::fromName(userTypeName).id();
+ if (MetaTypesAreOptional && !metaTypes)
+ return int(typeInfo) == QMetaType::fromName(userTypeName).id();
+ return int(typeInfo) == metaTypes[idx]->typeId;
}
- // compare strings
QLatin1StringView methodTypeName = stringDataView(priv->mobj, typeInfo & TypeNameIndexMask);
- if (methodTypeName == userTypeName)
+ if ((MetaTypesAreOptional && !metaTypes) || !metaTypes[idx]) {
+ // compatibility call, compare strings
+ if (methodTypeName == userTypeName)
+ return true;
+
+ // maybe the user type needs normalization
+ QByteArray normalized = normalizeTypeInternal(userTypeName.begin(), userTypeName.end());
+ return methodTypeName == QLatin1StringView(normalized);
+ }
+
+ QMetaType userType(metaTypes[idx]);
+ Q_ASSERT(userType.isValid());
+ if (QMetaType(methodMetaTypes[idx - 1]) == userType)
return true;
- // maybe the user type needs normalization
- QByteArray normalized = normalizeTypeInternal(userTypeName.begin(), userTypeName.end());
- return methodTypeName == QLatin1StringView(normalized);
+ // 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)
for (qsizetype i = 1; i < paramCount; ++i) {
if (!checkTypesAreCompatible(i))
@@ -2497,6 +2534,14 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
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();
if (priv->mobj->static_metacall(QMetaObject::CreateInstance, idx, param) >= 0)
return InvokeFailReason::ConstructorCallFailed;
@@ -2506,10 +2551,11 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
// regular type - check return type
if (parameters[0]) {
if (!checkTypesAreCompatible(0)) {
+ const char *retType = typeNames[0] ? typeNames[0] : metaTypes[0]->name;
qWarning("QMetaMethod::invokeMethod: return type mismatch for method %s::%s:"
" cannot convert from %s to %s during invocation",
priv->mobj->className(), priv->methodSignature().constData(),
- priv->rawReturnTypeName(), typeNames[0]);
+ priv->rawReturnTypeName(), retType);
return InvokeFailReason::ReturnTypeMismatch;
}
}
@@ -2561,8 +2607,12 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
// fill in the meta types first
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())
+ types[i] = priv->parameterMetaType(i - 1);
+ if (!types[i].iface() && typeNames[i])
types[i] = QMetaType::fromName(typeNames[i]);
if (!types[i].iface()) {
qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",