[Qt-interest] QMetaMethod invocation without compile-time knowledge of the return type
André Fillipe de Oliveira e Silva
andref at syst.com.br
Thu Sep 24 00:15:40 CEST 2009
Hello,
I'm trying to build a QObject introspector in Qt 4.5 that should work
somewhat like Java's commons beanutils or Cocoa Key-Value Coding. For most
of my job, QObject::property and QObject::setProperty do what I need, but
for some special cases I need to call methods that are named by convention
and tagged with Q_INVOKABLE.
For example, if MyObject has a to-many property called author, I might in
theory write something like this:
Introspector intro = getAnIntrospectorForMyObjectGraph();
Author *author =
intro.readProperty("rootObject.anotherObject.myObject.author[2]")
.value<Author*>();
and it will navigate from rootObject to myObject and return it's second
author. All the path until "myObject" can be resolved using
QObject::property(), but to resolve "myObject.author[2]" I will need to
call, say, Author* MyObject::authorAt(int) using a QMetaMethod, to obtain
the i-th author in the list of authors. The introspector, however, knows
nothing about the Author class. It deals only with QObjects (Author, of
course, extends QObject and Author* was registered with the Qt Metatype
System).
My problem is: QMetaMethod::invoke has a QGenericReturnArgument parameter.
QGenericReturnArgument is a template class, and one of it's parameters is
the type of the return value. Therefore, I must know the type of the return
value at compile time to properly construct a QGenericReturnArgument.
Such a generic invocation can be written as a template function (colored
version at http://www.pastie.org/628079):
template <typename T>
T readPropertyAt(QObject *object, QString name, int index, T defaultValue =
T())
{
if (object != NULL) {
QString getter(QString("%1At(int)").arg(name));
// getMethod does the normalizedSignature + methodIndex stuff
QMetaMethod method = getMethod(object, getter.toAscii());
T returnValue;
bool success = method.invoke(
object,
Qt::DirectConnection,
// Q_RETURN_ARG is a macro and doesn't work here.
QReturnArgument<T>(method.typeName(), returnValue),
Q_ARG(int, index)
);
if (success) return returnValue;
}
return defaultValue;
}
and can be called like so:
Author *secondAuthor = readPropertyAt<Author *>(myObject, "author", 2,
NULL);
But that only works for a direct access. One might want to chain indexed
accesses (e.g. "myObject.author[2].book[3]") and the return types of the
intermediate acessess must somehow be specified. An option:
Book *thirdBook = introspector
.simple("myObject")
.indexed<Author*>("author", 2)
.indexed<Book*>("book", 3).value();
It looks ugly, however, and also forces tthe caller to know the type of the
properties in the middle of the path (otherwise, QMetaObject::invoke doesn't
work).
Why is it that QMetaMethod::invoke cannot accept a (non-const reference to
a) QVariant and just proceed? Why is it necessary to provide the type name
at compile time? Do you have any ideas how I could work around this
limitation? It's not impossible: after all that's exactly what
QObject::property() does -- it invokes a method by reflection and gives back
the return value as a QVariant.
I'll go back to digging QObject::property() code and whatever code the moc
generates for Q_INVOKABLE metacalls. If anyone can come up with an idea,
please, reply.
Thank you for your time, people.
--
André
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.qt-project.org/pipermail/qt-interest-old/attachments/20090923/7e91c12a/attachment.html
More information about the Qt-interest-old
mailing list