Skip to content

Creating QMetaObjects from GObject Introspection data

Wednesday, 4 February 2009  |  richard dale

With the next Akademy and GUADEC being co-located in Gran Canaria, I thought it would be nice to do a bit of 'cross-desktopping'. My Gnome friend, Alberto Ruiz, is organizing the Gtk+ 3.0 Theming API Hackfest with some GTK hackers along with Jens Bache-Wiig from Qt Software. That is really good news as it means they'll be thinking about making the toolkits apis compatible at the look and feel level right from the start.

My own idea for improving toolkit inter-operability is so see if it is possible to generate Qt's runtime introspeciton data in the form of QMetaObjects, from a GObject introspection repository and vice versa. So a Qt program looking at a GTK class would see slot names in camel case using Qt datatypes such as QStrings, qreals and QList types, and it would be able to connect Qt signals to the GTK slots as though they were native.

I began looking into how GObjects worked when Norbert Frese started a discussion on the kde-core-devel mailing list about a GIO-KIO bridge that he was working on. But I found there wasn't enough introspection data in a vanilla GObject to make it possible to construct a QMetaObject. I met Norbert at last years Akademy and he showed my the code for the bridge and explained what the problems were, and he also told me about the 'gobject introspection' project to make it easier to produce language bindings for the GTK apis. Although GIO is a Gnome library it didn't actually make much use of GObjects which meant that it wouldn't be possible to automate the GIO-KIO bridge and it looked like there was a heroic amount of hand crafted code that would be needed.

I've done some initial work on seeing how gobject introspection works by writing code to dump the contents of the Clutter api, and it looks pretty good. You can get the names of methods and their arg types, class names and the class hierarchy, property names, enums and flags. In fact everything that goes into making up QMetaObjects is in there. One problem though is that the enums are C based and global in scope and I haven't worked out how to solve that yet. I know how to construct QMetaObjects on the fly because thats how the QtRuby and Qyoto bindings work, and so it shouldn't be too hard. QObjects have a virtual method called metaObject() and all you need to do is to override that and return your custom constructed meta object.

After the meta objects have been created the next problem is the get the runtime invocation part working. All slot and property invocations go through a method called qt_metacall() which looks like this:

int MyClass::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QWidget::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;
    if (_c == QMetaObject::InvokeMetaMethod) {
        switch (_id) {
        case 0: foobar((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 1: frob((*reinterpret_cast< const QImage(*)>(_a[1]))); break;

qt_metacall() takes an integer '_id' arg and it is used in a big switch statement in the moc generated code to despatch the call the the right slot method. The QMetaObject::Call arg tells you what sort of call is being made - calling a slot, setting a property and so on. Finally, the 'void ** _a' arg is an array of the argument values to use. To get GTK slot invocations working it will mean marshalling code from the '_a' array to the form expected by libffi in the g_function_info_invoke() function. After the ffi call has been made, any arg types whose values can be changed must be copied back into the C++ '_a' array along with the return value.

And that is about all there is to it - what could possibly go wrong? I asked on the irc.gnome.org #introspection channel whether anyone was going to FOSDEM this year and there were several people going. So I'm looking forward to have some discussion and getting a 'hello world' working there.