4

I'm trying to hack with Qt's signals and slots, and I ran into an issue where QMetaType::invokeMethod won't properly pass pointer arguments to the slot being called.

call(QObject *receiver, const char *slot, const QList<QGenericArgument> &args)
{
    const QMetaObject *meta = receiver->metaObject();
    bool success = meta->invokeMethod(receiver, slot, 
            args.value(0, QGenericArgument()),
            args.value(1, QGenericArgument()),
            args.value(2, QGenericArgument()),
            ...
            args.value(9, QGenericArgument()));
}

Then I call it the following way:

MyReceiver *receiver;
MyObject *myObject;

call(receiver, "mySlot", QList<QGenericArgument>() << Q_ARG(MyObject *, myObject));

Where class MyObject : public QObject { ... }. I also do Q_DECLARE_METATYPE(MyObject *) and qRegisterMetaType<MyObject *>("MyObject *")

What happens is that the slot on the receiver is being invoked, but with the value of the argument is always 0 no matter what I pass to the call(...) as Q_ARG

Out of curiosity I looked into the auto-generated MOC file of the receiver, and found that the slots are invoked with the following code:

void MyReceiver::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        Q_ASSERT(staticMetaObject.cast(_o));
        MyReceiver *_t = static_cast<MyReceiver *>(_o);
        switch (_id) {
        case 0: _t->mySlot((*reinterpret_cast< MyObject*(*)>(_a[1]))); break;
        default: ;
        }
    }
}

Turns out that the value of _a[1] bears proper address of MyObject *. But the reinterpret_cast turns it into 0.

Now I have the following questions:

1) How to programmatically invoke a slot and make sure that the pointer arguments are properly passed to the slot? 2) What does this *reinterpret_cast< MyObject*(*)>(_a[1]) mean? What the extra parentheses (*) mean, and how to interpret this piece of code?

ak.
  • 3,329
  • 3
  • 38
  • 50
  • Have a look into the SLOT() macro. I's been a while for me, but this may answer your question. – dmaij Dec 03 '12 at 15:39
  • `invokeMethod` allows you to call a slot just by its name and argument values, without providing complete signature. It works fine, but doesn't work with non-standard argument types, at least for me... – ak. Dec 03 '12 at 16:22
  • This should work. Your call() implementation does not appear to actually forward the arguments (or is this just your sample code being incorrect?). Does it work if you call QMetaObject::invokeMethod directly? _a[1] is a pointer to a pointer, hence the (*) in the reinterpret_cast. – Dan Milburn Dec 03 '12 at 17:20
  • Yep, forgot to rename the variable, will edit right away. But back to `_a`... I think `_a[1]` is just a pointer (since `_a` is a pointer to a pointer). Why not just write `reinterpret_cast< MyObject *>(_a[1])`? – ak. Dec 03 '12 at 17:26

1 Answers1

0

Ok, I think I figured why it's not working... Q_ARG only will create a pointer to my pointer and store the former. I didn't mention that the call function was part of the Task call meant to invoke a slot later on - when the values wrapped into Q_ARG are already out of scope. Basically Q_ARG only maintains a weak reference to the argument object.

ak.
  • 3,329
  • 3
  • 38
  • 50