Forbid implicit conversions between QFuture and other types

- Remove the casting operator of QFuture<T> to T. It calls
  QFuture::result(), which may lead to undefined behavior if the user
  has moved the results from QFuture via QFuture::takeResult() before
  trying to do the conversion.

- Disable implicit conversion of QFuture<T> to QFuture<void>, by making
  the constructor explicit. If the users really intend to do the
  conversion, they should do it explicitly.

[ChangeLog][Source-Incompatible Changes][QFuture] Implicit conversions
of QFuture<T> to T and to QFuture<void> have been disabled. Use
QFuture::result() or QFuture::takeResult() where you need to convert
QFuture<T> to T. Use the explicit QFuture<void>(const QFuture<T> &)
constructor to convert QFuture<T> to QFuture<void>.

Change-Id: I153d4137d36365b1611ac934fb3ac2eb667fdd6c
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
Sona Kurazyan 2020-10-30 11:21:01 +01:00
parent 48a13327c6
commit ff0ba7e2d7
9 changed files with 73 additions and 87 deletions

View File

@ -157,7 +157,7 @@ int main(int argc, char** argv)
{
QElapsedTimer timer;
timer.start();
WordCount total = mappedReduced(files, countWords, reduce);
WordCount total = mappedReduced(files, countWords, reduce).result();
mapReduceTime = timer.elapsed();
qDebug() << "MapReduce" << mapReduceTime;
}

View File

@ -81,7 +81,7 @@ public:
}
template<typename U, typename V = T, typename = QtPrivate::EnableForVoid<V>>
QFuture(const QFuture<U> &other) : d(other.d)
explicit QFuture(const QFuture<U> &other) : d(other.d)
{
}
@ -96,9 +96,6 @@ public:
~QFuture() { }
QFuture(const QFuture<T> &) { }
QFuture<T> & operator=(const QFuture<T> &) { }
// This is required to allow QDoc to find the declaration of operator T().
operator T() const;
#endif
void cancel() { d.cancel(); }
@ -150,10 +147,6 @@ QT_WARNING_POP
template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>>
bool isResultReadyAt(int resultIndex) const { return d.isResultReadyAt(resultIndex); }
// operator T()
template<typename U = T>
operator typename std::enable_if_t<!std::is_same_v<U, void>, U>() const { return result(); }
template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>>
QList<T> results() const { return d.results(); }

View File

