-
-
Save andref/2838534 to your computer and use it in GitHub Desktop.
#include <QtCore> | |
#include <QtDebug> | |
QVariant call(QObject* object, QMetaMethod metaMethod, QVariantList args) | |
{ | |
// Convert the arguments | |
QVariantList converted; | |
// We need enough arguments to perform the conversion. | |
QList<QByteArray> methodTypes = metaMethod.parameterTypes(); | |
if (methodTypes.size() < args.size()) { | |
qWarning() << "Insufficient arguments to call" << metaMethod.signature(); | |
return QVariant(); | |
} | |
for (int i = 0; i < methodTypes.size(); i++) { | |
const QVariant& arg = args.at(i); | |
QByteArray methodTypeName = methodTypes.at(i); | |
QByteArray argTypeName = arg.typeName(); | |
QVariant::Type methodType = QVariant::nameToType(methodTypeName); | |
QVariant::Type argType = arg.type(); | |
QVariant copy = QVariant(arg); | |
// If the types are not the same, attempt a conversion. If it | |
// fails, we cannot proceed. | |
if (copy.type() != methodType) { | |
if (copy.canConvert(methodType)) { | |
if (!copy.convert(methodType)) { | |
qWarning() << "Cannot convert" << argTypeName | |
<< "to" << methodTypeName; | |
return QVariant(); | |
} | |
} | |
} | |
converted << copy; | |
} | |
QList<QGenericArgument> arguments; | |
for (int i = 0; i < converted.size(); i++) { | |
// Notice that we have to take a reference to the argument, else | |
// we'd be pointing to a copy that will be destroyed when this | |
// loop exits. | |
QVariant& argument = converted[i]; | |
// A const_cast is needed because calling data() would detach | |
// the QVariant. | |
QGenericArgument genericArgument( | |
QMetaType::typeName(argument.userType()), | |
const_cast<void*>(argument.constData()) | |
); | |
arguments << genericArgument; | |
} | |
QVariant returnValue(QMetaType::type(metaMethod.typeName()), | |
static_cast<void*>(NULL)); | |
QGenericReturnArgument returnArgument( | |
metaMethod.typeName(), | |
const_cast<void*>(returnValue.constData()) | |
); | |
// Perform the call | |
bool ok = metaMethod.invoke( | |
object, | |
Qt::DirectConnection, | |
returnArgument, | |
arguments.value(0), | |
arguments.value(1), | |
arguments.value(2), | |
arguments.value(3), | |
arguments.value(4), | |
arguments.value(5), | |
arguments.value(6), | |
arguments.value(7), | |
arguments.value(8), | |
arguments.value(9) | |
); | |
if (!ok) { | |
qWarning() << "Calling" << metaMethod.signature() << "failed."; | |
return QVariant(); | |
} else { | |
return returnValue; | |
} | |
} | |
class Target : public QObject | |
{ | |
Q_OBJECT; | |
public: | |
explicit Target(QObject* parent = 0) : QObject(parent) { | |
} | |
Q_INVOKABLE virtual void foo(qulonglong val) { | |
qDebug() << "Value:" << val; | |
} | |
}; | |
int main(int argc, char** argv) { | |
QCoreApplication a(argc, argv); | |
Target* t = new Target(); | |
int index = t->metaObject()->indexOfMethod("foo(qulonglong)"); | |
QMetaMethod metaMethod = t->metaObject()->method(index); | |
QVariantList list; | |
list << QVariant("10"); | |
call(t, metaMethod, list); | |
a.exec(); | |
} | |
#include <main.moc> |
TEMPLATE = app | |
TARGET = | |
DEPENDPATH += . | |
INCLUDEPATH += . | |
SOURCES += main.cpp |
I think preventing detach
here is meaningless for both arguments and return value.
I mean, at least for Qt 6.6.0 currently I'm looking around as of now.
From what I understand, detach
becomes meaningful when different QVariant
s share a same resource.
But here,
QGenericArgument
acceptsconst void*
for data.
So just directly passingconst void*
fromconstData()
would suffice. (constData()
won't detach any data)
Casting tovoid*
usingconst_cast<void*>
to giveconst void*
type parameter is meaningless here.QGenericReturnArgument
acceptsvoid*
for data.
TheQVarant
that we are newly creating definitely shares nothing, no worries aboutdetach
.
So just directly passingvoid*
fromdata()
would suffice.
So just call constData()
for argument and data()
for return value when creating generic arguments.
// argument
QGenericArgument genericArgument(
argument.typeName(),
argument.constData()
);
// return value
QGenericReturnArgument returnArgument(
metaMethod.typeName(),
returnValue.data()
);
Note: the original implementation would return QVarant
that would give isNull() == false
always, even when internal data is written with some meaningful data.
Also it seems that the code around line 13 conflicts with the comment (when checking whether we have enough arguments):
// We need enough arguments to perform the conversion.
QList<QByteArray> methodTypes = metaMethod.parameterTypes();
if (methodTypes.size() < args.size()) { // Note: this checks whether we have more args than we need, not whether it's insufficient.
qWarning() << "Insufficient arguments to call" << metaMethod.signature();
return QVariant();
}
https://gist.github.com/andref/2838534#file-main-cpp-L13
And I think a possible attempt for that would be like: args.size() < metaMethod.parameterCount()
This is exactly what I need, thanks!