Created
May 30, 2012 19:47
-
-
Save andref/2838534 to your computer and use it in GitHub Desktop.
Calling QMetaMethods with QVariant arguments and best-effort type conversion
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TEMPLATE = app | |
TARGET = | |
DEPENDPATH += . | |
INCLUDEPATH += . | |
SOURCES += main.cpp |
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()
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 differentQVariant
s share a same resource.But here,
QGenericArgument
acceptsconst void*
for data.So just directly passing
const void*
fromconstData()
would suffice. (constData()
won't detach any data)Casting to
void*
usingconst_cast<void*>
to giveconst void*
type parameter is meaningless here.QGenericReturnArgument
acceptsvoid*
for data.The
QVarant
that we are newly creating definitely shares nothing, no worries aboutdetach
.So just directly passing
void*
fromdata()
would suffice.So just call
constData()
for argument anddata()
for return value when creating generic arguments.Note: the original implementation would return
QVarant
that would giveisNull() == false
always, even when internal data is written with some meaningful data.