I wrote a wrapper class in Qt for an external executable. It has plenty of methods, and most of them:
- are time-consuming.
- need to return the values of different types.
The synchronous wrapper is pretty straight forward in this case:
class SyncWrapper {
public:
// Values are fetched *synchronously* in these methods
QString name() const;
QStringList files() const;
bool remove(const QString &file) const;
};
However, I would like to make this wrapper asynchronous, like this:
class AsyncWrapper {
Q_OBJECT
public:
// Values are fetched *asynchronously* in these methods
void name() const;
void files() const;
void remove(const QString &file) const;
signals:
// Values are returned via signals
void nameReady(const QString &name) const;
void filesReady(const QStringList &files) const;
void removeDone(bool success) const;
};
Problems
I'm not sure if this pattern is ok as there are multiple points concerning me:
- Duplication. Imagine that there are 100 methods. Each of these methods will need a dedicated signal.
- Race condition. If I run the same method multiple times, I cannot know the order of the signals I catch.
Possible Solutions
Some other ideas I came up with:
- Stick to synchronous wrapper and use
QtConcurrent::run
when calling the class methods. Unfortunately, this is a very bulky solution. - Return
QFuture
objects from methods. Again, this will require handling them usingQFutureWatcher
s outside the class which is still very bulky. - Create a separate class for each method. If I'm not mistaken, this is a command pattern. Though this will majorly increase the amount of classes, I believe this is the cleanest solution.
What is the proper way to design such asynchronous wrapper in Qt?
I think the command pattern could work here.
Start with the abstract command interface:
Subclasses aren't that complicated, just give them some status and override
execute
, e.g.or
This way you can build a set of objects that perform several different actions, and yet you can implement a command router and keep the chance to run commands asyncrounously or not:
So you would end up with something like: