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? -
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: using AbstractDevice::AbstractDevice; 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: using QThread::QThread; 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: using QObject::QObject; 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! )