QT : Templated Q_OBJECT class
Asked Answered
B

4

56

Is it possible to have a template class, which inherit from QObject (and has Q_OBJECT macro in it's declaration)?

I would like to create something like adapter for slots, which would do something, but the slot can take arbitrary number of arguments (number of arguments depends on the template argument).

I just tried doing it, and got linker errors. I guess gmake or moc is not getting called on this template class. Is there a way to do this? Maybe by explicitly instantiating templates?

Bennie answered 9/12, 2010 at 11:0 Comment(4)
Have you observed the inclusion model ? :) linuxtopia.org/online_books/programming_books/…Bougie
@Armen Yes, I didn't think about that. I thought qt preprocessor would munch it.Burnisher
See my post. May help.Phenocryst
Rationale about why Q_OBJECT doesn't mix well with template can be found here: Academic Solutions to Academic Problems.Sid
P
46

It is not possible to mix template and Q_OBJECT but if you have a subset of types you can list the slots and signals like this:

    class SignalsSlots : public QObject
    {
        Q_OBJECT

    public:
        explicit SignalsSlots(QObject *parent = 0) :
            QObject(parent) {}

    public slots:
        virtual void writeAsync(int value) {}
        virtual void writeAsync(float value) {}
        virtual void writeAsync(double value) {}
        virtual void writeAsync(bool state) {}
        virtual void writeAsync(svga::SSlideSwitch::SwitchState state) {}   

    signals:
        void readAsynkPolledChanged(int value);
        void readAsynkPolledChanged(float value);
        void readAsynkPolledChanged(double value);
        void readAsynkPolledChanged(bool state);
        void readAsynkPolledChanged(svga::SSlideSwitch::SwitchState state);
    };
...
template <class T>
class Abstraction : public SignalsSlots
{...
Pumpernickel answered 2/10, 2013 at 14:9 Comment(10)
Simple separation of notions...!Bradfordbradlee
Good work-around. Moreover, if the signal definitions need to access the template parameter, they can be simply declared as virtual abstract in the SignalsSlots equivalent and defined in the class template.Barca
Can you explain that? The SignalsSlots class is not a template class, so how am I able to use a Template parameter in the signature of the signal? For example if I wanted to do something like this: void someSignal(T value).Julissa
You can use the subset only with the template, that is in the example; int, float, double, bool, SwitchState. Add all types you need support for. I think that version 5 of QT can mix template and Q_OBJECT, but I have not tried it.Pumpernickel
@JonasW Qt 5.6 still does not support templates with the Q_OBJECT macro.Poniard
I would appreciate a few more lines in the Abstraction class example. Do you create regular methods with the same name as the slots you are overriding? The last 10% is confusinGarlaand
For anyone else interested, you have to emit a signal within Abstraction using "emit SingalsSlots::readAsynkPolledChange(X)" - yes you must reference the base signals and slots class.Garlaand
There is an example within the Qt sources that uses a very similar pattern for a templated QObject: QFutureWatcher<T>. It has a non-templated base class of QFutureWatcherBase that defines the signals/slots and has the Q_OBJECT macro. source link. Note that you still can't use the template type in signals/slots.Juncaceous
But how do you define slot overrides in the template class (e.g. for one of the writeAsync slots above)? Still get Error: Class declaration lacks Q_OBJECT macro. if I define one there.Tachycardia
The thing is that you can't use slots / signals / properties with the template type.Fly
V
12

Taking into account some restrictions: you can. First please became familiar (if already not) https://doc.qt.io/archives/qq/qq16-dynamicqobject.html. - it will help to imlement it. And about restrictions: you can have a template QObject class i.e. template class derived from QObject, but:

  1. Do not tell the moc to compile it.
  2. Q_OBJECT is just a macro and you have to replace it by it real content which is virtual interface and something else :)
  3. Implement QMetaObject activation (above mentioned virtual interface and be caution with object info data, which is also come from Q_OBJECT) and some else functionality and you will have template QObject (even with template slots)
  4. But as I managed to catch the one draw back - it is not possible to simply use this class as a base for another class.
  5. There are some other drawbacks - but I think the detail investigation will show you them.

Hope this will helpful.

Veliger answered 9/12, 2010 at 11:0 Comment(1)
Without deep investigation into this, it really feels like a maintenance nightmare to go down this path and attempt to reimplement Q_OBJECT macro just so I can use signals and slots in a templated class. Do you have an example of how you've done this successfully?Footless
M
5

It is still not possible to mix templates and Q_OBJECT but depending on your use case you may use the new 'connect'-syntax. This allows at least the usage of template-slots.

Classical non-working approach:

class MySignalClass : public QObject {
  Q_OBJECT
public:

signals:
  void signal_valueChanged(int newValue);
};     


template<class T>
class MySlotClass : public QObject {
  Q_OBJECT
public slots:
  void slot_setValue(const T& newValue){ /* Do sth. */}
};

Desired usage but not compilable:

MySignalClass a;
MySlotClass<int> b;

QObject::connect(&a, SIGNAL(signal_valueChanged(int)),
                 &b, SLOT(slot_setValue(int)));

Error: Template classes not supported by Q_OBJECT (For MySlotClass).

Solution using new the 'connect'-syntax:

// Nothing changed here
class MySignalClass : public QObject {
  Q_OBJECT
public:

signals:
  void signal_valueChanged(int newValue);
};


// Removed Q_OBJECT and slots-keyword
template<class T>
class MySlotClass : public QObject {  // Inheritance is still required
public:
  void slot_setValue(const T& newValue){ /* Do sth. */}
};

Now we can instantiate desired 'MySlotClass'-objects and connect them to appropriate signal emitters.

  MySignalClass a;
  MySlotClass<int> b;

  connect(&a, &MySignalClass::signal_valueChanged,
          &b, &MySlotClass<int>::slot_setValue);

Conclusion: Using template-slots is possible. Emitting template signals is not working since a compiler error will occur due to missing Q_OBJECT.

Magenmagena answered 29/6, 2017 at 6:1 Comment(2)
You mean emitting a signal is still not working, right? (thanks for this)Footless
@Magenmagena I was really happy about this but I'm getting "Error: Class declaration lacks Q_OBJECT macro" - did it change since 2017?Bonfire
B
1

I tried explicitly instantiating templates, and got this :

core_qta_qt_publisheradapter.hpp:96: Error: Template classes not supported by Q_OBJECT

I guess that answers my question.

EDIT

Actually, if I place whole template class definition in the header, then the qt preprocessor doesn't process it, and then I get linker errors. Therefore it must be possible to do it, if I add missing methods.

EDIT #2

This library did exactly what I wanted - to use a custom signal/slot mechanism, where the slot has not-defined signature.

Bennie answered 9/12, 2010 at 11:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.