Я занимаюсь разработкой приложения для сбора научных данных с использованием 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 базового класса иметь дело с некоторыми из более общих аспектов системы типов.
Итак, есть ли способ получить параметр шаблонного сигнала? Есть ли лучшая архитектура, чем та, которую я предложил?