diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc index 82b7657016..7603fe2230 100644 --- a/src/corelib/thread/qfuture.qdoc +++ b/src/corelib/thread/qfuture.qdoc @@ -134,7 +134,12 @@ you can attach multiple continuations to a signal, which are invoked in the same thread or a new thread. - \sa QtFuture::connect(), QFutureWatcher, {Qt Concurrent} + A ready QFuture object with a value or a QFuture object holding exception can + be created using convenience functions QtFuture::makeReadyFuture and + QtFuture::makeExceptionalFuture. + + \sa QtFuture::connect(), QtFuture::makeReadyFuture(), + QtFuture::makeExceptionalFuture(), QFutureWatcher, {Qt Concurrent} */ /*! \fn template QFuture::QFuture() @@ -944,6 +949,107 @@ \sa QFuture, QFuture::then() */ +/*! \fn template static QFuture> QtFuture::makeReadyFuture(T &&value) + + \since 6.1 + \overload + + Creates and returns a QFuture which already has a result \a value. + The returned QFuture has a type of std::decay_t, where T is not void. + + \code + auto f = QtFuture::makeReadyFuture(std::make_unique(42)); + ... + const int result = *f.takeResult(); // result == 42 + \endcode + + \sa QFuture, QtFuture::makeExceptionalFuture() +*/ + +/*! \fn QFuture QtFuture::makeReadyFuture() + + \since 6.1 + \overload + + Creates and returns a void QFuture. Such QFuture can't store any result. + One can use it to query the state of the computation. + The returned QFuture will always be in the finished state. + + \code + auto f = QtFuture::makeReadyFuture(); + ... + const bool started = f.isStarted(); // started == true + const bool running = f.isRunning(); // running == false + const bool finished = f.isFinished(); // finished == true + \endcode + + \sa QFuture, QFuture::isStarted(), QFuture::isRunning(), + QFuture::isFinished(), QtFuture::makeExceptionalFuture() +*/ + +/*! \fn template static QFuture QtFuture::makeReadyFuture(const QList &values) + + \since 6.1 + \overload + + Creates and returns a QFuture which already has multiple results set from \a values. + + \code + const QList values { 1, 2, 3 }; + auto f = QtFuture::makeReadyFuture(values); + ... + const int count = f.resultCount(); // count == 3 + const auto results = f.results(); // results == { 1, 2, 3 } + \endcode + + \sa QFuture, QtFuture::makeExceptionalFuture() +*/ + +/*! \fn template static QFuture QtFuture::makeExceptionalFuture(const QException &exception) + + \since 6.1 + + Creates and returns a QFuture which already has an exception \a exception. + + \code + QException e; + auto f = QtFuture::makeExceptionalFuture(e); + ... + try { + f.result(); // throws QException + } catch (QException &) { + // handle exception here + } + \endcode + + \sa QFuture, QException, QtFuture::makeReadyFuture() +*/ + +/*! \fn template static QFuture QtFuture::makeExceptionalFuture(std::exception_ptr exception) + + \since 6.1 + \overload + + Creates and returns a QFuture which already has an exception \a exception. + + \code + struct TestException + { + }; + ... + auto exception = std::make_exception_ptr(TestException()); + auto f = QtFuture::makeExceptionalFuture(exception); + ... + try { + f.result(); // throws TestException + } catch (TestException &) { + // handle exception here + } + \endcode + + \sa QFuture, QException, QtFuture::makeReadyFuture() +*/ + /*! \fn template template QFuture::ResultType> QFuture::then(Function &&function) \since 6.0 diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h index b8b9f39951..1a02cc9cf1 100644 --- a/src/corelib/thread/qfuture_impl.h +++ b/src/corelib/thread/qfuture_impl.h @@ -49,6 +49,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -741,6 +742,68 @@ static QFuture> connect(Sender *sender, Signal signal) return promise.future(); } +template> +static QFuture> makeReadyFuture(T &&value) +{ + QFutureInterface> promise; + promise.reportStarted(); + promise.reportResult(std::forward(value)); + promise.reportFinished(); + + return promise.future(); +} + +#if defined(Q_CLANG_QDOC) +static QFuture makeReadyFuture() +#else +template +static QFuture makeReadyFuture() +#endif +{ + QFutureInterface promise; + promise.reportStarted(); + promise.reportFinished(); + + return promise.future(); +} + +template +static QFuture makeReadyFuture(const QList &values) +{ + QFutureInterface promise; + promise.reportStarted(); + promise.reportResults(values); + promise.reportFinished(); + + return promise.future(); +} + +#ifndef QT_NO_EXCEPTIONS + +template +static QFuture makeExceptionalFuture(std::exception_ptr exception) +{ + QFutureInterface promise; + promise.reportStarted(); + promise.reportException(exception); + promise.reportFinished(); + + return promise.future(); +} + +template +static QFuture makeExceptionalFuture(const QException &exception) +{ + try { + exception.raise(); + } catch (...) { + return makeExceptionalFuture(std::current_exception()); + } + Q_UNREACHABLE(); +} + +#endif // QT_NO_EXCEPTIONS + } // namespace QtFuture QT_END_NAMESPACE diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index 0d1097ce1c..c7d552cb30 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -156,6 +156,8 @@ private slots: void rejectPendingResultOverwrite_data() { rejectResultOverwrite_data(); } void rejectPendingResultOverwrite(); + void createReadyFutures(); + private: using size_type = std::vector::size_type; @@ -3338,5 +3340,72 @@ void tst_QFuture::rejectPendingResultOverwrite() QCOMPARE(f.results(), initResults); } +void tst_QFuture::createReadyFutures() +{ + // using const T & + { + const int val = 42; + QFuture f = QtFuture::makeReadyFuture(val); + QCOMPARE(f.result(), val); + } + + // using T + { + int val = 42; + QFuture f = QtFuture::makeReadyFuture(val); + QCOMPARE(f.result(), val); + } + + // using T && + { + auto f = QtFuture::makeReadyFuture(std::make_unique(42)); + QCOMPARE(*f.takeResult(), 42); + } + + // using void + { + auto f = QtFuture::makeReadyFuture(); + QVERIFY(f.isStarted()); + QVERIFY(!f.isRunning()); + QVERIFY(f.isFinished()); + } + + // using const QList & + { + const QList values { 1, 2, 3 }; + auto f = QtFuture::makeReadyFuture(values); + QCOMPARE(f.resultCount(), 3); + QCOMPARE(f.results(), values); + } + +#ifndef QT_NO_EXCEPTIONS + // using QException + { + QException e; + auto f = QtFuture::makeExceptionalFuture(e); + bool caught = false; + try { + f.result(); + } catch (QException &) { + caught = true; + } + QVERIFY(caught); + } + + // using std::exception_ptr and QFuture + { + auto exception = std::make_exception_ptr(TestException()); + auto f = QtFuture::makeExceptionalFuture(exception); + bool caught = false; + try { + f.waitForFinished(); + } catch (TestException &) { + caught = true; + } + QVERIFY(caught); + } +#endif +} + QTEST_MAIN(tst_QFuture) #include "tst_qfuture.moc"