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:
parent
48a13327c6
commit
ff0ba7e2d7
@ -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;
|
||||
}
|
||||
|
@ -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(); }
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
{
|
||||
{
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user