Archive for August, 2006

invokeMethodWithVariants

QMetaObject::invokeMethod uses QArgument to store arguments and return values. But wouldn’t it be cool if we could use QVariant instead of QArgument? QArgument is just a thin wrapper around a pointer, and using it in a signal is certainly dangerous. On the other hand, putting argument values inside QVariants means we would have real objects that we can marshall around, which would give us great flexibility in performing dynamic function calls.

We already talked about how to dynamically obtain return types. The getReturnType function, as well as what we learned from it, should allow us to make a function for invoking methods that uses QVariant instead of QArgument.

Okay, so here’s getReturnType:

static QByteArray getReturnType(const QMetaObject *obj,
        const QByteArray &method, const QList<QByteArray> argTypes)
{
        for(int n = 0; n < obj->methodCount(); ++n)
        {
                QMetaMethod m = obj->method(n);
                QByteArray sig = m.signature();
                int offset = sig.indexOf('(');
                if(offset == -1)
                        continue;
                QByteArray name = sig.mid(0, offset);
                if(name != method)
                        continue;
                if(m.parameterTypes() != argTypes)
                        continue;

                return m.typeName();
        }
        return QByteArray();
}

Now for the new stuff:

bool invokeMethodWithVariants(QObject *obj,
        const QByteArray &method, const QVariantList &args,
        QVariant *ret, Qt::ConnectionType type = Qt::AutoConnection)
{
        // QMetaObject::invokeMethod() has a 10 argument maximum
        if(args.count() > 10)
                return false;

        QList<QByteArray> argTypes;
        for(int n = 0; n < args.count(); ++n)
                argTypes += args[n].typeName();

        // get return type
        int metatype = 0;
        QByteArray retTypeName = getReturnType(obj->metaObject(),
                method, argTypes);
        if(!retTypeName.isEmpty())
        {
                metatype = QMetaType::type(retTypeName.data());
                if(metatype == 0) // lookup failed
                        return false;
        }

        QGenericArgument arg[10];
        for(int n = 0; n < args.count(); ++n)
                arg[n] = QGenericArgument(args[n].typeName(),
                        args[n].constData());

        QGenericReturnArgument retarg;
        QVariant retval;
        if(metatype != 0)
        {
                retval = QVariant(metatype, (const void *)0);
                retarg = QGenericReturnArgument(retval.typeName(),
                        retval.data());
        }

        if(!QMetaObject::invokeMethod(obj, method.data(), type, retarg,
                arg[0], arg[1], arg[2], arg[3], arg[4],
                arg[5], arg[6], arg[7], arg[8], arg[9]))
        {
                return false;
        }

        if(retval.isValid() && ret)
                *ret = retval;
        return true;
}

Now let’s compare QMetaObject::invokeMethod to invokeMethodWithVariants. Well, here’s the example from invokeMethod’s documentation:

QString retVal;
QMetaObject::invokeMethod(obj, "compute", Qt::DirectConnection,
        Q_RETURN_ARG(QString, retVal),
        Q_ARG(QString, "sqrt"),
        Q_ARG(int, 42),
        Q_ARG(double, 9.7));

Let’s call the same function, using invokeMethodWithVariants:

QVariantList args;
QVariant retVal;
args << "sqrt" << 42 << 9.7;
invokeMethodWithVariants(obj, "compute", args, &retVal,
  Qt::DirectConnection);
// assuming successful invoke, retVal should contain a QString

Now you can call any method dynamically. Yay!

This trick is used in Leapfrog’s Qt-to-Cocoa object bridge. We’ll also make use of it in the Synchronized Threads series.

Comments (1)

Bad Behavior has blocked 557 access attempts in the last 7 days.