53

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?

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • Have you observed the inclusion model ? :) http://linuxtopia.org/online_books/programming_books/c++_practical_programming/c++_practical_programming_134.html – Armen Tsirunyan Dec 09 '10 at 11:04
  • @Armen Yes, I didn't think about that. I thought qt preprocessor would munch it. – BЈовић Dec 09 '10 at 11:07
  • See my [post](https://codereview.stackexchange.com/questions/268217/handle-c-templates-in-qt5). May help. – LRDPRDX Sep 21 '21 at 13:00
  • Rationale about why Q_OBJECT doesn't mix well with template can be found here: [Academic Solutions to Academic Problems](https://doc.qt.io/archives/qq/qq15-academic.html). – Delgan Feb 23 '22 at 09:33

4 Answers4

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
{...
Jonas W
  • 498
  • 4
  • 4
  • 3
    Simple separation of notions...! – g24l Oct 22 '15 at 12:58
  • 1
    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. – Greg Kramida Oct 23 '15 at 20:49
  • 1
    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)`. – bweber Mar 01 '16 at 08:49
  • 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. – Jonas W Mar 05 '16 at 18:41
  • @JonasW Qt 5.6 still does not support templates with the Q_OBJECT macro. – Will Bickford Nov 08 '16 at 19:45
  • 1
    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 confusin – TSG May 21 '20 at 17:53
  • 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. – TSG May 23 '20 at 17:52
  • There is an example within the Qt sources that uses a very similar pattern for a templated QObject: QFutureWatcher. It has a non-templated base class of QFutureWatcherBase that defines the signals/slots and has the Q_OBJECT macro. [source link](https://code.woboq.org/qt5/qtbase/src/corelib/thread/qfuturewatcher.h.html#QFutureWatcher). Note that you still can't use the template type in signals/slots. – Dean Johnson Feb 17 '21 at 17:15
  • 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. – dqbydt Dec 09 '21 at 22:21
  • The thing is that you can't use slots / signals / properties with the template type. – ניר May 03 '23 at 04:50
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.

TManhente
  • 2,526
  • 1
  • 15
  • 11
milyaaf
  • 136
  • 1
  • 4
  • 3
    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? – Lisa May 08 '20 at 01:12
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.

Anonymous
  • 4,617
  • 9
  • 48
  • 61
  • 1
    You mean emitting a signal is still not working, right? (thanks for this) – Lisa May 08 '20 at 01:15
  • @Anonymous I was really happy about this but I'm getting "Error: Class declaration lacks Q_OBJECT macro" - did it change since 2017? – smsware Apr 11 '21 at 16:21
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.

Community
  • 1
  • 1
BЈовић
  • 62,405
  • 41
  • 173
  • 273