Skip to content

Qt4 moc format and Smoke v2

Saturday, 11 June 2005  |  richard dale

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.