diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp | 34 | ||||
-rw-r--r-- | src/corelib/thread/qfuture.h | 20 | ||||
-rw-r--r-- | src/corelib/thread/qfuture.qdoc | 35 | ||||
-rw-r--r-- | src/corelib/thread/qfuture_impl.h | 53 |
4 files changed, 142 insertions, 0 deletions
diff --git a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp index ffb78dc4c8..9ece90553a 100644 --- a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp @@ -398,3 +398,37 @@ QtFuture::whenAny(intFuture, stringFuture, voidFuture).then([](const FuturesVari ... }); //! [27] + +//! [28] + +QFuture<QFuture<int>> outerFuture = ...; +QFuture<int> unwrappedFuture = outerFuture.unwrap(); + +//! [28] + +//! [29] + +auto downloadImages = [] (const QUrl &url) { + QList<QImage> images; + ... + return images; +}; + +auto processImages = [](const QList<QImage> &images) { + return QtConcurrent::mappedReduced(images, scale, reduceImages); +} + +auto show = [](const QImage &image) { ... }; + +auto future = QtConcurrent::run(downloadImages, url) + .then(processImages) + .unwrap() + .then(show); +//! [29] + +//! [30] + +QFuture<QFuture<QFuture<int>>>> outerFuture; +QFuture<int> unwrappedFuture = outerFuture.unwrap(); + +//! [30] diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h index 7aed38be16..c450221769 100644 --- a/src/corelib/thread/qfuture.h +++ b/src/corelib/thread/qfuture.h @@ -191,6 +191,14 @@ QT_WARNING_POP template<class Function, typename = std::enable_if_t<std::is_invocable_r_v<T, Function>>> QFuture<T> onCanceled(QObject *context, Function &&handler); +#if !defined(Q_CLANG_QDOC) + template<class U = T, typename = std::enable_if_t<QtPrivate::isQFutureV<U>>> + auto unwrap(); +#else + template<class U> + QFuture<U> unwrap(); +#endif + class const_iterator { public: @@ -317,6 +325,8 @@ private: template<typename ResultType> friend struct QtPrivate::WhenAnyContext; + friend struct QtPrivate::UnwrapHandler; + using QFuturePrivate = std::conditional_t<std::is_same_v<T, void>, QFutureInterfaceBase, QFutureInterface<T>>; @@ -431,6 +441,16 @@ QFuture<T> QFuture<T>::onCanceled(QObject *context, Function &&handler) return promise.future(); } +template<class T> +template<class U, typename> +auto QFuture<T>::unwrap() +{ + if constexpr (QtPrivate::isQFutureV<typename QtPrivate::Future<T>::type>) + return QtPrivate::UnwrapHandler::unwrapImpl(this).unwrap(); + else + return QtPrivate::UnwrapHandler::unwrapImpl(this); +} + inline QFuture<void> QFutureInterface<void>::future() { return QFuture<void>(this); diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc index d52db1c481..bc710bc42f 100644 --- a/src/corelib/thread/qfuture.qdoc +++ b/src/corelib/thread/qfuture.qdoc @@ -1357,6 +1357,41 @@ \sa then(), onFailed() */ +/*! \fn template<class T> template<class U> QFuture<U> QFuture<T>::unwrap() + + \since 6.4 + + Unwraps the inner future from this \c QFuture<T>, where \c T is a future + of type \c QFuture<U>, i.e. this future has type of \c QFuture<QFuture<U>>. + For example: + + \snippet code/src_corelib_thread_qfuture.cpp 28 + + \c unwrappedFuture will be fulfilled as soon as the inner future nested + inside the \c outerFuture is fulfilled, with the same result or exception + and in the same thread that reports the inner future as finished. If the + inner future is canceled, \c unwrappedFuture will also be canceled. + + This is especially useful when chaining multiple computations, and one of + them returns a \c QFuture as its result type. For example, let's say we + want to download multiple images from an URL, scale the images, and reduce + them to a single image using QtConcurrent::mappedReduced(). We could write + something like: + + \snippet code/src_corelib_thread_qfuture.cpp 29 + + Here \c QtConcurrent::mappedReduced() returns a \c QFuture<QImage>, so + \c .then(processImages) returns a \c QFuture<QFuture<QImage>>. Since + \c show() takes a \c QImage as argument, the result of \c .then(processImages) + can't be passed to it directly. We need to call \c .unwrap(), that will + get the result of the inner future when it's ready and pass it to the next + continuation. + + In case of multiple nesting, \c .unwrap() goes down to the innermost level: + + \snippet code/src_corelib_thread_qfuture.cpp 30 +*/ + /*! \fn template<typename OutputSequence, typename InputIt> QFuture<OutputSequence> QtFuture::whenAll(InputIt first, InputIt last) \since 6.3 diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h index e4b0ea6289..5a70659c1d 100644 --- a/src/corelib/thread/qfuture_impl.h +++ b/src/corelib/thread/qfuture_impl.h @@ -867,6 +867,59 @@ public: } }; +struct UnwrapHandler +{ + template<class T> + static auto unwrapImpl(T *outer) + { + Q_ASSERT(outer); + + using ResultType = typename QtPrivate::Future<std::decay_t<T>>::type; + using NestedType = typename QtPrivate::Future<ResultType>::type; + QFutureInterface<NestedType> promise(QFutureInterfaceBase::State::Pending); + + outer->then([promise](const QFuture<ResultType> &outerFuture) mutable { + // We use the .then([](QFuture<ResultType> outerFuture) {...}) version + // (where outerFuture == *outer), to propagate the exception if the + // outer future has failed. + Q_ASSERT(outerFuture.isFinished()); +#ifndef QT_NO_EXCEPTIONS + if (outerFuture.d.hasException()) { + promise.reportStarted(); + promise.reportException(outerFuture.d.exceptionStore().exception()); + promise.reportFinished(); + return; + } +#endif + + promise.reportStarted(); + ResultType nestedFuture = outerFuture.result(); + + nestedFuture.then([promise] (const QFuture<NestedType> &nested) mutable { +#ifndef QT_NO_EXCEPTIONS + if (nested.d.hasException()) { + promise.reportException(nested.d.exceptionStore().exception()); + } else +#endif + { + if constexpr (!std::is_void_v<NestedType>) + promise.reportResults(nested.results()); + } + promise.reportFinished(); + }).onCanceled([promise] () mutable { + promise.reportCanceled(); + promise.reportFinished(); + }); + }).onCanceled([promise]() mutable { + // propagate the cancellation of the outer future + promise.reportStarted(); + promise.reportCanceled(); + promise.reportFinished(); + }); + return promise.future(); + } +}; + } // namespace QtPrivate namespace QtFuture { |