QT: Шаблонизированный класс с Q_OBJECT
Вопрос: Возможность использования шаблонного класса, наследующего от QObject, с макросом Q_OBJECT
Здравствуйте! У меня возникла проблема с использованием шаблонного класса, который должен наследоваться от QObject и содержать макрос Q_OBJECT в своем объявлении.
Я пытаюсь создать адаптер для слотов, который выполняет определенные действия, однако количество аргументов слота может быть произвольным и зависит от аргумента шаблона.
Когда я попытался реализовать это, я столкнулся с ошибками при компиляции, связанными с линковщиком. Я предполагаю, что gmake или moc не вызываются для этого шаблонного класса. Есть ли способ сделать это? Возможно, стоит явно инстанциировать шаблоны?
Буду признателен за любые советы или решения!
3 ответ(ов)
Вам не удастся смешать шаблоны (template) и 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
{
...
Таким образом, вы создаете класс SignalsSlots
, который наследуется от QObject
и включает в себя определенные слоты и сигналы. Затем, ваш шаблонный класс Abstraction
может наследоваться от SignalsSlots
. Убедитесь, что классы, которые вы собираетесь использовать как шаблоны, соответствуют требованиям Qt и не конфликтуют с макросами метаобъектов.
Принимая во внимание некоторые ограничения, вы можете реализовать это. Сначала ознакомьтесь (если еще не знакомы) с документом по ссылке: https://doc.qt.io/archives/qq/qq16-dynamicqobject.html — это поможет вам в реализации.
Что касается ограничений: вы можете создать шаблонный класс QObject, т.е. шаблонный класс, производный от QObject, но:
- Не говорите moc компилировать его.
- Q_OBJECT — это просто макрос, и вам нужно заменить его на его реальное содержимое, которое представляет собой виртуальный интерфейс и кое-что еще 😃
- Реализуйте активацию QMetaObject (упомянутый выше виртуальный интерфейс, будьте осторожны с объектной информацией, которая также поступает от Q_OBJECT) и другую функциональность, и у вас получится шаблонный QObject (даже с шаблонными слотами).
- Но, как я успел заметить, есть один недостаток — вы не можете просто использовать этот класс в качестве базового для другого класса.
- Существуют и другие недостатки, но я думаю, что более подробное исследование покажет вам их.
Надеюсь, это будет полезно.
В настоящее время невозможно смешивать шаблоны и 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){ /* Делает что-то */}
};
Желаемое использование, но не компилируется:
MySignalClass a;
MySlotClass<int> b;
QObject::connect(&a, SIGNAL(signal_valueChanged(int)),
&b, SLOT(slot_setValue(int)));
Ошибка: Шаблонные классы не поддерживаются Q_OBJECT (для MySlotClass).
Решение с использованием нового синтаксиса 'connect':
// Здесь ничего не изменилось
class MySignalClass : public QObject {
Q_OBJECT
public:
signals:
void signal_valueChanged(int newValue);
};
// Убрали Q_OBJECT и ключевое слово slots
template<class T>
class MySlotClass : public QObject { // Наследование по-прежнему требуется
public:
void slot_setValue(const T& newValue){ /* Делает что-то */}
};
Теперь мы можем создавать экземпляры нужных объектов 'MySlotClass' и подключать их к соответствующим сигналам.
MySignalClass a;
MySlotClass<int> b;
connect(&a, &MySignalClass::signal_valueChanged,
&b, &MySlotClass<int>::slot_setValue);
Заключение: Использование слотов-шаблонов возможно. Испускание сигналов шаблонов не работает, так как возникнет ошибка компиляции из-за отсутствия Q_OBJECT.
Где и зачем нужно использовать ключевые слова "template" и "typename"?
В чем разница между параметрами шаблона "typename" и "class"?
Как проверить, имеет ли шаблонный класс член-функцию?
Хранение определений шаблонных функций C++ в .CPP файле
Что такое Правило трёх?