@ -433,19 +433,6 @@
\sa resultAt(), resultCount(), takeResult()
*/
/*! \fn template <typename T> QFuture<T>::operator T() const
Returns the first result in the future. If the result is not immediately
available, this function will block and wait for the result to become
available. This is a convenience method for calling result() or
resultAt(0).
\note Calling this function leads to undefined behavior if isValid()
returns \c false for this QFuture.
\sa result(), resultAt(), results(), takeResult(), isValid()
*/
/*! \fn template <typename T> QList<T> QFuture<T>::results() const
Returns all results from the future. If the results are not immediately available,

View File

@ -333,12 +333,12 @@ void testFilteredReduced(const QList<SourceObject> &sourceObjectList,
ReduceObject reduceObject)
{
const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>(
sourceObjectList, filterObject, reduceObject);
sourceObjectList, filterObject, reduceObject).result();
QCOMPARE(result1, expectedResult);
const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>(
sourceObjectList.constBegin(), sourceObjectList.constEnd(),
filterObject, reduceObject);
sourceObjectList.constBegin(), sourceObjectList.constEnd(),
filterObject, reduceObject).result();
QCOMPARE(result2, expectedResult);
const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>(
@ -362,12 +362,13 @@ void testFilteredReduced(const QList<SourceObject> &sourceObjectList,
QtConcurrent::ReduceOptions options)
{
const ResultObject result1 = QtConcurrent::filteredReduced(
sourceObjectList, filterObject, reduceObject, options);
sourceObjectList, filterObject, reduceObject, options).result();
QCOMPARE(result1, expectedResult);
const ResultObject result2 = QtConcurrent::filteredReduced(
sourceObjectList.constBegin(), sourceObjectList.constEnd(), filterObject,
reduceObject, options);
const ResultObject result2 =
QtConcurrent::filteredReduced(sourceObjectList.constBegin(),
sourceObjectList.constEnd(),
filterObject, reduceObject, options).result();
QCOMPARE(result2, expectedResult);
const ResultObject result3 = QtConcurrent::blockingFilteredReduced(
@ -492,13 +493,14 @@ void testFilteredReducedThreadPool(QThreadPool *pool,
ReduceObject reduceObject)
{
const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>(
pool, sourceObjectList, filterObject, reduceObject);
pool, sourceObjectList, filterObject, reduceObject).result();
QCOMPARE(result1, expectedResult);
QCOMPARE(threadCount(), 1); // ensure the only one thread was working
const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>(
pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(),
filterObject, reduceObject);
const ResultObject result2 =
QtConcurrent::filteredReduced<ResultObject>(pool, sourceObjectList.constBegin(),
sourceObjectList.constEnd(), filterObject,
reduceObject).result();
QCOMPARE(result2, expectedResult);
QCOMPARE(threadCount(), 1); // ensure the only one thread was working
@ -640,12 +642,12 @@ void testFilteredReducedInitialValue(const QList<SourceObject> &sourceObjectList
InitialObject &&initialObject)
{
const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>(
sourceObjectList, filterObject, reduceObject, initialObject);
sourceObjectList, filterObject, reduceObject, initialObject).result();
QCOMPARE(result1, expectedResult);
const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>(
sourceObjectList.constBegin(), sourceObjectList.constEnd(),
filterObject, reduceObject, initialObject);
sourceObjectList.constBegin(), sourceObjectList.constEnd(),
filterObject, reduceObject, initialObject).result();
QCOMPARE(result2, expectedResult);
const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>(
@ -671,12 +673,13 @@ void testFilteredReducedInitialValue(const QList<SourceObject> &sourceObjectList
QtConcurrent::ReduceOptions options)
{
const ResultObject result1 = QtConcurrent::filteredReduced(
sourceObjectList, filterObject, reduceObject, initialObject, options);
sourceObjectList, filterObject, reduceObject, initialObject, options).result();
QCOMPARE(result1, expectedResult);
const ResultObject result2 = QtConcurrent::filteredReduced(
sourceObjectList.constBegin(), sourceObjectList.constEnd(),
filterObject, reduceObject, initialObject, options);
const ResultObject result2 =
QtConcurrent::filteredReduced(sourceObjectList.constBegin(),
sourceObjectList.constEnd(), filterObject, reduceObject,
initialObject, options).result();
QCOMPARE(result2, expectedResult);
const ResultObject result3 = QtConcurrent::blockingFilteredReduced(
@ -810,13 +813,14 @@ void testFilteredReducedInitialValueThreadPool(QThreadPool *pool,
InitialObject &&initialObject)
{
const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>(
pool, sourceObjectList, filterObject, reduceObject, initialObject);
pool, sourceObjectList, filterObject, reduceObject, initialObject).result();
QCOMPARE(result1, expectedResult);
QCOMPARE(threadCount(), 1); // ensure the only one thread was working
const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>(
pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(),
filterObject, reduceObject, initialObject);
const ResultObject result2 =
QtConcurrent::filteredReduced<ResultObject>(pool, sourceObjectList.constBegin(),
sourceObjectList.constEnd(), filterObject,
reduceObject, initialObject).result();
QCOMPARE(result2, expectedResult);
QCOMPARE(threadCount(), 1); // ensure the only one thread was working

View File

@ -674,11 +674,12 @@ template <typename SourceObject, typename ResultObject, typename MapObject, type
void testMappedReduced(const QList<SourceObject> &sourceObjectList, const ResultObject &expectedResult, MapObject mapObject, ReduceObject reduceObject)
{
const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>(
sourceObjectList, mapObject, reduceObject);
sourceObjectList, mapObject, reduceObject).result();
QCOMPARE(result1, expectedResult);
const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>(
sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject);
sourceObjectList.constBegin(), sourceObjectList.constEnd(),
mapObject, reduceObject).result();
QCOMPARE(result2, expectedResult);
const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>(
@ -694,11 +695,12 @@ template <typename SourceObject, typename ResultObject, typename MapObject, type
void testMappedReduced(const QList<SourceObject> &sourceObjectList, const ResultObject &expectedResult, MapObject mapObject, ReduceObject reduceObject, QtConcurrent::ReduceOptions options)
{
const ResultObject result1 = QtConcurrent::mappedReduced(
sourceObjectList, mapObject, reduceObject, options);
sourceObjectList, mapObject, reduceObject, options).result();
QCOMPARE(result1, expectedResult);
const ResultObject result2 = QtConcurrent::mappedReduced(
sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject, options);
const ResultObject result2 =
QtConcurrent::mappedReduced(sourceObjectList.constBegin(), sourceObjectList.constEnd(),
mapObject, reduceObject, options).result();
QCOMPARE(result2, expectedResult);
const ResultObject result3 = QtConcurrent::blockingMappedReduced(
@ -796,13 +798,15 @@ void testMappedReducedThreadPool(QThreadPool *pool,
MapObject mapObject,
ReduceObject reduceObject)
{
const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>(pool,
sourceObjectList, mapObject, reduceObject);
const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>(
pool, sourceObjectList, mapObject, reduceObject).result();
QCOMPARE(result1, expectedResult);
QCOMPARE(threadCount(), 1); // ensure the only one thread was working
const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>(pool,
sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject);
const ResultObject result2 =
QtConcurrent::mappedReduced<ResultObject>(pool, sourceObjectList.constBegin(),
sourceObjectList.constEnd(), mapObject,
reduceObject).result();
QCOMPARE(result2, expectedResult);
QCOMPARE(threadCount(), 1); // ensure the only one thread was working
@ -954,12 +958,14 @@ void testMappedReducedInitialValue(const QList<SourceObject> &sourceObjectList,
ReduceObject reduceObject,
InitialObject &&initialObject)
{
const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>(
sourceObjectList, mapObject, reduceObject, initialObject);
const ResultObject result1 =
QtConcurrent::mappedReduced<ResultObject>(sourceObjectList, mapObject, reduceObject,
initialObject).result();
QCOMPARE(result1, expectedResult);
const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>(
sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject, initialObject);
sourceObjectList.constBegin(), sourceObjectList.constEnd(),
mapObject, reduceObject, initialObject).result();
QCOMPARE(result2, expectedResult);
const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>(
@ -980,11 +986,12 @@ void testMappedReducedInitialValue(const QList<SourceObject> &sourceObjectList,
QtConcurrent::ReduceOptions options)
{
const ResultObject result1 = QtConcurrent::mappedReduced(
sourceObjectList, mapObject, reduceObject, initialObject, options);
sourceObjectList, mapObject, reduceObject, initialObject, options).result();
QCOMPARE(result1, expectedResult);
const ResultObject result2 = QtConcurrent::mappedReduced(
sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject, initialObject, options);
const ResultObject result2 =
QtConcurrent::mappedReduced(sourceObjectList.constBegin(), sourceObjectList.constEnd(),
mapObject, reduceObject, initialObject, options).result();
QCOMPARE(result2, expectedResult);
const ResultObject result3 = QtConcurrent::blockingMappedReduced(
@ -1090,13 +1097,14 @@ void testMappedReducedInitialValueThreadPool(QThreadPool *pool,
InitialObject &&initialObject)
{
const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>(
pool, sourceObjectList, mapObject, reduceObject, initialObject);
pool, sourceObjectList, mapObject, reduceObject, initialObject).result();
QCOMPARE(result1, expectedResult);
QCOMPARE(threadCount(), 1); // ensure the only one thread was working
const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>(
pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(),
mapObject, reduceObject, initialObject);
const ResultObject result2 =
QtConcurrent::mappedReduced<ResultObject>(pool, sourceObjectList.constBegin(),
sourceObjectList.constEnd(), mapObject,
reduceObject, initialObject).result();
QCOMPARE(result2, expectedResult);
QCOMPARE(threadCount(), 1); // ensure the only one thread was working

View File

@ -127,14 +127,14 @@ void tst_QtConcurrentRun::runLightFunction()
void (*f3)(QPromise<int> &) = lightOverloaded;
qDebug("starting function with promise");
QFuture<void> future3 = run(f3);
QFuture<int> future3 = run(f3);
qDebug("waiting");
future3.waitForFinished();
qDebug("done");
void (*f4)(QPromise<double> &, int v) = lightOverloaded;
qDebug("starting function with promise and with arg");
QFuture<void> future4 = run(f4, 2);
QFuture<double> future4 = run(f4, 2);
qDebug("waiting");
future4.waitForFinished();
qDebug("done");

View File

@ -249,7 +249,7 @@ void tst_QLockFile::waitForLock()
if (!releaseEarly) // only let the thread release the lock now
semMainThreadDone.release();
QVERIFY(ret); // waits for the thread to finish
QVERIFY(ret.result()); // waits for the thread to finish
}
void tst_QLockFile::staleLockFromCrashedProcess_data()

View File

@ -99,6 +99,7 @@ class tst_QFuture: public QObject
private slots:
void resultStore();
void future();
void futureToVoid();
void futureInterface();
void refcounting();
void cancel();
@ -109,7 +110,6 @@ private slots:
void progressText();
void resultsAfterFinished();
void resultsAsList();
void implicitConversions();
void iterators();
void iteratorsThread();
#if QT_DEPRECATED_SINCE(6, 0)
@ -609,6 +609,19 @@ void tst_QFuture::future()
QCOMPARE(intFuture2.isFinished(), true);
}
void tst_QFuture::futureToVoid()
{
QPromise<int> p;
QFuture<int> future = p.future();
p.start();
p.setProgressValue(42);
p.finish();
QFuture<void> voidFuture = QFuture<void>(future);
QCOMPARE(voidFuture.progressValue(), 42);
}
class IntResult : public QFutureInterface<int>
{
public:
@ -1067,25 +1080,6 @@ void tst_QFuture::resultsAsList()
QCOMPARE(results, QList<int>() << 1 << 2);
}
/*
Test that QFuture<T> can be implicitly converted to T
*/
void tst_QFuture::implicitConversions()
{
QFutureInterface<QString> iface;
iface.reportStarted();
QFuture<QString> f(&iface);
const QString input("FooBar 2000");
iface.reportFinished(&input);
const QString result = f;
QCOMPARE(result, input);
QCOMPARE(QString(f), input);
QCOMPARE(static_cast<QString>(f), input);
}
void tst_QFuture::iterators()
{
{

View File

@ -864,7 +864,7 @@ void tst_QFutureWatcher::suspendEvents()
void tst_QFutureWatcher::suspended()
{
QFutureWatcher<void> watcher;
QFutureWatcher<int> watcher;
QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
#if QT_DEPRECATED_SINCE(6, 0)
QT_WARNING_PUSH