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:
Ivan Solovev 2023-03-14 17:55:44 +01:00
parent 10242a8250
commit 7f38f9f394
5 changed files with 193 additions and 10 deletions

View File

@ -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]

View File

@ -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())

View File

@ -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

View File

@ -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

View File

@ -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()