Архитектура для Qt SIGNAL с шаблонным типом аргумента для подкласса - PullRequest
9 голосов
/ 16 марта 2010

Я занимаюсь разработкой приложения для сбора научных данных с использованием Qt. Поскольку я не являюсь глубоким экспертом в Qt, я хотел бы получить от сообщества совет по архитектуре по следующей проблеме:

Приложение поддерживает несколько аппаратных интерфейсов сбора данных, но я хотел бы предоставить общий API поверх этих интерфейсов. Каждый интерфейс имеет тип данных и единицы измерения для своих данных. Таким образом, я представляю вектор выборок с каждого устройства в виде std::vector количества Boost.Units (т.е. std::vector<boost::units::quantity<unit,sample_type> >). Я хотел бы использовать архитектуру многоадресного стиля, в которой каждый источник данных передает новые полученные данные одной или нескольким заинтересованным сторонам. Механизм сигналов / слотов в Qt явно подходит для этого стиля. Итак, я бы хотел, чтобы каждый источник данных излучал сигнал типа

typedef std::vector<boost::units::quantity<unit,sample_type> > SampleVector
signals:
    void samplesAcquired(SampleVector sampleVector);

для единицы измерения и sample_type, подходящих для этого устройства. Поскольку временные подклассы QObject не поддерживаются компилятором мета-объектов, похоже, не существует способа иметь (временный) базовый класс для всех источников данных, который определяет сигнал samplesAcquired. Другими словами, следующие не будут работать :

template<T,U> //sample type and units
class DataSource : public QObject {
  Q_OBJECT
  ...
  public:
    typedef std::vector<boost::units::quantity<U,T> > SampleVector
  signals:
    void samplesAcquired(SampleVector sampleVector);
};

Лучший вариант, который мне удалось найти, - это двухслойный подход:

template<T,U> //sample type and units
class IAcquiredSamples {
    public:
        typedef std::vector<boost::units::quantity<U,T> > SampleVector
        virtual shared_ptr<SampleVector> acquiredData(TimeStamp ts, unsigned long nsamples);
};

class DataSource : public QObject {
    ...
    signals:
      void samplesAcquired(TimeStamp ts, unsigned long nsamples);
};

Сигнал samplesAcquired теперь дает метку времени и количество выборок для получения, и клиенты должны использовать API IAcquiredSamples для получения этих выборок. Очевидно, что источники данных должны быть подклассами как DataSource, так и IAcquiredSamples.

.

Недостатком этого подхода является потеря простоты в API ... было бы гораздо приятнее, если бы клиенты могли подключить полученные образцы в слот. Возможность использования подключений Qt в очереди также облегчила бы многопоточность, вместо того, чтобы управлять ими в методе acquiredData в каждом подклассе.

Еще одна возможность - использовать аргумент QVariant. Это обязательно накладывает ответственность на подкласс для регистрации их конкретного типа вектора выборки с Q_REGISTER_METATYPE / qRegisterMetaType. Не очень большое дело. Однако клиенты базового класса не смогут узнать, к какому типу относится тип значения QVariant, если только структура тега также не передана вместе с сигналом. Я считаю, что это решение, по крайней мере, столь же запутанное, как и вышеприведенное, поскольку оно вынуждает клиентов абстрактного API базового класса иметь дело с некоторыми из более общих аспектов системы типов.

Итак, есть ли способ получить параметр шаблонного сигнала? Есть ли лучшая архитектура, чем та, которую я предложил?

Ответы [ 4 ]

2 голосов
/ 16 марта 2010

Существует тип QVariant - вы можете создать свой собственный подтип на нем
и использовать его в качестве параметра (если я понимаю ваше право и это то, что вы хотите) в сигналах
http://doc.trolltech.com/qq/qq14-metatypes.html#customtypesinqvariant

1 голос
/ 05 апреля 2017

Вы на самом деле можете использовать классы Qt с шаблонами с небольшими изменениями в вашем коде.

Проблема с классами Qt и шаблонами - это инструмент moc, который генерирует информацию мета-объекта, он вообще не знает шаблонов, но сгенерированный код не должен поступать из Moc.

вы можете использовать Verdigris для создания вашего класса C ++ / QObject, который будет работать с шаблонами без каких-либо проблем, минуя шаг moc для такого кода.

1 голос
/ 27 апреля 2011

Qt не нравится шаблоны классов, которые наследуются от QObject. Если вам не нужен самоанализ QObject во время выполнения, вы можете вместо этого использовать Boost.Signals , который не имеет этой проблемы.

Впрочем, введение библиотеки Boost.Signals в проект Qt может быть немного сложным. В Boost.Signals signals - это пространство имен, в то время как Qt #define s signal до protected. Вы должны убедиться, что ваш проект Qt компилируется с определением QT_NO_KEYWORDS (CONFIG += no_keywords в qmake), прежде чем вводить Boost.Signals.

1 голос
/ 01 апреля 2010

Одним из упрощений вашего двухуровневого подхода было бы иметь класс QObject в качестве базы без шаблонов для шаблона класса, то есть что-то вроде

class DataSourceBase : public QObject {
    Q_OBJECT
    ...
    signals:
      void samplesAcquired(TimeStamp ts, unsigned long nsamples);
};

template<T,U> //sample type and units
class DataSource : public DataSourceBase {
    public:
        typedef std::vector<boost::units::quantity<U,T> > SampleVector
        virtual shared_ptr<SampleVector> acquiredData(TimeStamp ts, unsigned long nsamples);
};

Обратите внимание, что недостатком этого подхода является то, что поскольку вы не можете использовать макрос Q_OBJECT в шаблоне класса, в мета-объектной системе Qt нет информации об этом.

...