QT:模板化的Q_OBJECT类

53

是否有可能创建一个从 QObject 继承(并在其声明中具有 Q_OBJECT 宏)的模板类?

我想创建一个适配器,用于槽函数,它可以执行一些操作,但是槽函数可以接受任意数量的参数(参数数量取决于模板参数)。

我尝试了这样做,但得到了链接器错误。我猜 gmake 或 moc 没有调用这个模板类。有没有一种方法来解决这个问题?也许通过显式实例化模板?


你有观察到这个包含模型吗? :) http://linuxtopia.org/online_books/programming_books/c++_practical_programming/c++_practical_programming_134.html - Armen Tsirunyan
@Armen 是的,我没有考虑到那一点。我以为Qt预处理器会处理它。 - BЈовић
请查看我的帖子,或许会有所帮助。 - LRDPRDX
Q_OBJECT和模板不兼容的原理可以在这里找到: 学术解决方案 - Delgan
4个回答

46

不可能同时混合使用模板和Q_OBJECT,但如果您有一个类型的子集,可以像这样列出槽和信号:

    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
{...

3
简单的概念分离...! - g24l
1
不错的解决方案。此外,如果信号定义需要访问模板参数,则可以在SignalsSlots等效项中简单地声明为虚抽象,并在类模板中定义。 - Greg Kramida
1
你能解释一下吗?SignalsSlots类不是一个模板类,那么我如何能在信号的签名中使用模板参数呢?例如,如果我想要这样做:void someSignal(T value) - bweber
你可以只使用模板中的子集,例如:int、float、double、bool、SwitchState。添加所有需要支持的类型。我认为QT的第5个版本可以混合使用模板和Q_OBJECT,但我还没有尝试过。 - Jonas W
1
我希望在抽象类示例中再多几行代码。您是否创建与要覆盖的插槽名称相同的常规方法?最后10%很令人困惑。 - TSG
显示剩余5条评论

12

考虑到一些限制:你可以这样做。

首先,请熟悉(如果还没有的话)https://doc.qt.io/archives/qq/qq16-dynamicqobject.html。-这将有助于实现它。

关于限制:您可以拥有一个模板QObject类,即继承自QObject的模板类,但是:

  1. 不要告诉moc编译它。
  2. Q_OBJECT只是一个宏,您必须用它的实际内容来替换它,该内容是虚拟接口和其他内容:)
  3. 实现QMetaObject激活(上述虚拟接口并小心处理对象信息数据,这也来自Q_OBJECT)和一些其他功能,您将拥有模板QObject(甚至具有模板插槽)
  4. 但是,正如我设法抓住的那个缺点-不可能简单地将此类用作另一个类的基础。
  5. 还有其他一些缺点-但我认为详细调查会显示给您。

希望这有所帮助。


3
如果不深入研究,这似乎是一个维护噩梦,因为我必须重新实现Q_OBJECT宏才能在模板类中使用信号和槽。你有成功实现的例子吗? - Lisa

5

目前还无法混合使用模板和Q_OBJECT,但根据您的用例,可以使用新的“connect”语法。这至少允许使用模板槽。

传统的不起作用的方法:

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. */}
};

期望的使用方式,但无法编译:

MySignalClass a;
MySlotClass<int> b;

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

错误:模板类不支持Q_OBJECT(对于MySlotClass)。

使用新的“connect”语法解决方案:

// 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. */}
};

现在我们可以实例化所需的'MySlotClass'对象并将它们连接到适当的信号发射器。
  MySignalClass a;
  MySlotClass<int> b;

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

结论:使用模板插槽是可行的。由于缺少 Q_OBJECT,发出模板信号不起作用会导致编译器错误。

1
你的意思是发射信号还是不起作用,对吗?(谢谢) - Lisa
@Anonymous 我对此感到非常高兴,但是我遇到了“错误:类声明缺少Q_OBJECT宏”的问题 - 自2017年以来有所改变吗? - smsware

1

我尝试显式实例化模板,结果出现了以下错误:

core_qta_qt_publisheradapter.hpp:96: 错误:Q_OBJECT不支持模板类

我想这回答了我的问题。

编辑

实际上,如果我将整个模板类定义放在头文件中,则qt预处理器不会处理它,然后我会得到链接器错误。因此,如果我添加缺少的方法,就必须能够做到这一点。

编辑#2

这个库正是我想要的 - 使用自定义信号/槽机制,其中槽没有定义的签名。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接