Architecture for Qt SIGNAL with subclass-specific, templated argument type
Asked Answered
A

4

9

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?

Amorino answered 15/3, 2010 at 21:58 Comment(2)
I think it can be more understandable if you give pseudo code of usage of this std::vector of Boost.Units quantities Is it need run-time polymorphism?Bandler
I don't think there is a better solution than the two you already have. Have you considered using Boost Signals or Signals2 where template parameters can be used?Hopehopeful
B
2

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

Bandler answered 16/3, 2010 at 10:40 Comment(1)
I don't think this the complete solution, unfortunately. See my edit to the question.Amorino
R
1

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.

Reject answered 31/3, 2010 at 23:41 Comment(0)
I
1

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 #defines 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.

Irrespirable answered 27/4, 2011 at 13:26 Comment(0)
H
1

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.

Hanni answered 5/4, 2017 at 15:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.