diff options
author | Jarek Kobus <[email protected]> | 2020-07-26 14:05:24 +0200 |
---|---|---|
committer | Jarek Kobus <[email protected]> | 2020-08-19 12:46:10 +0200 |
commit | 765f503b1e0b3ab18b1847fb2d91aa3e9da94ecb (patch) | |
tree | 036099821a3b09f7d39e3824a04a7555b696fbdf | |
parent | 0243951e0aba20a010577f09d6486f21d621d575 (diff) |
QtConcurrent: Introduce runWithPromise()
The differences to run() method:
1. The passed function should have additional
argument QPromise<T> &, declared as a first argument.
2. The return value of the function must be void.
Result reporting should be done through
passed QPromise<T> &promise argument.
3. By default, runWithPromise() doesn't support functors
with overloaded operator()().
In case of overloaded functors the user
needs to explicitly specify the result type
as a template parameter passed to runWithPromise,
like:
struct Functor {
void operator()(QPromise<int> &) { }
void operator()(QPromise<double> &) { }
};
Functor f;
runWithPromise<double>(f); // this will select the 2nd overload
Task-number: QTBUG-84702
Change-Id: Ie40d466938d316fc46eb7690e6ae0ce1c6c6d649
Reviewed-by: Sona Kurazyan <[email protected]>
-rw-r--r-- | src/concurrent/qtconcurrentrun.h | 139 | ||||
-rw-r--r-- | src/concurrent/qtconcurrentstoredfunctioncall.h | 89 | ||||
-rw-r--r-- | src/corelib/thread/qpromise.h | 1 | ||||
-rw-r--r-- | src/corelib/thread/qpromise.qdoc | 10 | ||||
-rw-r--r-- | tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp | 622 |
5 files changed, 858 insertions, 3 deletions
diff --git a/src/concurrent/qtconcurrentrun.h b/src/concurrent/qtconcurrentrun.h index 4f583a343c9..2f9b1b5e96d 100644 --- a/src/concurrent/qtconcurrentrun.h +++ b/src/concurrent/qtconcurrentrun.h @@ -67,13 +67,108 @@ namespace QtConcurrent { namespace QtConcurrent { +// Note: It's a copy taken from qfuture_impl.h with some specialization added +// TODO: Get rid of the code repetition and unify this for both purposes, see QTBUG-83331 +template<typename...> +struct ArgsType; + +template<typename Arg, typename... Args> +struct ArgsType<Arg, Args...> +{ + using PromiseType = void; + static const bool IsPromise = false; +}; + +// Note: this specialization was added +template<typename Arg, typename... Args> +struct ArgsType<QPromise<Arg> &, Args...> +{ + using PromiseType = Arg; + static const bool IsPromise = true; +}; + +template<> +struct ArgsType<> +{ + using PromiseType = void; + static const bool IsPromise = false; +}; + +template<typename F> +struct ArgResolver : ArgResolver<decltype(&std::decay_t<F>::operator())> +{ +}; + +// Note: this specialization was added, see callableObjectWithState() test in qtconcurrentrun +template<typename F> +struct ArgResolver<std::reference_wrapper<F>> : ArgResolver<decltype(&std::decay_t<F>::operator())> +{ +}; + +template<typename R, typename... Args> +struct ArgResolver<R(Args...)> : public ArgsType<Args...> +{ +}; + +template<typename R, typename... Args> +struct ArgResolver<R (*)(Args...)> : public ArgsType<Args...> +{ +}; + +// Note: this specialization was added, see light() test in qtconcurrentrun +template<typename R, typename... Args> +struct ArgResolver<R (*&)(Args...)> : public ArgsType<Args...> +{ +}; + +// Note: this specialization was added, see light() test in qtconcurrentrun +template<typename R, typename... Args> +struct ArgResolver<R (* const)(Args...)> : public ArgsType<Args...> +{ +}; + +template<typename R, typename... Args> +struct ArgResolver<R (&)(Args...)> : public ArgsType<Args...> +{ +}; + +template<typename Class, typename R, typename... Args> +struct ArgResolver<R (Class::*)(Args...)> : public ArgsType<Args...> +{ +}; + +template<typename Class, typename R, typename... Args> +struct ArgResolver<R (Class::*)(Args...) noexcept> : public ArgsType<Args...> +{ +}; + +template<typename Class, typename R, typename... Args> +struct ArgResolver<R (Class::*)(Args...) const> : public ArgsType<Args...> +{ +}; + +template<typename Class, typename R, typename... Args> +struct ArgResolver<R (Class::*)(Args...) const noexcept> : public ArgsType<Args...> +{ +}; + +// Note: this specialization was added, see crefFunction() test in qtconcurrentrun +template<typename Class, typename R, typename... Args> +struct ArgResolver<R (Class::* const)(Args...) const> : public ArgsType<Args...> +{ +}; + +template<typename Class, typename R, typename... Args> +struct ArgResolver<R (Class::* const)(Args...) const noexcept> : public ArgsType<Args...> +{ +}; + template <class Function, class ...Args> [[nodiscard]] auto run(QThreadPool *pool, Function &&f, Args &&...args) { return (new StoredFunctionCall<Function, Args...>( - std::forward<Function>(f), std::forward<Args>(args)...)) - ->start(pool); + std::forward<Function>(f), std::forward<Args>(args)...))->start(pool); } template <class Function, class ...Args> @@ -83,6 +178,46 @@ auto run(Function &&f, Args &&...args) return run(QThreadPool::globalInstance(), std::forward<Function>(f), std::forward<Args>(args)...); } +template <class PromiseType, class Function, class ...Args> +[[nodiscard]] +auto runWithPromise(QThreadPool *pool, Function &&f, Args &&...args) +{ + return (new StoredFunctionCallWithPromise<Function, PromiseType, Args...>( + std::forward<Function>(f), std::forward<Args>(args)...))->start(pool); +} + +template <class Function, class ...Args> +[[nodiscard]] +auto runWithPromise(QThreadPool *pool, Function &&f, Args &&...args) +{ + static_assert(ArgResolver<Function>::IsPromise, "The first argument of passed callable object isn't a QPromise<T> & type."); + using PromiseType = typename ArgResolver<Function>::PromiseType; + return runWithPromise<PromiseType>(pool, std::forward<Function>(f), std::forward<Args>(args)...); +} + +template <class Function, class ...Args> +[[nodiscard]] +auto runWithPromise(QThreadPool *pool, std::reference_wrapper<const Function> &&functionWrapper, Args &&...args) +{ + static_assert(ArgResolver<const Function>::IsPromise, "The first argument of passed callable object isn't a QPromise<T> & type."); + using PromiseType = typename ArgResolver<const Function>::PromiseType; + return runWithPromise<PromiseType>(pool, std::forward<const Function>(functionWrapper.get()), std::forward<Args>(args)...); +} + +template <class PromiseType, class Function, class ...Args> +[[nodiscard]] +auto runWithPromise(Function &&f, Args &&...args) +{ + return runWithPromise<PromiseType>(QThreadPool::globalInstance(), std::forward<Function>(f), std::forward<Args>(args)...); +} + +template <class Function, class ...Args> +[[nodiscard]] +auto runWithPromise(Function &&f, Args &&...args) +{ + return runWithPromise(QThreadPool::globalInstance(), std::forward<Function>(f), std::forward<Args>(args)...); +} + } //namespace QtConcurrent #endif // Q_CLANG_QDOC diff --git a/src/concurrent/qtconcurrentstoredfunctioncall.h b/src/concurrent/qtconcurrentstoredfunctioncall.h index f6d47f51194..a15f254c1e5 100644 --- a/src/concurrent/qtconcurrentstoredfunctioncall.h +++ b/src/concurrent/qtconcurrentstoredfunctioncall.h @@ -46,12 +46,76 @@ #include <QtConcurrent/qtconcurrentrunbase.h> #include <type_traits> +#include <qpromise.h> + QT_BEGIN_NAMESPACE #ifndef Q_QDOC namespace QtConcurrent { +template<typename...> +struct NonMemberFunctionResolver; + +template <class Function, class PromiseType, class... Args> +struct NonMemberFunctionResolver<Function, PromiseType, Args...> +{ + using Type = std::tuple<std::decay_t<Function>, QPromise<PromiseType> &, std::decay_t<Args>...>; + static_assert(std::is_invocable_v<std::decay_t<Function>, QPromise<PromiseType> &, std::decay_t<Args>...>, + "It's not possible to invoke the function with passed arguments."); + static_assert(std::is_void_v<std::invoke_result_t<std::decay_t<Function>, QPromise<PromiseType> &, std::decay_t<Args>...>>, + "The function must return void type."); + + static constexpr decltype (auto) invokePointer() + { + return &std::invoke<std::decay_t<Function>, QPromise<PromiseType> &, std::decay_t<Args>...>; + } + static Type initData(Function &&f, QPromise<PromiseType> &promise, Args &&...args) + { + return Type { std::forward<Function>(f), std::ref(promise), std::forward<Args>(args)... }; + } +}; + +template<typename...> +struct MemberFunctionResolver; + +template <typename Function, typename PromiseType, typename Arg, typename ... Args> +struct MemberFunctionResolver<Function, PromiseType, Arg, Args...> +{ + using Type = std::tuple<std::decay_t<Function>, std::decay_t<Arg>, QPromise<PromiseType> &, std::decay_t<Args>...>; + static_assert(std::is_invocable_v<std::decay_t<Function>, std::decay_t<Arg>, QPromise<PromiseType> &, std::decay_t<Args>...>, + "It's not possible to invoke the function with passed arguments."); + static_assert(std::is_void_v<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Arg>, QPromise<PromiseType> &, std::decay_t<Args>...>>, + "The function must return void type."); + + static constexpr decltype (auto) invokePointer() + { + return &std::invoke<std::decay_t<Function>, std::decay_t<Arg>, QPromise<PromiseType> &, std::decay_t<Args>...>; + } + static Type initData(Function &&f, QPromise<PromiseType> &promise, Arg &&fa, Args &&...args) + { + return Type { std::forward<Function>(f), std::forward<Arg>(fa), std::ref(promise), std::forward<Args>(args)... }; + } +}; + +template <class IsMember, class Function, class PromiseType, class... Args> +struct FunctionResolverHelper; + +template <class Function, class PromiseType, class... Args> +struct FunctionResolverHelper<std::false_type, Function, PromiseType, Args...> : public NonMemberFunctionResolver<Function, PromiseType, Args...> +{ +}; + +template <class Function, class PromiseType, class... Args> +struct FunctionResolverHelper<std::true_type, Function, PromiseType, Args...> : public MemberFunctionResolver<Function, PromiseType, Args...> +{ +}; + +template <class Function, class PromiseType, class... Args> +struct FunctionResolver : public FunctionResolverHelper<typename std::is_member_function_pointer<std::decay_t<Function>>::type, Function, PromiseType, Args...> +{ +}; + template <class Function, class ...Args> struct InvokeResult { @@ -94,6 +158,31 @@ private: DecayedTuple<Function, Args...> data; }; +template <class Function, class PromiseType, class ...Args> +struct StoredFunctionCallWithPromise : public RunFunctionTaskBase<PromiseType> +{ + using Resolver = FunctionResolver<Function, PromiseType, Args...>; + using DataType = typename Resolver::Type; + StoredFunctionCallWithPromise(Function &&f, Args &&...args) + : prom(this->promise), + data(std::move(Resolver::initData(std::forward<Function>(f), std::ref(prom), std::forward<Args>(args)...))) + {} + + StoredFunctionCallWithPromise(DataType &&_data) + : data(std::move(_data)) + {} + +protected: + void runFunctor() override + { + std::apply(Resolver::invokePointer(), std::move(data)); + } + +private: + QPromise<PromiseType> prom; + DataType data; +}; + } //namespace QtConcurrent #endif // Q_QDOC diff --git a/src/corelib/thread/qpromise.h b/src/corelib/thread/qpromise.h index 32aaa202f5d..59c31de212f 100644 --- a/src/corelib/thread/qpromise.h +++ b/src/corelib/thread/qpromise.h @@ -63,6 +63,7 @@ public: { other.d = QFutureInterface<T>(); } + QPromise(QFutureInterface<T> &other) : d(other) {} QPromise& operator=(QPromise<T> &&other) { QPromise<T> tmp(std::move(other)); diff --git a/src/corelib/thread/qpromise.qdoc b/src/corelib/thread/qpromise.qdoc index b404732a9e6..9c7925da953 100644 --- a/src/corelib/thread/qpromise.qdoc +++ b/src/corelib/thread/qpromise.qdoc @@ -78,6 +78,16 @@ \sa operator=() */ +/*! \fn template <typename T> QPromise<T>::QPromise(QFutureInterface<T> &other) + + \internal + Constructs a QPromise with a passed QFutureInterface \a other. + Used internally for QtConcurrent::runWithPromise. + + \sa operator=() +*/ + + /*! \fn template <typename T> QPromise<T> &QPromise<T>::operator=(QPromise<T> &&other) Move assigns \a other to this promise and returns a reference to this diff --git a/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp b/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp index 8f50329ec5e..e4b728f9509 100644 --- a/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp +++ b/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp @@ -39,6 +39,7 @@ private slots: void runLightFunction(); void runHeavyFunction(); void returnValue(); + void reportValueWithPromise(); void functionObject(); void memberFunctions(); void implicitConvertibleTypes(); @@ -51,6 +52,10 @@ private slots: void functor(); void lambda(); void callableObjectWithState(); + void withPromise(); + void withPromiseInThreadPool(); + void moveOnlyType(); + void crefFunction(); }; void light() @@ -59,6 +64,30 @@ void light() qDebug("done function"); } +void lightOverloaded() +{ + qDebug("in function"); + qDebug("done function"); +} + +void lightOverloaded(int) +{ + qDebug("in function with arg"); + qDebug("done function"); +} + +void lightOverloaded(QPromise<int> &) +{ + qDebug("in function with promise"); + qDebug("done function"); +} + +void lightOverloaded(QPromise<double> &, int) +{ + qDebug("in function with promise and with arg"); + qDebug("done function"); +} + void heavy() { qDebug("in function"); @@ -68,7 +97,6 @@ void heavy() qDebug("done function"); } - void tst_QtConcurrentRun::runLightFunction() { qDebug("starting function"); @@ -76,6 +104,34 @@ void tst_QtConcurrentRun::runLightFunction() qDebug("waiting"); future.waitForFinished(); qDebug("done"); + + void (*f1)() = lightOverloaded; + qDebug("starting function"); + QFuture<void> future1 = run(f1); + qDebug("waiting"); + future1.waitForFinished(); + qDebug("done"); + + void (*f2)(int) = lightOverloaded; + qDebug("starting function with arg"); + QFuture<void> future2 = run(f2, 2); + qDebug("waiting"); + future2.waitForFinished(); + qDebug("done"); + + void (*f3)(QPromise<int> &) = lightOverloaded; + qDebug("starting function with promise"); + QFuture<void> future3 = runWithPromise(f3); + qDebug("waiting"); + future3.waitForFinished(); + qDebug("done"); + + void (*f4)(QPromise<double> &, int v) = lightOverloaded; + qDebug("starting function with promise and with arg"); + QFuture<void> future4 = runWithPromise(f4, 2); + qDebug("waiting"); + future4.waitForFinished(); + qDebug("done"); } void tst_QtConcurrentRun::runHeavyFunction() @@ -147,6 +203,11 @@ void tst_QtConcurrentRun::returnValue() QCOMPARE(f.result(), 10); f = run(&pool, returnInt0); QCOMPARE(f.result(), 10); + f = run(returnInt1, 4); + QCOMPARE(f.result(), 4); + f = run(&pool, returnInt1, 4); + QCOMPARE(f.result(), 4); + A a; f = run(&A::member0, &a); @@ -189,6 +250,7 @@ void tst_QtConcurrentRun::returnValue() f = run(&pool, std::ref(a), 20); QCOMPARE(f.result(), 20); + const AConst aConst = AConst(); f = run(&AConst::member0, &aConst); QCOMPARE(f.result(), 10); @@ -230,6 +292,7 @@ void tst_QtConcurrentRun::returnValue() f = run(&pool, std::ref(aConst), 20); QCOMPARE(f.result(), 20); + ANoExcept aNoExcept; f = run(&ANoExcept::member0, &aNoExcept); QCOMPARE(f.result(), 10); @@ -271,6 +334,7 @@ void tst_QtConcurrentRun::returnValue() f = run(&pool, std::ref(aNoExcept), 20); QCOMPARE(f.result(), 20); + const AConstNoExcept aConstNoExcept = AConstNoExcept(); f = run(&AConstNoExcept::member0, &aConstNoExcept); QCOMPARE(f.result(), 10); @@ -313,6 +377,195 @@ void tst_QtConcurrentRun::returnValue() QCOMPARE(f.result(), 20); } +void reportInt0(QPromise<int> &promise) +{ + promise.addResult(0); +} + +void reportIntPlusOne(QPromise<int> &promise, int i) +{ + promise.addResult(i + 1); +} + +class AWithPromise +{ +public: + void member0(QPromise<int> &promise) { promise.addResult(10); } + void member1(QPromise<int> &promise, int in) { promise.addResult(in); } + + void operator()(QPromise<int> &promise) { promise.addResult(10); } +}; + +class AConstWithPromise +{ +public: + void member0(QPromise<int> &promise) const { promise.addResult(10); } + void member1(QPromise<int> &promise, int in) const { promise.addResult(in); } + + void operator()(QPromise<int> &promise) const { promise.addResult(10); } +}; + +class ANoExceptWithPromise +{ +public: + void member0(QPromise<int> &promise) noexcept { promise.addResult(10); } + void member1(QPromise<int> &promise, int in) noexcept { promise.addResult(in); } + + void operator()(QPromise<int> &promise) noexcept { promise.addResult(10); } +}; + +class AConstNoExceptWithPromise +{ +public: + void member0(QPromise<int> &promise) const noexcept { promise.addResult(10); } + void member1(QPromise<int> &promise, int in) const noexcept { promise.addResult(in); } + + void operator()(QPromise<int> &promise) const noexcept { promise.addResult(10); } +}; + +void tst_QtConcurrentRun::reportValueWithPromise() +{ + QThreadPool pool; + QFuture<int> f; + + f = runWithPromise(reportInt0); + QCOMPARE(f.result(), 0); + f = runWithPromise(&pool, reportInt0); + QCOMPARE(f.result(), 0); + f = runWithPromise(reportIntPlusOne, 5); + QCOMPARE(f.result(), 6); + f = runWithPromise(&pool, reportIntPlusOne, 5); + QCOMPARE(f.result(), 6); + + + AWithPromise a; + f = runWithPromise(&AWithPromise::member0, &a); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, &AWithPromise::member0, &a); + QCOMPARE(f.result(), 10); + + f = runWithPromise(&AWithPromise::member1, &a, 20); + QCOMPARE(f.result(), 20); + f = runWithPromise(&pool, &AWithPromise::member1, &a, 20); + QCOMPARE(f.result(), 20); + + f = runWithPromise(&AWithPromise::member0, a); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, &AWithPromise::member0, a); + QCOMPARE(f.result(), 10); + + f = runWithPromise(&AWithPromise::member1, a, 20); + QCOMPARE(f.result(), 20); + f = runWithPromise(&pool, &AWithPromise::member1, a, 20); + QCOMPARE(f.result(), 20); + + f = runWithPromise(a); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, a); + QCOMPARE(f.result(), 10); + + f = runWithPromise(std::ref(a)); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, std::ref(a)); + QCOMPARE(f.result(), 10); + + + const AConstWithPromise aConst = AConstWithPromise(); + f = runWithPromise(&AConstWithPromise::member0, &aConst); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, &AConstWithPromise::member0, &aConst); + QCOMPARE(f.result(), 10); + + f = runWithPromise(&AConstWithPromise::member1, &aConst, 20); + QCOMPARE(f.result(), 20); + f = runWithPromise(&pool, &AConstWithPromise::member1, &aConst, 20); + QCOMPARE(f.result(), 20); + + f = runWithPromise(&AConstWithPromise::member0, aConst); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, &AConstWithPromise::member0, aConst); + QCOMPARE(f.result(), 10); + + f = runWithPromise(&AConstWithPromise::member1, aConst, 20); + QCOMPARE(f.result(), 20); + f = runWithPromise(&pool, &AConstWithPromise::member1, aConst, 20); + QCOMPARE(f.result(), 20); + + f = runWithPromise(aConst); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, aConst); + QCOMPARE(f.result(), 10); + + f = runWithPromise(std::ref(a)); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, std::ref(a)); + QCOMPARE(f.result(), 10); + + + ANoExceptWithPromise aNoExcept; + f = runWithPromise(&ANoExceptWithPromise::member0, &aNoExcept); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, &ANoExceptWithPromise::member0, &aNoExcept); + QCOMPARE(f.result(), 10); + + f = runWithPromise(&ANoExceptWithPromise::member1, &aNoExcept, 20); + QCOMPARE(f.result(), 20); + f = runWithPromise(&pool, &ANoExceptWithPromise::member1, &aNoExcept, 20); + QCOMPARE(f.result(), 20); + + f = runWithPromise(&ANoExceptWithPromise::member0, aNoExcept); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, &ANoExceptWithPromise::member0, aNoExcept); + QCOMPARE(f.result(), 10); + + f = runWithPromise(&ANoExceptWithPromise::member1, aNoExcept, 20); + QCOMPARE(f.result(), 20); + f = runWithPromise(&pool, &ANoExceptWithPromise::member1, aNoExcept, 20); + QCOMPARE(f.result(), 20); + + f = runWithPromise(aNoExcept); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, aNoExcept); + QCOMPARE(f.result(), 10); + + f = runWithPromise(std::ref(aNoExcept)); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, std::ref(aNoExcept)); + QCOMPARE(f.result(), 10); + + + const AConstNoExceptWithPromise aConstNoExcept = AConstNoExceptWithPromise(); + f = runWithPromise(&AConstNoExceptWithPromise::member0, &aConstNoExcept); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, &AConstNoExceptWithPromise::member0, &aConstNoExcept); + QCOMPARE(f.result(), 10); + + f = runWithPromise(&AConstNoExceptWithPromise::member1, &aConstNoExcept, 20); + QCOMPARE(f.result(), 20); + f = runWithPromise(&pool, &AConstNoExceptWithPromise::member1, &aConstNoExcept, 20); + QCOMPARE(f.result(), 20); + + f = runWithPromise(&AConstNoExceptWithPromise::member0, aConstNoExcept); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, &AConstNoExceptWithPromise::member0, aConstNoExcept); + QCOMPARE(f.result(), 10); + + f = runWithPromise(&AConstNoExceptWithPromise::member1, aConstNoExcept, 20); + QCOMPARE(f.result(), 20); + f = runWithPromise(&pool, &AConstNoExceptWithPromise::member1, aConstNoExcept, 20); + QCOMPARE(f.result(), 20); + + f = runWithPromise(aConstNoExcept); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, aConstNoExcept); + QCOMPARE(f.result(), 10); + + f = runWithPromise(std::ref(aConstNoExcept)); + QCOMPARE(f.result(), 10); + f = runWithPromise(&pool, std::ref(aConstNoExcept)); + QCOMPARE(f.result(), 10); +} + struct TestClass { void foo() { } @@ -651,6 +904,17 @@ struct Functor { void operator()(int, int, int, int, int, int) { } }; +struct FunctorWithPromise { + void operator()(QPromise<int> &, double) { } +}; + +struct OverloadedFunctorWithPromise { + void operator()(QPromise<int> &) { } + void operator()(QPromise<double> &) { } + void operator()(QPromise<int> &, int) { } + void operator()(QPromise<double> &, int) { } +}; + // This tests functor without result_type; decltype need to be supported by the compiler. void tst_QtConcurrentRun::functor() { @@ -674,6 +938,17 @@ void tst_QtConcurrentRun::functor() QtConcurrent::run(f, 1,2,3,4).waitForFinished(); QtConcurrent::run(f, 1,2,3,4,5).waitForFinished(); } + FunctorWithPromise fWithPromise; + { + QtConcurrent::runWithPromise(fWithPromise, 1.5).waitForFinished(); + } + OverloadedFunctorWithPromise ofWithPromise; + { + QtConcurrent::runWithPromise<int>(ofWithPromise).waitForFinished(); + QtConcurrent::runWithPromise<double>(ofWithPromise).waitForFinished(); + QtConcurrent::runWithPromise<int>(ofWithPromise, 1).waitForFinished(); + QtConcurrent::runWithPromise<double>(ofWithPromise, 1).waitForFinished(); + } // and now with explicit pool: QThreadPool pool; { @@ -705,6 +980,9 @@ void tst_QtConcurrentRun::lambda() QCOMPARE(QtConcurrent::run([](int a, double b){ return a + b; }, 12, 15).result(), double(12+15)); QCOMPARE(QtConcurrent::run([](int a , int, int, int, int b){ return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); + QCOMPARE(QtConcurrent::runWithPromise([](QPromise<int> &promise){ promise.addResult(45); }).result(), 45); + QCOMPARE(QtConcurrent::runWithPromise([](QPromise<int> &promise, double input){ promise.addResult(input / 2.0); }, 15.0).result(), 7); + { QString str { "Hello World Foo" }; QFuture<QStringList> f1 = QtConcurrent::run([&](){ return str.split(' '); }); @@ -736,6 +1014,15 @@ struct CallableWithState int state = defaultState(); }; +struct CallableWithStateWithPromise +{ + void setNewState(QPromise<int> &, int newState) { state = newState; } + void operator()(QPromise<int> &promise, int newState) { state = newState; promise.addResult(newState); } + + static constexpr int defaultState() { return 42; } + int state = defaultState(); +}; + void tst_QtConcurrentRun::callableObjectWithState() { CallableWithState o; @@ -754,6 +1041,339 @@ void tst_QtConcurrentRun::callableObjectWithState() // Explicitly run on a temporary object QCOMPARE(run(CallableWithState(), 15).result(), 15); + + CallableWithStateWithPromise oWithPromise; + + // Run method setNewState explicitly + runWithPromise(&CallableWithStateWithPromise::setNewState, &oWithPromise, CallableWithStateWithPromise::defaultState() + 1).waitForFinished(); + QCOMPARE(oWithPromise.state, CallableWithStateWithPromise::defaultState() + 1); + + // Run operator()(int) explicitly + runWithPromise(std::ref(oWithPromise), CallableWithStateWithPromise::defaultState() + 2).waitForFinished(); + QCOMPARE(oWithPromise.state, CallableWithStateWithPromise::defaultState() + 2); + + // Run on a copy of object (original object remains unchanged) + runWithPromise(oWithPromise, CallableWithStateWithPromise::defaultState() + 3).waitForFinished(); + QCOMPARE(oWithPromise.state, CallableWithStateWithPromise::defaultState() + 2); + + // Explicitly run on a temporary object + QCOMPARE(runWithPromise(CallableWithStateWithPromise(), 15).result(), 15); +} + +void report3(QPromise<int> &promise) +{ + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); +} + +void reportN(QPromise<double> &promise, int n) +{ + for (int i = 0; i < n; ++i) + promise.addResult(0); +} + +void reportString1(QPromise<QString> &promise, const QString &s) +{ + promise.addResult(s); +} + +void reportString2(QPromise<QString> &promise, QString s) +{ + promise.addResult(s); +} + +class Callable { +public: + void operator()(QPromise<double> &promise, int n) const + { + for (int i = 0; i < n; ++i) + promise.addResult(0); + } +}; + +class MyObject { +public: + static void staticMember0(QPromise<double> &promise) + { + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); + } + + static void staticMember1(QPromise<double> &promise, int n) + { + for (int i = 0; i < n; ++i) + promise.addResult(0); + } + + void member0(QPromise<double> &promise) const + { + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); + } + + void member1(QPromise<double> &promise, int n) const + { + for (int i = 0; i < n; ++i) + promise.addResult(0); + } + + void memberString1(QPromise<QString> &promise, const QString &s) const + { + promise.addResult(s); + } + + void memberString2(QPromise<QString> &promise, QString s) const + { + promise.addResult(s); + } + + void nonConstMember(QPromise<double> &promise) + { + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); + } +}; + +void tst_QtConcurrentRun::withPromise() +{ + // free function pointer + QCOMPARE(runWithPromise(&report3).results(), + QList<int>({0, 2, 1})); + QCOMPARE(runWithPromise(report3).results(), + QList<int>({0, 2, 1})); + + QCOMPARE(runWithPromise(reportN, 4).results(), + QList<double>({0, 0, 0, 0})); + QCOMPARE(runWithPromise(reportN, 2).results(), + QList<double>({0, 0})); + + QString s = QLatin1String("string"); + const QString &crs = QLatin1String("cr string"); + const QString cs = QLatin1String("c string"); + + QCOMPARE(runWithPromise(reportString1, s).results(), + QList<QString>({s})); + QCOMPARE(runWithPromise(reportString1, crs).results(), + QList<QString>({crs})); + QCOMPARE(runWithPromise(reportString1, cs).results(), + QList<QString>({cs})); + QCOMPARE(runWithPromise(reportString1, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + + QCOMPARE(runWithPromise(reportString2, s).results(), + QList<QString>({s})); + QCOMPARE(runWithPromise(reportString2, crs).results(), + QList<QString>({crs})); + QCOMPARE(runWithPromise(reportString2, cs).results(), + QList<QString>({cs})); + QCOMPARE(runWithPromise(reportString2, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + + // lambda + QCOMPARE(runWithPromise([](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }, 3).results(), + QList<double>({0, 0, 0})); + + // std::function + const std::function<void(QPromise<double> &, int)> fun = [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(runWithPromise(fun, 2).results(), + QList<double>({0, 0})); + + // operator() + QCOMPARE(runWithPromise(Callable(), 3).results(), + QList<double>({0, 0, 0})); + const Callable c{}; + QCOMPARE(runWithPromise(c, 2).results(), + QList<double>({0, 0})); + + // static member functions + QCOMPARE(runWithPromise(&MyObject::staticMember0).results(), + QList<double>({0, 2, 1})); + QCOMPARE(runWithPromise(&MyObject::staticMember1, 2).results(), + QList<double>({0, 0})); + + // member functions + const MyObject obj{}; + QCOMPARE(runWithPromise(&MyObject::member0, &obj).results(), + QList<double>({0, 2, 1})); + QCOMPARE(runWithPromise(&MyObject::member1, &obj, 4).results(), + QList<double>({0, 0, 0, 0})); + QCOMPARE(runWithPromise(&MyObject::memberString1, &obj, s).results(), + QList<QString>({s})); + QCOMPARE(runWithPromise(&MyObject::memberString1, &obj, crs).results(), + QList<QString>({crs})); + QCOMPARE(runWithPromise(&MyObject::memberString1, &obj, cs).results(), + QList<QString>({cs})); + QCOMPARE(runWithPromise(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + QCOMPARE(runWithPromise(&MyObject::memberString2, &obj, s).results(), + QList<QString>({s})); + QCOMPARE(runWithPromise(&MyObject::memberString2, &obj, crs).results(), + QList<QString>({crs})); + QCOMPARE(runWithPromise(&MyObject::memberString2, &obj, cs).results(), + QList<QString>({cs})); + QCOMPARE(runWithPromise(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + MyObject nonConstObj{}; + QCOMPARE(runWithPromise(&MyObject::nonConstMember, &nonConstObj).results(), + QList<double>({0, 2, 1})); +} + +void tst_QtConcurrentRun::withPromiseInThreadPool() +{ + QScopedPointer<QThreadPool> pool(new QThreadPool); + // free function pointer + QCOMPARE(runWithPromise(pool.data(), &report3).results(), + QList<int>({0, 2, 1})); + QCOMPARE(runWithPromise(pool.data(), report3).results(), + QList<int>({0, 2, 1})); + + QCOMPARE(runWithPromise(pool.data(), reportN, 4).results(), + QList<double>({0, 0, 0, 0})); + QCOMPARE(runWithPromise(pool.data(), reportN, 2).results(), + QList<double>({0, 0})); + + QString s = QLatin1String("string"); + const QString &crs = QLatin1String("cr string"); + const QString cs = QLatin1String("c string"); + + QCOMPARE(runWithPromise(pool.data(), reportString1, s).results(), + QList<QString>({s})); + QCOMPARE(runWithPromise(pool.data(), reportString1, crs).results(), + QList<QString>({crs})); + QCOMPARE(runWithPromise(pool.data(), reportString1, cs).results(), + QList<QString>({cs})); + QCOMPARE(runWithPromise(pool.data(), reportString1, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + + QCOMPARE(runWithPromise(pool.data(), reportString2, s).results(), + QList<QString>({s})); + QCOMPARE(runWithPromise(pool.data(), reportString2, crs).results(), + QList<QString>({crs})); + QCOMPARE(runWithPromise(pool.data(), reportString2, cs).results(), + QList<QString>({cs})); + QCOMPARE(runWithPromise(pool.data(), reportString2, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + + // lambda + QCOMPARE(runWithPromise(pool.data(), [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }, 3).results(), + QList<double>({0, 0, 0})); + + // std::function + const std::function<void(QPromise<double> &, int)> fun = [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(runWithPromise(pool.data(), fun, 2).results(), + QList<double>({0, 0})); + + // operator() + QCOMPARE(runWithPromise(pool.data(), Callable(), 3).results(), + QList<double>({0, 0, 0})); + const Callable c{}; + QCOMPARE(runWithPromise(pool.data(), c, 2).results(), + QList<double>({0, 0})); + + // static member functions + QCOMPARE(runWithPromise(pool.data(), &MyObject::staticMember0).results(), + QList<double>({0, 2, 1})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::staticMember1, 2).results(), + QList<double>({0, 0})); + + // member functions + const MyObject obj{}; + QCOMPARE(runWithPromise(pool.data(), &MyObject::member0, &obj).results(), + QList<double>({0, 2, 1})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::member1, &obj, 4).results(), + QList<double>({0, 0, 0, 0})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString1, &obj, s).results(), + QList<QString>({s})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString1, &obj, crs).results(), + QList<QString>({crs})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString1, &obj, cs).results(), + QList<QString>({cs})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString2, &obj, s).results(), + QList<QString>({s})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString2, &obj, crs).results(), + QList<QString>({crs})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString2, &obj, cs).results(), + QList<QString>({cs})); + QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); +} + +class MoveOnlyType +{ +public: + MoveOnlyType() = default; + MoveOnlyType(const MoveOnlyType &) = delete; + MoveOnlyType(MoveOnlyType &&) = default; + MoveOnlyType &operator=(const MoveOnlyType &) = delete; + MoveOnlyType &operator=(MoveOnlyType &&) = default; +}; + +class MoveOnlyCallable : public MoveOnlyType +{ +public: + void operator()(QPromise<int> &promise, const MoveOnlyType &) + { + promise.addResult(1); + } +}; + +void tst_QtConcurrentRun::moveOnlyType() +{ + QCOMPARE(runWithPromise(MoveOnlyCallable(), MoveOnlyType()).results(), + QList<int>({1})); +} + +void tst_QtConcurrentRun::crefFunction() +{ + // free function pointer with promise + auto fun = &report3; + QCOMPARE(runWithPromise(std::cref(fun)).results(), + QList<int>({0, 2, 1})); + + // lambda with promise + auto lambda = [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(runWithPromise(std::cref(lambda), 3).results(), + QList<double>({0, 0, 0})); + + // std::function with promise + const std::function<void(QPromise<double> &, int)> funObj = [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(runWithPromise(std::cref(funObj), 2).results(), + QList<double>({0, 0})); + + // callable with promise + const Callable c{}; + QCOMPARE(runWithPromise(std::cref(c), 2).results(), + QList<double>({0, 0})); + + // member functions with promise + auto member = &MyObject::member0; + const MyObject obj{}; + QCOMPARE(runWithPromise(std::cref(member), &obj).results(), + QList<double>({0, 2, 1})); } QTEST_MAIN(tst_QtConcurrentRun) |