From ac3a0030123a350bff490d69a4786954a9003686 Mon Sep 17 00:00:00 2001 From: Determinant Date: Sun, 4 Feb 2018 15:16:58 -0500 Subject: add `race` --- README.rst | 67 +++++++++++++++++++++++++++++++++++++++++++----------- promise.hpp | 74 ++++++++++++++++++++++++++++++++++++++++++++---------------- test.cpp | 17 ++++++++++++-- test_ref.txt | 4 +++- 4 files changed, 127 insertions(+), 35 deletions(-) diff --git a/README.rst b/README.rst index 9cdcd21..76591e6 100644 --- a/README.rst +++ b/README.rst @@ -19,7 +19,7 @@ API .. code-block:: cpp - typename promise_t(Func callback); + typename promise_t::promise_t(Func callback); Create a new promise object, the ``callback(promise_t pm)`` is invoked immediately after the object is constructed, so usually the user registers @@ -28,44 +28,85 @@ immediately after the object is constructed, so usually the user registers .. code-block:: cpp - template resolve(T result) const; + template promise_t::resolve(T result) const; Resolve the promise with value ``result``. This may trigger the other promises waiting for the current promise recursively. When a promise is triggered, the -registered ``on_fulfilled()`` function will be invoked using ``result`` as the argument. +registered ``on_fulfilled()`` function will be invoked with ``result`` as the +argument. .. code-block:: cpp - template reject(T reason) const; + template promise_t::reject(T reason) const; -Reject the promise with value ``result``. This may reject the other promises +Reject the promise with value ``reason``. This may reject the other promises waiting for the current promise recursively. When a promise is rejected, the -registered ``on_rejected()`` function will be invoked using ``reason`` as the argument. +registered ``on_rejected()`` function will be invoked with ``reason`` as the +argument. + +.. code-block:: cpp + + promise_t::resolve() const; + +Resolve the promise without empty result. This may trigger the other promises +waiting for the current promise recursively. When a promise is triggered, the +registered ``on_fulfilled()`` function should be expecting no argument, +otherwise a type mismatch is thrown. + +.. code-block:: cpp + + promise_t::reject() const; + +Reject the promise without empty reason. This may reject the other promises +waiting for the current promise recursively. When a promise is rejected, the +registered ``on_rejected()`` function should be expecting on argument, +otherwise a type mismatch is thrown. .. code-block:: cpp template - promise_t then(FuncFulfilled on_fulfilled) const; + promise_t promise_t::then(FuncFulfilled on_fulfilled) const; Create a new promise that waits for the resolution of the current promise. -``on_fulfilled`` will be called with result from the current promise when +``on_fulfilled`` will be invoked with result from the current promise when resolved. The rejection will skip the callback and pass on to the promises that follow the created promise. .. code-block:: cpp template - promise_t fail(FuncRejected on_rejected) const; + promise_t promise_t::fail(FuncRejected on_rejected) const; Create a new promise that waits for the rejection of the current promise. -``on_rejected`` will be called with reason from the current promise when +``on_rejected`` will be invoked with reason from the current promise when rejected. The resolution will skip the callback and pass on to the promises that follow the created promise. .. code-block:: cpp template - promise_t then(FuncFulfilled on_fulfilled, - FuncRejected on_rejected) const; + promise_t promise_t::then(FuncFulfilled on_fulfilled, + FuncRejected on_rejected) const; + +Create a promise with callbacks that handle both resolution and rejection of +the current promise. + +.. code-block:: cpp + + template promise_t promise::all(PList promise_list); + +Create a promise waiting for the asynchronous resolution of all promises in +``promise_list``. The result for the created promise will be typed +``values_t``, a vector of ``pm_any_t`` values, each of which being the result +corresponds to a listed promise in ``promise_list`` in order. The created +promise will be rejected with the reason from the first rejection of any listed +promises. + +.. code-block:: cpp + + template promise_t promise::race(PList promise_list); -Create a promise that handles both resolution and rejection of the current promise. +Create a promise waiting for the asynchronous resolution of any promises in +``promise_list``. The result for the created promise will be the result from +the first resolved promise, and typed ``pm_any_t``. The created promise will +be rejected with the reason from the first rejection of any listed promises. diff --git a/promise.hpp b/promise.hpp index ae77c43..97a1400 100644 --- a/promise.hpp +++ b/promise.hpp @@ -110,11 +110,22 @@ namespace promise { !std::is_same::ret_type, ReturnType>::value>; + template + using enable_if_arg = typename std::enable_if< + std::is_same::arg_type, + ArgType>::value>; + + template + using disable_if_arg = typename std::enable_if< + !std::is_same::arg_type, + ArgType>::value>; + class Promise; class promise_t: std::shared_ptr { public: friend Promise; template friend promise_t all(PList promise_list); + template friend promise_t race(PList promise_list); template promise_t(Func callback): std::shared_ptr(std::make_shared()) { @@ -159,29 +170,25 @@ namespace promise { rejected_callbacks.push_back(cb); } - template::non_empty_arg * = nullptr> - static constexpr auto cps_transform(Func f, const pm_any_t &result, const promise_t &npm) { + template::non_empty_arg * = nullptr> + static constexpr auto cps_transform( + Func f, const pm_any_t &result, const promise_t &npm) { return [&result, f, npm]() mutable { f(result)->then( - [npm] (pm_any_t result_) { - npm->resolve(result_); - }, - [npm] (pm_any_t reason_) { - npm->reject(reason_); - }); + [npm] (pm_any_t result) {npm->resolve(result);}, + [npm] (pm_any_t reason) {npm->reject(reason);}); }; } - template::empty_arg * = nullptr> - static constexpr auto cps_transform(Func f, const pm_any_t &, const promise_t &npm) { + template::empty_arg * = nullptr> + static constexpr auto cps_transform( + Func f, const pm_any_t &, const promise_t &npm) { return [f, npm]() mutable { f()->then( - [npm] (pm_any_t result_) { - npm->resolve(result_); - }, - [npm] (pm_any_t reason_) { - npm->reject(reason_); - }); + [npm] (pm_any_t result) {npm->resolve(result);}, + [npm] (pm_any_t reason) {npm->reject(reason);}); }; } @@ -391,14 +398,21 @@ namespace promise { if (!--(*size)) npm->resolve(*results); }, - [npm, size](pm_any_t reason) { - npm->reject(reason); - }); + [npm](pm_any_t reason) {npm->reject(reason);}); idx++; } }); } + template promise_t race(PList promise_list) { + return promise_t([promise_list] (promise_t npm) { + for (const auto &pm: promise_list) { + pm->then([npm](pm_any_t result) {npm->resolve(result);}, + [npm](pm_any_t reason) {npm->reject(reason);}); + } + }); + } + template inline void promise_t::resolve(T result) const { (*this)->resolve(result); } @@ -417,6 +431,7 @@ namespace promise { }; template::type * = nullptr, typename enable_if_return::type * = nullptr, typename function_traits::non_empty_arg * = nullptr> constexpr auto gen_any_callback(Func f) { @@ -428,12 +443,33 @@ namespace promise { }; } + template::type * = nullptr, + typename enable_if_return::type * = nullptr, + typename function_traits::non_empty_arg * = nullptr> + constexpr auto gen_any_callback(Func f) { + using func_t = callback_types; + return [f](pm_any_t v) mutable {f(v);}; + } + template::type * = nullptr, typename function_traits::empty_arg * = nullptr> constexpr auto gen_any_callback(Func f) { return f; } template::type * = nullptr, + typename disable_if_return::type * = nullptr, + typename function_traits::non_empty_arg * = nullptr> + constexpr auto gen_any_callback(Func f) { + using func_t = callback_types; + return [f](pm_any_t v) mutable { + return typename func_t::ret_type(f(v)); + }; + } + + template::type * = nullptr, typename disable_if_return::type * = nullptr, typename function_traits::non_empty_arg * = nullptr> constexpr auto gen_any_callback(Func f) { diff --git a/test.cpp b/test.cpp index afa0f3f..737512e 100644 --- a/test.cpp +++ b/test.cpp @@ -63,7 +63,7 @@ int main() { printf("got resolved x = %d, output 12\n", x); return 12; }).then(f).then(a1).fail(a2).then(b1).fail(b2).then(g).then(a3, b3) - .then([](int x) { + .then([](int) { puts("void return is ok"); }).then([]() { puts("void parameter is ok"); @@ -102,8 +102,21 @@ int main() { return reason; }) .then([](const promise::values_t values) { - printf("finally %d\n", any_cast(values[1])); + int x = any_cast(values[1]); + printf("promise 1, 6 resolved %d\n", x); + return x + 1; }); + + auto pm8 = promise_t([](promise_t) { + puts("promsie 8 will never be resolved"); + }); + + auto pm9 = promise::race(std::vector{pm7, pm8}) + .then([](promise::pm_any_t value) { + printf("finally, promise 9 resolved with %d\n", + any_cast(value)); + }); + puts("calling t4: resolve promise 3"); t4(); puts("calling t5: resolve promise 4"); diff --git a/test_ref.txt b/test_ref.txt index 54d6079..142ba93 100644 --- a/test_ref.txt +++ b/test_ref.txt @@ -2,6 +2,7 @@ promise 1 constructed, but won't be resolved immediately promise 3 constructed promise 4 constructed promise 5 constructed +promsie 8 will never be resolved calling t4: resolve promise 3 calling t5: resolve promise 4 calling t3: resolve promise 5 @@ -21,4 +22,5 @@ operator A got 1 void return is ok void parameter is ok void parameter will ignore the returned value -finally 100 +promise 1, 6 resolved 100 +finally, promise 9 resolved with 101 -- cgit v1.2.3-70-g09d2