diff options
author | Ivan Solovev <ivan.solovev@qt.io> | 2023-03-14 17:55:44 +0100 |
---|---|---|
committer | Ivan Solovev <ivan.solovev@qt.io> | 2023-04-05 13:38:14 +0200 |
commit | 7f38f9f394b7283de93a2367765572241dede84b (patch) | |
tree | 84dd519202d0d247d2c59db9941f955a1e67c8dc | |
parent | 10242a825099d693b390d9598491c3bcecc336b7 (diff) |
Long live QtFuture::makeReadyRangeFuture()
[ChangeLog][QtCore][QFuture] Introduce
QtFuture::makeReadyRangeFuture(). This method takes a container
which has input iterators and returns a multi-value
QFuture<ValueType>, where ValueType is the underlying type of
the input container.
This commit also replaces the usage of buggy
QtFuture::makeReadyFuture(const QList<T> &) overload with the new
method.
Task-number: QTBUG-109677
Change-Id: I019e62eac74c643d88a65b3cc0085bc7c33bc712
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
-rw-r--r-- | src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp | 14 | ||||
-rw-r--r-- | src/corelib/platform/android/qandroidextras.cpp | 2 | ||||
-rw-r--r-- | src/corelib/thread/qfuture.qdoc | 41 | ||||
-rw-r--r-- | src/corelib/thread/qfuture_impl.h | 52 | ||||
-rw-r--r-- | tests/auto/corelib/thread/qfuture/tst_qfuture.cpp | 94 |
5 files changed, 193 insertions, 10 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 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<int> 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 <typename T> QFuture<T>::QFuture() @@ -1068,6 +1069,40 @@ \sa QFuture, QException, QtFuture::makeReadyFuture() */ +/*! \fn template<typename Container, QtFuture::if_container_with_input_iterators<Container>> static QFuture<QtFuture::ContainedType<Container>> 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<typename ValueType> static QFuture<ValueType> QtFuture::makeReadyRangeFuture(std::initializer_list<ValueType> 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<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::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<Sequence>()))>>::iterator_category, std::random_access_iterator_tag>; +template<class Sequence> +using HasInputIterator = + std::is_convertible<typename std::iterator_traits<std::decay_t<decltype( + std::begin(std::declval<Sequence>()))>>::iterator_category, + std::input_iterator_tag>; + template<class Iterator> using IsForwardIterable = std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, @@ -886,6 +892,16 @@ struct UnwrapHandler } }; +template<typename ValueType> +QFuture<ValueType> makeReadyRangeFutureImpl(const QList<ValueType> &values) +{ + QFutureInterface<ValueType> promise; + promise.reportStarted(); + promise.reportResults(values); + promise.reportFinished(); + return promise.future(); +} + } // namespace QtPrivate namespace QtFuture { @@ -951,6 +967,35 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal) return promise.future(); } +template<typename Container> +using if_container_with_input_iterators = + std::enable_if_t<QtPrivate::HasInputIterator<Container>::value, bool>; + +template<typename Container> +using ContainedType = + typename std::iterator_traits<decltype( + std::cbegin(std::declval<Container&>()))>::value_type; + +template<typename Container, if_container_with_input_iterators<Container> = true> +static QFuture<ContainedType<Container>> makeReadyRangeFuture(Container &&container) +{ + // handle QList<T> separately, because reportResults() takes a QList + // as an input + using ValueType = ContainedType<Container>; + if constexpr (std::is_convertible_v<q20::remove_cvref_t<Container>, QList<ValueType>>) { + return QtPrivate::makeReadyRangeFutureImpl(container); + } else { + return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{std::cbegin(container), + std::cend(container)}); + } +} + +template<typename ValueType> +static QFuture<ValueType> makeReadyRangeFuture(std::initializer_list<ValueType> values) +{ + return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{values}); +} + template<typename T, typename = QtPrivate::EnableForNonVoid<T>> static QFuture<std::decay_t<T>> makeReadyFuture(T &&value) { @@ -979,12 +1024,7 @@ static QFuture<T> makeReadyFuture() template<typename T> static QFuture<T> makeReadyFuture(const QList<T> &values) { - QFutureInterface<T> 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 <QtConcurrent/qtconcurrentrun.h> #include <private/qfutureinterface_p.h> +#include <forward_list> +#include <list> #include <vector> #include <memory> +#include <set> // 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<int> expectedResult{1, 2, 3}; + + const QList<int> list{1, 2, 3}; + auto f = QtFuture::makeReadyRangeFuture(list); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + QVarLengthArray<int> varArray{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(varArray); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + std::vector<int> 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<int, 3>{1, 2, 3}); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + f = QtFuture::makeReadyRangeFuture(std::list<int>{1, 2, 3}); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + std::forward_list<int> fwdlist{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(fwdlist); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + const QSet<int> 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<QString, int> 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<int> 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<QObject*> expectedResult{&obj1, &obj2, &obj3}; + + const QList<QObject*> list{&obj1, &obj2, &obj3}; + auto f = QtFuture::makeReadyRangeFuture(list); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + std::list<QObject*> 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() |