I am developing a scientific data acquisition application using Qt. Since I'm not a deep expert in Qt, I'd like some architecture advise from the community on the following problem:
The application supports several hardware acquisition interfaces but I would like to provide an common API on top of those interfaces. Each interface has a sample data type and a units for its data. So I'm representing a vector of samples from each device as a std::vector
of Boost.Units quantities (i.e. std::vector<boost::units::quantity<unit,sample_type> >
). I'd like to use a multi-cast style architecture, where each data source broadcasts newly received data to 1 or more interested parties. Qt's Signal/Slot mechanism is an obvious fit for this style. So, I'd like each data source to emit a signal like
typedef std::vector<boost::units::quantity<unit,sample_type> > SampleVector
signals:
void samplesAcquired(SampleVector sampleVector);
for the unit and sample_type appropriate for that device. Since tempalted QObject
subclasses aren't supported by the meta-object compiler, there doesn't seem to be a way to have a (tempalted) base class for all data sources which defines the samplesAcquired
Signal. In other words, the following won't work:
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);
};
The best option I've been able to come up with is a two-layered approach:
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);
};
The samplesAcquired
signal now gives a timestamp and number of samples for the acquisition and clients must use the IAcquiredSamples
API to retrieve those samples. Obviously data sources must subclass both DataSource and IAcquiredSamples
.
The disadvantage of this approach appears to be a loss of simplicity in the API... it would be much nicer if clients could get the acquired samples in the Slot connected. Being able to use Qt's queued connections would also make threading issues easier instead of having to manage them in the acquiredData
method within each subclass.
One other possibility, is to use a QVariant
argument. This necessarily puts the onus on subclass to register their particular sample vector type with Q_REGISTER_METATYPE
/qRegisterMetaType
. Not really a big deal. Clients of the base class however, will have no way of knowing what type the QVariant
value type is, unless a tag struct is also passed with the signal. I consider this solution at least as convoluted as the one above, as it forces clients of the abstract base class API to deal with some of the gnarlier aspects of type system.
So, is there a way to achieve the templated signal parameter? Is there a better architecture than the one I've proposed?
There is QVariant type -- you can create your custom sub-type on it
and use it as parameter (if I understand your right and that's what you want) in signals
http://doc.trolltech.com/qq/qq14-metatypes.html#customtypesinqvariant
One simplification to your two-layered approach would be to have the QObject
class as a non-templated base for the class template i.e. something like
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);
};
Note that the drawback to this approach is that since you cannot use the Q_OBJECT
macro in the class template there is no information about it in Qt's meta-object system.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With