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 cf85dab226..494719d988 100644 --- a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp @@ -399,3 +399,17 @@ p.start(); p.addResult(42); p.finish(); //! [31] + +//! [32] +const std::vector values{1, 2, 3}; +auto f = QtFuture::makeReadyRangeFuture(values); +//! [32] + +//! [33] +auto f = QtFuture::makeReadyRangeFuture({1, 2, 3}); +//! [33] + +//! [34] +const int count = f.resultCount(); // count == 3 +const auto results = f.results(); // results == { 1, 2, 3 } +//! [34] diff --git a/src/corelib/platform/android/qandroidextras.cpp b/src/corelib/platform/android/qandroidextras.cpp index 89b4bed283..b7c07a8f03 100644 --- a/src/corelib/platform/android/qandroidextras.cpp +++ b/src/corelib/platform/android/qandroidextras.cpp @@ -1091,7 +1091,7 @@ requestPermissionsInternal(const QStringList &permissions) // ### can we kick off all checkPermission()s, and whenAll() collect results? for (const QString &permission : permissions) result.push_back(QtAndroidPrivate::checkPermission(permission).result()); - return QtFuture::makeReadyFuture(std::as_const(result)); // as_const d/t QTBUG-109677 + return QtFuture::makeReadyRangeFuture(result); } if (!QtAndroidPrivate::acquireAndroidDeadlockProtector()) diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc index 50ec206afd..7ba4debeea 100644 --- a/src/corelib/thread/qfuture.qdoc +++ b/src/corelib/thread/qfuture.qdoc @@ -118,14 +118,15 @@ combine several futures and track when the last or first of them completes. A ready QFuture object with a value or a QFuture object holding exception can - be created using convenience functions QtFuture::makeReadyFuture() and - QtFuture::makeExceptionalFuture(). + be created using convenience functions QtFuture::makeReadyFuture(), + QtFuture::makeReadyRangeFuture(), and QtFuture::makeExceptionalFuture(). \note To start a computation and store results in a QFuture, use QPromise or one of the APIs in the \l {Qt Concurrent} framework. \sa QPromise, QtFuture::connect(), QtFuture::makeReadyFuture(), - QtFuture::makeExceptionalFuture(), QFutureWatcher, {Qt Concurrent} + QtFuture::makeReadyRangeFuture(), QtFuture::makeExceptionalFuture(), + QFutureWatcher, {Qt Concurrent} */ /*! \fn template QFuture::QFuture() @@ -1068,6 +1069,40 @@ \sa QFuture, QException, QtFuture::makeReadyFuture() */ +/*! \fn template> static QFuture> QtFuture::makeReadyRangeFuture(Container &&container) + + \since 6.6 + \overload + + Takes an input container \a container and returns a QFuture with multiple + results of type \c ContainedType initialized from the values of the + \a container. + + \note This overload only participates in overload resolution if the + \c Container has input iterators. + + \snippet code/src_corelib_thread_qfuture.cpp 32 + \dots + \snippet code/src_corelib_thread_qfuture.cpp 34 + + \sa QFuture, QtFuture::makeReadyFuture(), QtFuture::makeExceptionalFuture() +*/ + +/*! \fn template static QFuture QtFuture::makeReadyRangeFuture(std::initializer_list values) + + \since 6.6 + \overload + + Returns a QFuture with multiple results of type \c ValueType initialized + from the input initializer list \a values. + + \snippet code/src_corelib_thread_qfuture.cpp 33 + \dots + \snippet code/src_corelib_thread_qfuture.cpp 34 + + \sa QFuture, QtFuture::makeReadyFuture(), QtFuture::makeExceptionalFuture() +*/ + /*! \fn template template QFuture::ResultType> QFuture::then(Function &&function) \since 6.0 diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h index 58169bd05b..5ed7a4d27f 100644 --- a/src/corelib/thread/qfuture_impl.h +++ b/src/corelib/thread/qfuture_impl.h @@ -274,6 +274,12 @@ using IsRandomAccessible = std::begin(std::declval()))>>::iterator_category, std::random_access_iterator_tag>; +template +using HasInputIterator = + std::is_convertible()))>>::iterator_category, + std::input_iterator_tag>; + template using IsForwardIterable = std::is_convertible::iterator_category, @@ -886,6 +892,16 @@ struct UnwrapHandler } }; +template +QFuture makeReadyRangeFutureImpl(const QList &values) +{ + QFutureInterface promise; + promise.reportStarted(); + promise.reportResults(values); + promise.reportFinished(); + return promise.future(); +} + } // namespace QtPrivate namespace QtFuture { @@ -951,6 +967,35 @@ static QFuture> connect(Sender *sender, Signal signal) return promise.future(); } +template +using if_container_with_input_iterators = + std::enable_if_t::value, bool>; + +template +using ContainedType = + typename std::iterator_traits()))>::value_type; + +template = true> +static QFuture> makeReadyRangeFuture(Container &&container) +{ + // handle QList separately, because reportResults() takes a QList + // as an input + using ValueType = ContainedType; + if constexpr (std::is_convertible_v, QList>) { + return QtPrivate::makeReadyRangeFutureImpl(container); + } else { + return QtPrivate::makeReadyRangeFutureImpl(QList{std::cbegin(container), + std::cend(container)}); + } +} + +template +static QFuture makeReadyRangeFuture(std::initializer_list values) +{ + return QtPrivate::makeReadyRangeFutureImpl(QList{values}); +} + template> static QFuture> makeReadyFuture(T &&value) { @@ -979,12 +1024,7 @@ static QFuture makeReadyFuture() template static QFuture makeReadyFuture(const QList &values) { - QFutureInterface promise; - promise.reportStarted(); - promise.reportResults(values); - promise.reportFinished(); - - return promise.future(); + return makeReadyRangeFuture(values); } #ifndef QT_NO_EXCEPTIONS diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index 09cb0836d8..f457ba4668 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -22,8 +22,11 @@ #include #include +#include +#include #include #include +#include // COM interface macro. #if defined(Q_OS_WIN) && defined(interface) @@ -4137,6 +4140,97 @@ void tst_QFuture::createReadyFutures() QVERIFY(caught); } #endif + + // testing makeReadyRangeFuture with various containers + { + const QList expectedResult{1, 2, 3}; + + const QList list{1, 2, 3}; + auto f = QtFuture::makeReadyRangeFuture(list); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + QVarLengthArray varArray{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(varArray); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + std::vector vec{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(std::move(vec)); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + f = QtFuture::makeReadyRangeFuture(std::array{1, 2, 3}); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + f = QtFuture::makeReadyRangeFuture(std::list{1, 2, 3}); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + std::forward_list fwdlist{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(fwdlist); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + const QSet qset{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(qset); + QCOMPARE_EQ(f.resultCount(), 3); + auto result = f.results(); + std::sort(result.begin(), result.end()); + QCOMPARE_EQ(result, expectedResult); + + const QMap qmap{ + {"one", 1}, + {"two", 2}, + {"three", 3} + }; + f = QtFuture::makeReadyRangeFuture(qmap); + QCOMPARE_EQ(f.resultCount(), 3); + result = f.results(); + std::sort(result.begin(), result.end()); + QCOMPARE_EQ(result, expectedResult); + + std::set stdset{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(stdset); + QCOMPARE_EQ(f.resultCount(), 3); + result = f.results(); + std::sort(result.begin(), result.end()); + QCOMPARE_EQ(result, expectedResult); + + // testing ValueType[N] overload + const int c_array[] = {1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(c_array); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + f = QtFuture::makeReadyRangeFuture({1, 2, 3}); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + } + // testing makeReadyRangeFuture with a more complex underlying type + { + QObject obj1; + QObject obj2; + QObject obj3; + + const QList expectedResult{&obj1, &obj2, &obj3}; + + const QList list{&obj1, &obj2, &obj3}; + auto f = QtFuture::makeReadyRangeFuture(list); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + std::list stdlist{&obj1, &obj2, &obj3}; + f = QtFuture::makeReadyRangeFuture(std::move(stdlist)); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + QObject* const c_array[] = {&obj1, &obj2, &obj3}; + f = QtFuture::makeReadyRangeFuture(c_array); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + } } void tst_QFuture::getFutureInterface()