Skip navigation.
KDE Developer's Journals

Qt4 moc format and Smoke v2

richard dale's picture

Over on the kdebindings mailing list Ashley Winters has started thinking about doing a version of the
Smoke libary using the Qt4 meta object system. My best summary description of Smoke has been 'a moc on steriods', so designing a better Smoke by extending the slots/signals idea to cover an entire api does seem a logical step.





He writes it up a blog entry, and summarizes it like this:



As I promised earler, I threw up my ideas for a Smoke based off Qt4's
meta-calling conventions. It's long and technical.

The cliffs notes version:
* Every function should be made a slot
  - that means you can connect() to anything
  - only one function call implementation required to use regular
functions, virtual functions, and signals/slot

* non-QObjects should have QObject proxy classes
  - QString/QRect/etc functions would be slots, and called the same way
as everything else

* Virtual functions in C++ should emit themselves
  - that probably means you can have multiple mouseMoveEvent handlers
  - you can handle virtual functions in ANOTHER object
    + $foo->connect($bar, mouseMoveEvent => 'doSomething')

Still more work to be done, but some interesting ideas. I like the idea of using qt_metacast() to cast a QObject to a QString, and connect to slots in a QString. Not the QStrings should be visible to the scripting language, they should use the native string type instead. And using signals/slots for overriding virtual methods in the scripting language sounds pretty neat to me




Meanwhile I've looking into the format of the meta object code generated by the Qt4 moc.
For this code:

public slots:
    void setValue(int value);

signals:
    void valueChanged(int newValue);

The moc generates the following:

static const uint qt_meta_data_LCDRange[] = {

// content:
       1,       // revision
       0,       // classname
       0,    0, // classinfo
       2,   10, // methods
       0,    0, // properties
       0,    0, // enums/sets

// signals: signature, parameters, type, tag, flags
       9,   27,   36,   36, 0x05,

// slots: signature, parameters, type, tag, flags
      37,   51,   36,   36, 0x0a,

       0        // eod
};

static const char qt_meta_stringdata_LCDRange[] = {
    "LCDRange\0valueChanged(int)\0newValue\0\0setValue(int)\0value\0"
};

const QMetaObject LCDRange::staticMetaObject = {
    { &QWidget::staticMetaObject, qt_meta_stringdata_LCDRange,
      qt_meta_data_LCDRange, 0 }
};

Just change the nulls in the string above to vertical bars, and reformat, and it becomes pretty self explanatory:

0         1         2         3         4         5         6
0123456789012345678901234567890123456789012345678901234567890
LCDRange|valueChanged(int)|newValue||setValue(int)|value|

static const uint qt_meta_data_LCDRange[] = {
...

// signals: signature, parameters, type, tag, flags
9,   // valueChanged(int)
27,   // newValue
36,  
36,
0x05, // MethodSignal | AccessProtected

// slots: signature, parameters, type, tag, flags
37,   // setValue(int)
51,   // value
36,  
36,
0x0a, // MethodSlot | AccessPublic

0        // eod
};

The enum values like 'MethodSlot' and 'AccessPublic' are defined like this in generator.cpp, which is part of the moc:
enum MethodFlags {
    AccessPrivate = 0x00,
    AccessProtected = 0x01,
    AccessPublic = 0x02,
    MethodMethod = 0x00,
    MethodSignal = 0x04,
    MethodSlot = 0x08,
    MethodCompatibility = 0x10,
    MethodCloned = 0x20,
    MethodScriptable = 0x40
};

The 'type' value is the return value, which is empty for vanilla Qt slots, but would have a value for DCOP
or DBUS slots, or Smoke method slots. That's the change that makes the Qt4 slots a lot more general purpose, and they can be used for DCOP or scripting language bindings now.




In the meta object this '&QWidget::staticMetaObject' pointer gives you the meta object of the superclass.




The slot method calls are in a case statement in qt_metacall(), and the int _id argument despatches to a particular slot. The arguments for the slot call are in the 'void **_a' argument in a very similar format to the one used by the Smoke library. If the slot had a return value it would be returned in _a[0]:
int LCDRange::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    int _id_global = _id;
    _id = QWidget::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;
    if (_c == QMetaObject::InvokeMetaMethod) {
        if (_id < 1)
            QMetaObject::activate(this, _id_global, _a);
        else switch (_id) {
        case 1: setValue(*(int*)_a[1] ) ; break;
        }
        _id -= 2;
    }
    return _id;
}

For each signal a method call is generated, which calls the QMetaObject::activate() method:
// SIGNAL 0
void LCDRange::valueChanged(int _t1)
{
    void *_a[] = { 0, (void*)&_t1 };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

And that's about it - it's all quite a bit simpler than the Qt3 moc code generation and runtime.




I shall still carry on working on a Qt4 QtRuby based on Smoke v1, and when the new Smoke v2 is working switch to that for a QtRuby 2.0 release. Mornfall has submitted a Summer of Coding proposal for adding some KDE plugin apis for ruby, so I'm not sure how these changes will affect any work he might do on that.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
geiseri's picture

the new metaobject is cool

it took me like 4 hours to get signals and slots working in kjsembed. unlike the current version we can use unlimited arguments of any supported type. its cool stuff.

we also have been looking at using the new metaobject for basicly making our database a QObject, and stored procedures slots. The end result is database applications you can drag and drop together in Qt designer. KDE 4 is going to be loads of fun!

geiseri's picture

also

if you give all of your bindings objects a QGenericArgs operator then it makes passing them to manually invoked slots trivial.

richard dale's picture

Re: also

I don't think the QtRuby bindings need to work at the invokeMethod() or QGenericArgs level. They are convenience wrappers onto qt_metacall() for C++ application programmers. For emitting a signal, QtRuby needs to marshall the ruby args passed to the signal method onto the void ** args array that can in turn be passed to QMetaObject::activate(). And calling a ruby slot means overriding qt_metacall() so that the void ** args array can be marshalled to ruby args to be passed to the target ruby method.

cniehaus's picture

QtRuby2 as in "second version

QtRuby2 as in "second version of Ruby binding for Qt" not as in "Qt-bindings for Ruby 2.0" I guess? Ruby 2 might be out of the door early next year I was told some days ago...

richard dale's picture

Re: QtRuby2 as in "second version"

Yes, I meant version 2.0 of QtRuby not ruby itself. But ruby 2.0 with YARV will make QtRuby 2.0 a lot faster - it should be a nice combination.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.