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 samplesAcqui开发者_StackOverflow社区red(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.
Qt doesn't like class templates that inherit from QObject. If you don't need the runtime introspection of QObject, you might want to use Boost.Signals instead, which doesn't have this problem.
Introducing the Boost.Signals library in a Qt project can be a bit challenging, though. In Boost.Signals, signals
is a namespace while Qt #define
s signal
to protected
. You should make sure your Qt project compiles with QT_NO_KEYWORDS
defined (CONFIG += no_keywords
in qmake), before introducing Boost.Signals.
You actually can use Qt classes with Templates with little modifications to your code.
The issue with Qt classes and Templates is the moc tool that generates the meta object information, it has no knowledge on templates at all, but the generated code doesn't needs to come from Moc.
you can use Verdigris to create your C++/QObject class that will work with templates without any issues, bypassing the moc step for such code.
精彩评论