Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combine QtConcurrent call

Tags:

c++

promise

qt

qt5

I have three methods and all of them returns a string, I want to run all of them using QtConcurrent and get their return into a single list or something like that. QtConcurrent::mapped is ideal as it returns an iterator but I can only run one method at time. In JavaScript there's promise.all([method_a, method_b, method_c]), it will automatically merge their return into a single result (iterator). How to do that in Qt?

like image 224
Mr Gisa Avatar asked Nov 04 '25 22:11

Mr Gisa


1 Answers

Since you have several methods to call, you can pass them as a sequence of functors as the first argument to QtConcurrent::mapped. The mapping functor would be an apply functor that takes a functor representing the method call and returns the result of invoking it.

First, let's have our class:

// https://github.com/KubaO/stackoverflown/tree/master/questions/concurrent-combine-49802153
#include <QtConcurrent>
#include <functional>
#include <initializer_list>
#include <type_traits>

class Cls {
public:
   QString method1() const { return QStringLiteral("10"); }
   QString method2() const { return QStringLiteral("20"); }
   QString method3() const { return QStringLiteral("30"); }
};

The apply_t functor invokes the method passed to it as an argument:

template <class Method> struct apply_t {
   using result_type = typename std::result_of_t<Method()>;
   auto operator()(Method method) {
      return method();
   }
};

Let's make it convenient to make such applicators from the type of a sequence of functors to be called:

template <class Sequence, class A = apply_t<typename std::decay_t<Sequence>::value_type>>
A make_apply(Sequence &&) { return {}; }

For convenience, we'll also have a vector generator in the spirit of e.g. make_unique, etc.:

template <class T> QVector<T> make_vector(std::initializer_list<T> init) {
   return {init};
}

Then, the problem becomes fairly simple. First, we make a vector of bound methods that will be called. Then we pass the methods to call, as well as the applicator to operate on them, to QtConcurrent::mapped. The results() gives a list of all results of the method calls, in sequence.

int main() {
   Cls obj;
   auto const methods = make_vector({
                                 std::bind(&Cls::method1, &obj),
                                 std::bind(&Cls::method2, &obj),
                                 std::bind(&Cls::method3, &obj)
                              });
   QFuture<QString> result =
         QtConcurrent::mapped(methods, make_apply(methods));
   Q_ASSERT((result.results() == QStringList{"10", "20", "30"}));
}

Instead of making a custom apply_t class, we could use a lambda, wrapped to provide the result_type member type that QtConcurrent::mapped expects. See this answer for details of wrapping the lambda. The rest of this answer provides examples of such wrapping.

like image 125
Kuba hasn't forgotten Monica Avatar answered Nov 06 '25 11:11

Kuba hasn't forgotten Monica



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!