From 1d946c166cc7a4a2556e8bfaab7dc66695b555e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Mon, 30 Mar 2015 15:47:45 +0200 Subject: Async: allow consumer continuation without arguments It is now possible to chain a job that takes no arguments after a job that returns void. Unfortunatelly it is not yet possible to disregard return value of a previous job. --- async/autotests/asynctest.cpp | 95 +++++++++++++++++++++++++++++++++++++++++-- async/src/async.h | 37 +++++++++++++++-- 2 files changed, 124 insertions(+), 8 deletions(-) (limited to 'async') diff --git a/async/autotests/asynctest.cpp b/async/autotests/asynctest.cpp index 7437608..1074ff0 100644 --- a/async/autotests/asynctest.cpp +++ b/async/autotests/asynctest.cpp @@ -48,18 +48,20 @@ private Q_SLOTS: void testAsyncThen(); void testSyncThen(); void testJoinedThen(); + void testVoidThen(); void testAsyncEach(); void testSyncEach(); void testJoinedEach(); + void testVoidEach(); void testAsyncReduce(); void testSyncReduce(); void testJoinedReduce(); + void testVoidReduce(); void testErrorHandler(); - void testChainingRunningJob(); void testChainingFinishedJob(); @@ -94,6 +96,30 @@ private: }; +template<> +class AsyncTest::AsyncSimulator { +public: + AsyncSimulator(Async::Future &future) + : mFuture(future) + { + QObject::connect(&mTimer, &QTimer::timeout, + [this]() { + mFuture.setFinished(); + }); + QObject::connect(&mTimer, &QTimer::timeout, + [this]() { + delete this; + }); + mTimer.setSingleShot(true); + mTimer.start(200); + } + +private: + Async::Future mFuture; + QTimer mTimer; +}; + + void AsyncTest::testSyncPromises() { @@ -213,7 +239,8 @@ void AsyncTest::testSyncThen() auto job = Async::start( []() -> int { return 42; - }).then( + }) + .then( [](int in) -> int { return in * 2; }); @@ -243,6 +270,32 @@ void AsyncTest::testJoinedThen() QCOMPARE(future.value(), 84); } +void AsyncTest::testVoidThen() +{ + int check = 0; + + auto job = Async::start( + [&check](Async::Future &future) { + new AsyncSimulator(future); + ++check; + }) + .then( + [&check](Async::Future &future) { + new AsyncSimulator(future); + ++check; + }) + .then( + [&check]() { + ++check; + }); + + auto future = job.exec(); + future.waitForFinished(); + + QVERIFY(future.isFinished()); + QCOMPARE(check, 3); +} + void AsyncTest::testAsyncEach() @@ -303,6 +356,25 @@ void AsyncTest::testJoinedEach() QCOMPARE(future.value(), expected); } +void AsyncTest::testVoidEach() +{ + QList check; + auto job = Async::start>( + []() -> QList { + return { 1, 2, 3, 4 }; + }).each( + [&check](const int &v) { + check << v; + }); + + auto future = job.exec(); + + const QList expected({ 1, 2, 3, 4 }); + QVERIFY(future.isFinished()); + QCOMPARE(check, expected); +} + + @@ -377,6 +449,23 @@ void AsyncTest::testJoinedReduce() QCOMPARE(future.value(), 10); } +void AsyncTest::testVoidReduce() +{ +// This must not compile (reduce with void result makes no sense) +#ifdef TEST_BUILD_FAIL + auto job = Async::start>( + []() -> QList { + return { 1, 2, 3, 4 }; + }) + .reduce>( + [](const QList &list) -> int { + return; + }); + + auto future = job.exec(); + QVERIFY(future.isFinished()); +#endif +} @@ -471,8 +560,6 @@ void AsyncTest::testChainingFinishedJob() - - void AsyncTest::benchmarkSyncThenExecutor() { auto job = Async::start( diff --git a/async/src/async.h b/async/src/async.h index 8296fbc..2be1260 100644 --- a/async/src/async.h +++ b/async/src/async.h @@ -150,7 +150,10 @@ class SyncThenExecutor : public Executor::type, public: SyncThenExecutor(SyncThenTask then, ErrorHandler errorHandler, const ExecutorBasePtr &parent); void previousFutureReady(); + private: + void run(std::false_type); // !std::is_void + void run(std::true_type); // std::is_void SyncThenTask mFunc; }; @@ -170,6 +173,8 @@ public: SyncEachExecutor(SyncEachTask each, ErrorHandler errorHandler, const ExecutorBasePtr &parent); void previousFutureReady(); private: + void run(Async::Future *future, const typename PrevOut::value_type &arg, std::false_type); // !std::is_void + void run(Async::Future *future, const typename PrevOut::value_type &arg, std::true_type); // std::is_void SyncEachTask mFunc; }; @@ -386,8 +391,8 @@ private: { static_assert(detail::isIterable::value, "The 'Each' task can only be connected to a job that returns a list or an array."); - static_assert(detail::isIterable::value, - "The result type of 'Each' task must be a list or an array."); + static_assert(std::is_void::value || detail::isIterable::value, + "The result type of 'Each' task must be void, a list or an array."); } template @@ -603,9 +608,21 @@ void SyncThenExecutor::previousFutureReady() assert(this->mPrevFuture->isFinished()); } + run(std::is_void()); + this->mResult->setFinished(); +} + +template +void SyncThenExecutor::run(std::false_type) +{ Out result = this->mFunc(this->mPrevFuture ? this->mPrevFuture->value() : In() ...); static_cast*>(this->mResult)->setValue(result); - this->mResult->setFinished(); +} + +template +void SyncThenExecutor::run(std::true_type) +{ + this->mFunc(this->mPrevFuture ? this->mPrevFuture->value() : In() ...); } template @@ -626,11 +643,23 @@ void SyncEachExecutor::previousFutureReady() } for (auto arg : this->mPrevFuture->value()) { - out->setValue(out->value() + this->mFunc(arg)); + run(out, arg, std::is_void()); } out->setFinished(); } +template +void SyncEachExecutor::run(Async::Future *out, const typename PrevOut::value_type &arg, std::false_type) +{ + out->setValue(out->value() + this->mFunc(arg)); +} + +template +void SyncEachExecutor::run(Async::Future * /* unushed */, const typename PrevOut::value_type &arg, std::true_type) +{ + this->mFunc(arg); +} + template SyncReduceExecutor::SyncReduceExecutor(SyncReduceTask reduce, ErrorHandler errorHandler, const ExecutorBasePtr &parent) : SyncThenExecutor(reduce, errorHandler, parent) -- cgit v1.2.3