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>
This commit is contained in:
parent
10242a8250
commit
7f38f9f394
@ -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]
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user