[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