its just a quiz... (for me at least)



  • Hello!

    What I want to do, could be done many ways, I suppose....
    Anyway, say, I'm gonna init some worker thread, leaving GUI thread's widgets signal-slot connected with object, that will do all job inside newly created thread. This object, call it AbstractDevice, has to behave differentially, depending upon argument, I pass to the constructor. So, the implementation has to be split into separate classes, derived from AbstractDevice. And here is the quiz: I don't want to care about which ParticularDevice implementation will do the job, when I instantiate it. They all will have the same signals&slots interface, derived from AbstractDevice. Is it possible to do?


  • Moderators

    As you said - it can be done many ways.
    To me it sounds like a classic factory + worker scenario, so here's a brief. Keep in mind I'm leaving stuff like error checking out for brevity.

    First create the worker interface. For example:

    class AbstractDevice : public QObject
    {
        Q_OBJECT
    public:
        AbstractDevice(QObject* parent = nullptr) : QObject(parent) {
            connect(this, &AbstractDevice::finished, this, &AbstractDevice::deleteLater); //auto destruction
        }
    public slots:
        virtual void doWork() = 0; //this is the interface that actual workers will implement
    
    signals:
        void finished() const; //something to let the world know when the work is done
    };
    

    Then implement some concrete workers, e.g.

    class ConcreteDevice1 : public AbstractDevice
    {
    public:
        ConcreteDevice1(QObject* parent = nullptr) : AbstractDevice(parent) {}
        void doWork() override { qDebug() << "CD1 working"; emit finished(); }
    };
    

    Next, you said that the type of work to do will depend on some argument, so lets define these types. Could be anything but I'll use enum for simplicity:

    enum WorkType { Stuff, OtherStuff };
    

    We need a worker thread to run the tasks, so here's a very minimal implementation:

    class WorkManager : public QThread
    {
    public:
        WorkManager(QObject* parent  = nullptr) : QThread(parent) {}
    
        void scheduleJob(AbstractDevice* worker) {
            worker->moveToThread(this);
            worker->metaObject()->invokeMethod(worker, "doWork"); //call the slot in the worker thread
        }
    };
    

    Last thing of your requirements is that at call site you don't want to know the class of worker that will do the job. So lets make a basic factory that will take care of that:

    class WorkerFactory : public QObject
    {
        Q_OBJECT
    public:
        WorkerFactory(QObject* parent = nullptr) : QObject(parent) {}
    
        template<typename T>
        void registerWorkerType(WorkType type) { types[type] = makeDevice<T>; }
    
        AbstractDevice* createWorker(WorkType type, QObject* parent = nullptr) { return types[type](parent); }
    
    private:
        template <typename T>
        static AbstractDevice* makeDevice(QObject* parent) { return new T(parent); }
    
        QMap<WorkType, AbstractDevice*(*)(QObject*)> types;
    };
    

    Right, all the pieces are in place, now you can use them.
    Create an instance of the factory and register all the workers:

    WorkerFactory factory;
    factory.registerWorkerType<ConcreteDevice1>(WorkType::Stuff);
    factory.registerWorkerType<ConcreteDevice2>(WorkType::OtherStuff);
    

    Create the worker thread manager and start the thread:

    WorkManager mgr;
    mgr.start();
    

    Don't forget to stop it when your app is done:

    mgr.quit();
    mgr.wait();
    

    Now, when you need some job done all you do is create a worker and schedule it:

    auto worker = factory.createWorker(WorkType::OtherStuff);
    mgr.scheduleJob(worker);
    //you can connect to some extra signals of the worker here if it has any
    


  • wow! its beautiful! )


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.