From 90f8c0f91e1de14685477a98b14a29e7b0781ec9 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 9 Dec 2018 09:40:36 +0700 Subject: [PATCH 1/3] is_promise, is_promise_r, make_resolved_promise, make_rejected_promise --- promise.hpp | 113 ++++++++++++++++++++++++++++++++++++++++++---------- tests.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 22 deletions(-) diff --git a/promise.hpp b/promise.hpp index 7370c39..d20348f 100644 --- a/promise.hpp +++ b/promise.hpp @@ -25,33 +25,43 @@ namespace promise_hpp class promise; // - // make_promise + // is_promise // - template < typename R, typename F > - promise make_promise(F&& f) { - promise result; + namespace impl + { + template < typename T > + struct is_promise_impl + : std::false_type {}; - auto resolver = std::bind([](promise& p, auto&& v){ - p.resolve(std::forward(v)); - }, result, std::placeholders::_1); - - auto rejector = std::bind([](promise& p, auto&& e){ - p.reject(std::forward(e)); - }, result, std::placeholders::_1); - - try { - invoke_hpp::invoke( - std::forward(f), - std::move(resolver), - std::move(rejector)); - } catch (...) { - result.reject(std::current_exception()); - } - - return result; + template < typename R > + struct is_promise_impl> + : std::true_type {}; } + template < typename T > + struct is_promise + : impl::is_promise_impl> {}; + + // + // is_promise_r + // + + namespace impl + { + template < typename R, typename T > + struct is_promise_r_impl + : std::false_type {}; + + template < typename R, typename PR > + struct is_promise_r_impl> + : std::is_convertible {}; + } + + template < typename R, typename T > + struct is_promise_r + : impl::is_promise_r_impl> {}; + // // promise // @@ -59,6 +69,8 @@ namespace promise_hpp template < typename T > class promise final { public: + using value_type = T; + enum class status : std::uint8_t { pending, resolved, @@ -298,6 +310,8 @@ namespace promise_hpp template <> class promise final { public: + using value_type = void; + enum class status : std::uint8_t { pending, resolved, @@ -487,4 +501,59 @@ namespace promise_hpp std::vector handlers_; }; }; + + // + // make_promise + // + + template < typename R, typename F > + promise make_promise(F&& f) { + promise result; + + auto resolver = std::bind([](promise& p, auto&& v){ + p.resolve(std::forward(v)); + }, result, std::placeholders::_1); + + auto rejector = std::bind([](promise& p, auto&& e){ + p.reject(std::forward(e)); + }, result, std::placeholders::_1); + + try { + invoke_hpp::invoke( + std::forward(f), + std::move(resolver), + std::move(rejector)); + } catch (...) { + result.reject(std::current_exception()); + } + + return result; + } + + inline promise make_resolved_promise() { + promise result; + result.resolve(); + return result; + } + + template < typename R > + promise> make_resolved_promise(R&& v) { + promise> result; + result.resolve(std::forward(v)); + return result; + } + + template < typename E > + promise make_rejected_promise(E&& e) { + promise result; + result.reject(std::forward(e)); + return result; + } + + template < typename R, typename E > + promise make_rejected_promise(E&& e) { + promise result; + result.reject(std::forward(e)); + return result; + } } diff --git a/tests.cpp b/tests.cpp index cf705e6..216a5ca 100644 --- a/tests.cpp +++ b/tests.cpp @@ -6,6 +6,9 @@ namespace pr = promise_hpp; namespace { + struct obj_t { + }; + bool check_hello_fail_exception(std::exception_ptr e) { try { std::rethrow_exception(e); @@ -17,6 +20,79 @@ namespace } } +TEST_CASE("is_promise") { + SECTION("positive") { + static_assert( + pr::is_promise>::value, + "unit test fail"); + static_assert( + pr::is_promise>::value, + "unit test fail"); + static_assert( + pr::is_promise>::value, + "unit test fail"); + + static_assert( + pr::is_promise>::value, + "unit test fail"); + static_assert( + pr::is_promise>::value, + "unit test fail"); + static_assert( + pr::is_promise>::value, + "unit test fail"); + } + SECTION("negative") { + static_assert( + !pr::is_promise&>::value, + "unit test fail"); + static_assert( + !pr::is_promise*>::value, + "unit test fail"); + static_assert( + !pr::is_promise&>::value, + "unit test fail"); + + static_assert( + !pr::is_promise::value, + "unit test fail"); + static_assert( + !pr::is_promise::value, + "unit test fail"); + static_assert( + !pr::is_promise::value, + "unit test fail"); + } +} + +TEST_CASE("is_promise_r") { + SECTION("positive") { + static_assert( + pr::is_promise_r>::value, + "unit test fail"); + static_assert( + pr::is_promise_r>::value, + "unit test fail"); + static_assert( + pr::is_promise_r>::value, + "unit test fail"); + } + SECTION("negative") { + static_assert( + !pr::is_promise_r>::value, + "unit test fail"); + static_assert( + !pr::is_promise_r>::value, + "unit test fail"); + static_assert( + !pr::is_promise_r>::value, + "unit test fail"); + static_assert( + !pr::is_promise_r::value, + "unit test fail"); + } +} + TEST_CASE("promise") { SECTION("resolved") { { @@ -191,6 +267,42 @@ TEST_CASE("promise") { REQUIRE(call_fail_with_logic_error); } } + SECTION("make_resolved_promise") { + { + bool call_check = false; + pr::make_resolved_promise() + .then([&call_check]{ + call_check = true; + }); + REQUIRE(call_check); + } + { + int check_42_int = 0; + pr::make_resolved_promise(42) + .then([&check_42_int](int value){ + check_42_int = value; + }); + REQUIRE(check_42_int == 42); + } + } + SECTION("make_rejected_promise") { + { + bool call_fail_with_logic_error = false; + pr::make_rejected_promise(std::logic_error("hello fail")) + .fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + REQUIRE(call_fail_with_logic_error); + } + { + bool call_fail_with_logic_error = 0; + pr::make_rejected_promise(std::logic_error("hello fail")) + .fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + REQUIRE(call_fail_with_logic_error); + } + } SECTION("exceptions") { { bool not_call_then_on_reject = true; From d572288cb64d41042dbf3691318556a6d7853abf Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 9 Dec 2018 11:14:20 +0700 Subject: [PATCH 2/3] remove std::bind --- promise.hpp | 130 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 94 insertions(+), 36 deletions(-) diff --git a/promise.hpp b/promise.hpp index d20348f..815aad2 100644 --- a/promise.hpp +++ b/promise.hpp @@ -201,29 +201,44 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> attach(promise& other, ResolveF&& resolve, RejectF&& reject) { - auto resolve_h = std::bind([](promise& p, const ResolveF& f, const T& v){ + auto resolve_h = [ + p = other, + f = std::forward(resolve) + ](const T& v) mutable { try { - p.resolve(invoke_hpp::invoke(f, v)); + auto r = invoke_hpp::invoke( + std::forward(f), + v); + p.resolve(std::move(r)); } catch (...) { p.reject(std::current_exception()); } - }, other, std::forward(resolve), std::placeholders::_1); + }; - auto reject_h = std::bind([](promise& p, const RejectF& f, std::exception_ptr e){ + auto reject_h = [ + p = other, + f = std::forward(reject) + ](std::exception_ptr e) mutable { try { - invoke_hpp::invoke(f, e); + invoke_hpp::invoke( + std::forward(f), + e); p.reject(e); } catch (...) { p.reject(std::current_exception()); } - }, other, std::forward(reject), std::placeholders::_1); + }; std::lock_guard guard(mutex_); if ( status_ == status::resolved ) { - resolve_h(storage_.value()); + invoke_hpp::invoke( + std::move(resolve_h), + storage_.value()); } else if ( status_ == status::rejected ) { - reject_h(exception_); + invoke_hpp::invoke( + std::move(reject_h), + exception_); } else { handlers_.emplace_back( std::move(resolve_h), @@ -234,30 +249,44 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> attach(promise& other, ResolveF&& resolve, RejectF&& reject) { - auto resolve_h = std::bind([](promise& p, const ResolveF& f, const T& v){ + auto resolve_h = [ + p = other, + f = std::forward(resolve) + ](const T& v) mutable { try { - invoke_hpp::invoke(f, v); + invoke_hpp::invoke( + std::forward(f), + v); p.resolve(); } catch (...) { p.reject(std::current_exception()); } - }, other, std::forward(resolve), std::placeholders::_1); + }; - auto reject_h = std::bind([](promise& p, const RejectF& f, std::exception_ptr e){ + auto reject_h = [ + p = other, + f = std::forward(reject) + ](std::exception_ptr e) mutable { try { - invoke_hpp::invoke(f, e); + invoke_hpp::invoke( + std::forward(f), + e); p.reject(e); } catch (...) { p.reject(std::current_exception()); } - }, other, std::forward(reject), std::placeholders::_1); + }; std::lock_guard guard(mutex_); if ( status_ == status::resolved ) { - resolve_h(storage_.value()); + invoke_hpp::invoke( + std::move(resolve_h), + storage_.value()); } else if ( status_ == status::rejected ) { - reject_h(exception_); + invoke_hpp::invoke( + std::move(reject_h), + exception_); } else { handlers_.emplace_back( std::move(resolve_h), @@ -402,29 +431,42 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> attach(promise& other, ResolveF&& resolve, RejectF&& reject) { - auto resolve_h = std::bind([](promise& p, const ResolveF& f){ + auto resolve_h = [ + p = other, + f = std::forward(resolve) + ]() mutable { try { - p.resolve(invoke_hpp::invoke(f)); + auto r = invoke_hpp::invoke( + std::forward(f)); + p.resolve(std::move(r)); } catch (...) { p.reject(std::current_exception()); } - }, other, std::forward(resolve)); + }; - auto reject_h = std::bind([](promise& p, const RejectF& f, std::exception_ptr e){ + auto reject_h = [ + p = other, + f = std::forward(reject) + ](std::exception_ptr e) mutable { try { - invoke_hpp::invoke(f, e); + invoke_hpp::invoke( + std::forward(f), + e); p.reject(e); } catch (...) { p.reject(std::current_exception()); } - }, other, std::forward(reject), std::placeholders::_1); + }; std::lock_guard guard(mutex_); if ( status_ == status::resolved ) { - resolve_h(); + invoke_hpp::invoke( + std::move(resolve_h)); } else if ( status_ == status::rejected ) { - reject_h(exception_); + invoke_hpp::invoke( + std::move(reject_h), + exception_); } else { handlers_.emplace_back( std::move(resolve_h), @@ -435,30 +477,42 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> attach(promise& other, ResolveF&& resolve, RejectF&& reject) { - auto resolve_h = std::bind([](promise& p, const ResolveF& f){ + auto resolve_h = [ + p = other, + f = std::forward(resolve) + ]() mutable { try { - invoke_hpp::invoke(f); + invoke_hpp::invoke( + std::forward(f)); p.resolve(); } catch (...) { p.reject(std::current_exception()); } - }, other, std::forward(resolve)); + }; - auto reject_h = std::bind([](promise& p, const RejectF& f, std::exception_ptr e){ + auto reject_h = [ + p = other, + f = std::forward(reject) + ](std::exception_ptr e) mutable { try { - invoke_hpp::invoke(f, e); + invoke_hpp::invoke( + std::forward(f), + e); p.reject(e); } catch (...) { p.reject(std::current_exception()); } - }, other, std::forward(reject), std::placeholders::_1); + }; std::lock_guard guard(mutex_); if ( status_ == status::resolved ) { - resolve_h(); + invoke_hpp::invoke( + std::move(resolve_h)); } else if ( status_ == status::rejected ) { - reject_h(exception_); + invoke_hpp::invoke( + std::move(reject_h), + exception_); } else { handlers_.emplace_back( std::move(resolve_h), @@ -510,13 +564,17 @@ namespace promise_hpp promise make_promise(F&& f) { promise result; - auto resolver = std::bind([](promise& p, auto&& v){ + auto resolver = [ + p = result + ](auto&& v) mutable { p.resolve(std::forward(v)); - }, result, std::placeholders::_1); + }; - auto rejector = std::bind([](promise& p, auto&& e){ + auto rejector = [ + p = result + ](auto&& e) mutable { p.reject(std::forward(e)); - }, result, std::placeholders::_1); + }; try { invoke_hpp::invoke( From 1148557b2f84b375d5d5edb3e6ec78ed882a4940 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 9 Dec 2018 22:33:31 +0700 Subject: [PATCH 3/3] promises chaining --- promise.hpp | 296 ++++++++++++++++++++++++++++++++++------------------ tests.cpp | 196 +++++++++++++++++++++++++++++++--- 2 files changed, 380 insertions(+), 112 deletions(-) diff --git a/promise.hpp b/promise.hpp index 815aad2..3eb8a4c 100644 --- a/promise.hpp +++ b/promise.hpp @@ -62,6 +62,57 @@ namespace promise_hpp struct is_promise_r : impl::is_promise_r_impl> {}; + // + // detail + // + + namespace detail + { + class noncopyable { + public: + noncopyable(const noncopyable&) = delete; + noncopyable& operator=(const noncopyable&) = delete; + protected: + noncopyable() = default; + ~noncopyable() = default; + }; + + template < typename T > + class storage final : private noncopyable { + public: + storage() = default; + + ~storage() noexcept(std::is_nothrow_destructible::value) { + if ( initialized_ ) { + ptr_()->~T(); + } + } + + template < typename U > + void set(U&& value) noexcept(std::is_nothrow_constructible::value) { + assert(!initialized_); + ::new(ptr_()) T(std::forward(value)); + initialized_ = true; + } + + const T& value() const noexcept { + assert(initialized_); + return *ptr_(); + } + private: + T* ptr_() noexcept { + return reinterpret_cast(&data_); + } + + const T* ptr_() const noexcept { + return reinterpret_cast(&data_); + } + private: + std::aligned_storage_t data_; + bool initialized_ = false; + }; + } + // // promise // @@ -82,7 +133,64 @@ namespace promise_hpp template < typename ResolveF , typename ResolveFR = invoke_hpp::invoke_result_t > - promise then(ResolveF&& on_resolve) { + std::enable_if_t< + is_promise::value && !std::is_void::value, + promise> + then(ResolveF&& on_resolve) { + promise next; + + then([ + n = next, + f = std::forward(on_resolve) + ](const T& v) mutable { + auto np = invoke_hpp::invoke( + std::forward(f), + v); + np.then([n = n](const typename ResolveFR::value_type& nv) mutable { + n.resolve(nv); + }).fail([n = n](std::exception_ptr e) mutable { + n.reject(e); + }); + }).fail([n = next](std::exception_ptr e) mutable { + n.reject(e); + }); + + return next; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + is_promise::value && std::is_void::value, + promise> + then(ResolveF&& on_resolve) { + promise next; + + then([ + n = next, + f = std::forward(on_resolve) + ](const T& v) mutable { + auto np = invoke_hpp::invoke( + std::forward(f), + v); + np.then([n = n]() mutable { + n.resolve(); + }).fail([n = n](std::exception_ptr e) mutable { + n.reject(e); + }); + }).fail([n = next](std::exception_ptr e) mutable { + n.reject(e); + }); + + return next; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + !is_promise::value, + promise> + then(ResolveF&& on_resolve) { promise next; state_->attach( next, @@ -91,18 +199,6 @@ namespace promise_hpp return next; } - template < typename ResolveF - , typename RejectF - , typename ResolveFR = invoke_hpp::invoke_result_t > - promise then(ResolveF&& on_resolve, RejectF&& on_reject) { - promise next; - state_->attach( - next, - std::forward(on_resolve), - std::forward(on_reject)); - return next; - } - template < typename RejectF > promise fail(RejectF&& on_reject) { promise next; @@ -133,50 +229,10 @@ namespace promise_hpp class state; std::shared_ptr state_; private: - class storage final { - public: - storage() = default; - - storage(const storage&) = delete; - storage& operator=(const storage&) = delete; - - ~storage() noexcept(std::is_nothrow_destructible::value) { - if ( initialized_ ) { - ptr_()->~T(); - } - } - - template < typename U > - void set(U&& value) { - assert(!initialized_); - ::new(ptr_()) T(std::forward(value)); - initialized_ = true; - } - - const T& value() const noexcept { - assert(initialized_); - return *ptr_(); - } - private: - T* ptr_() noexcept { - return reinterpret_cast(&data_); - } - - const T* ptr_() const noexcept { - return reinterpret_cast(&data_); - } - private: - std::aligned_storage_t data_; - bool initialized_ = false; - }; - - class state final { + class state final : private detail::noncopyable { public: state() = default; - state(const state&) = delete; - state& operator=(const state&) = delete; - template < typename U > void resolve(U&& value) { std::lock_guard guard(mutex_); @@ -200,32 +256,32 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach(promise& other, ResolveF&& resolve, RejectF&& reject) { + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { auto resolve_h = [ - p = other, + n = next, f = std::forward(resolve) ](const T& v) mutable { try { auto r = invoke_hpp::invoke( std::forward(f), v); - p.resolve(std::move(r)); + n.resolve(std::move(r)); } catch (...) { - p.reject(std::current_exception()); + n.reject(std::current_exception()); } }; auto reject_h = [ - p = other, + n = next, f = std::forward(reject) ](std::exception_ptr e) mutable { try { invoke_hpp::invoke( std::forward(f), e); - p.reject(e); + n.reject(e); } catch (...) { - p.reject(std::current_exception()); + n.reject(std::current_exception()); } }; @@ -248,32 +304,32 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach(promise& other, ResolveF&& resolve, RejectF&& reject) { + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { auto resolve_h = [ - p = other, + n = next, f = std::forward(resolve) ](const T& v) mutable { try { invoke_hpp::invoke( std::forward(f), v); - p.resolve(); + n.resolve(); } catch (...) { - p.reject(std::current_exception()); + n.reject(std::current_exception()); } }; auto reject_h = [ - p = other, + n = next, f = std::forward(reject) ](std::exception_ptr e) mutable { try { invoke_hpp::invoke( std::forward(f), e); - p.reject(e); + n.reject(e); } catch (...) { - p.reject(std::current_exception()); + n.reject(std::current_exception()); } }; @@ -309,7 +365,7 @@ namespace promise_hpp handlers_.clear(); } private: - storage storage_; + detail::storage storage_; status status_ = status::pending; std::exception_ptr exception_ = nullptr; @@ -352,7 +408,62 @@ namespace promise_hpp template < typename ResolveF , typename ResolveFR = invoke_hpp::invoke_result_t > - promise then(ResolveF&& on_resolve) { + std::enable_if_t< + is_promise::value && !std::is_void::value, + promise> + then(ResolveF&& on_resolve) { + promise next; + + then([ + n = next, + f = std::forward(on_resolve) + ]() mutable { + auto np = invoke_hpp::invoke( + std::forward(f)); + np.then([n = n](const typename ResolveFR::value_type& nv) mutable { + n.resolve(nv); + }).fail([n = n](std::exception_ptr e) mutable { + n.reject(e); + }); + }).fail([n = next](std::exception_ptr e) mutable { + n.reject(e); + }); + + return next; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + is_promise::value && std::is_void::value, + promise> + then(ResolveF&& on_resolve) { + promise next; + + then([ + n = next, + f = std::forward(on_resolve) + ]() mutable { + auto np = invoke_hpp::invoke( + std::forward(f)); + np.then([n = n]() mutable { + n.resolve(); + }).fail([n = n](std::exception_ptr e) mutable { + n.reject(e); + }); + }).fail([n = next](std::exception_ptr e) mutable { + n.reject(e); + }); + + return next; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + !is_promise::value, + promise> + then(ResolveF&& on_resolve) { promise next; state_->attach( next, @@ -361,18 +472,6 @@ namespace promise_hpp return next; } - template < typename ResolveF - , typename RejectF - , typename ResolveFR = invoke_hpp::invoke_result_t > - promise then(ResolveF&& on_resolve, RejectF&& on_reject) { - promise next; - state_->attach( - next, - std::forward(on_resolve), - std::forward(on_reject)); - return next; - } - template < typename RejectF > promise fail(RejectF&& on_reject) { promise next; @@ -402,13 +501,10 @@ namespace promise_hpp class state; std::shared_ptr state_; private: - class state final { + class state final : private detail::noncopyable { public: state() = default; - state(const state&) = delete; - state& operator=(const state&) = delete; - void resolve() { std::lock_guard guard(mutex_); if ( status_ != status::pending ) { @@ -430,31 +526,31 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach(promise& other, ResolveF&& resolve, RejectF&& reject) { + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { auto resolve_h = [ - p = other, + n = next, f = std::forward(resolve) ]() mutable { try { auto r = invoke_hpp::invoke( std::forward(f)); - p.resolve(std::move(r)); + n.resolve(std::move(r)); } catch (...) { - p.reject(std::current_exception()); + n.reject(std::current_exception()); } }; auto reject_h = [ - p = other, + n = next, f = std::forward(reject) ](std::exception_ptr e) mutable { try { invoke_hpp::invoke( std::forward(f), e); - p.reject(e); + n.reject(e); } catch (...) { - p.reject(std::current_exception()); + n.reject(std::current_exception()); } }; @@ -476,31 +572,31 @@ namespace promise_hpp template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach(promise& other, ResolveF&& resolve, RejectF&& reject) { + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { auto resolve_h = [ - p = other, + n = next, f = std::forward(resolve) ]() mutable { try { invoke_hpp::invoke( std::forward(f)); - p.resolve(); + n.resolve(); } catch (...) { - p.reject(std::current_exception()); + n.reject(std::current_exception()); } }; auto reject_h = [ - p = other, + n = next, f = std::forward(reject) ](std::exception_ptr e) mutable { try { invoke_hpp::invoke( std::forward(f), e); - p.reject(e); + n.reject(e); } catch (...) { - p.reject(std::current_exception()); + n.reject(std::current_exception()); } }; diff --git a/tests.cpp b/tests.cpp index 216a5ca..8ce75c1 100644 --- a/tests.cpp +++ b/tests.cpp @@ -114,16 +114,6 @@ TEST_CASE("promise") { }); REQUIRE(check_42_int == 42); } - { - int check_42_int = 0; - pr::promise() - .resolve(42) - .then([&check_42_int](int value){ - check_42_int = value; - }, [](std::exception_ptr){ - }); - REQUIRE(check_42_int == 42); - } { int check_84_int = 0; bool check_void_call = false; @@ -167,7 +157,7 @@ TEST_CASE("promise") { pr::promise() .reject(ee) .then([](int){ - }).fail([&call_fail_with_logic_error](const std::exception_ptr& e){ + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); }); REQUIRE(call_fail_with_logic_error); @@ -178,7 +168,7 @@ TEST_CASE("promise") { pr::promise() .reject(std::make_exception_ptr(ee)) .then([](int){ - }).fail([&call_fail_with_logic_error](const std::exception_ptr& e){ + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); }); REQUIRE(call_fail_with_logic_error); @@ -382,4 +372,186 @@ TEST_CASE("promise") { REQUIRE(pb_value == 21); } } + SECTION("chaining") { + { + int check_84_int = 0; + auto p1 = pr::make_resolved_promise(42); + auto p2 = pr::make_resolved_promise(84); + + p1.then([&p2](int v){ + return p2; + }).then([&check_84_int](int v2){ + check_84_int = v2; + }); + + REQUIRE(check_84_int == 84); + } + { + int check_84_int = 0; + auto p1 = pr::make_resolved_promise(); + auto p2 = pr::make_resolved_promise(84); + + p1.then([&p2](){ + return p2; + }).then([&check_84_int](int v){ + check_84_int = v; + }); + + REQUIRE(check_84_int == 84); + } + { + int check_84_int = 0; + auto p1 = pr::make_resolved_promise(42); + auto p2 = pr::make_resolved_promise(); + + p1.then([&p2](int v){ + return p2; + }).then([&check_84_int](){ + check_84_int = 84; + }); + + REQUIRE(check_84_int == 84); + } + { + int check_84_int = 0; + auto p1 = pr::make_resolved_promise(); + auto p2 = pr::make_resolved_promise(); + + p1.then([&p2](){ + return p2; + }).then([&check_84_int](){ + check_84_int = 84; + }); + + REQUIRE(check_84_int == 84); + } + } + SECTION("typed_chaining_fails") { + { + bool call_fail_with_logic_error = false; + auto p1 = pr::make_resolved_promise(42); + auto p2 = pr::make_resolved_promise(84); + + p1.then([&p2](int v){ + (void)v; + throw std::logic_error("hello fail"); + return p2; + }).then([&call_fail_with_logic_error](int v2){ + (void)v2; + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + + REQUIRE(call_fail_with_logic_error); + } + { + bool call_fail_with_logic_error = false; + auto p1 = pr::make_resolved_promise(42); + auto p2 = pr::make_resolved_promise(84); + + p1.then([&p2](int v){ + (void)v; + return p2; + }).then([&call_fail_with_logic_error](int v2){ + (void)v2; + throw std::logic_error("hello fail"); + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + + REQUIRE(call_fail_with_logic_error); + } + { + bool call_fail_with_logic_error = false; + auto p1 = pr::make_rejected_promise(std::logic_error("hello fail")); + auto p2 = pr::make_resolved_promise(84); + + p1.then([&p2](int v){ + (void)v; + return p2; + }).then([&call_fail_with_logic_error](int v2){ + (void)v2; + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + + REQUIRE(call_fail_with_logic_error); + } + { + bool call_fail_with_logic_error = false; + auto p1 = pr::make_resolved_promise(42); + auto p2 = pr::make_rejected_promise(std::logic_error("hello fail")); + + p1.then([&p2](int v){ + (void)v; + return p2; + }).then([&call_fail_with_logic_error](int v2){ + (void)v2; + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + + REQUIRE(call_fail_with_logic_error); + } + } + SECTION("void_chaining_fails") { + { + bool call_fail_with_logic_error = false; + auto p1 = pr::make_resolved_promise(); + auto p2 = pr::make_resolved_promise(); + + p1.then([&p2](){ + throw std::logic_error("hello fail"); + return p2; + }).then([&call_fail_with_logic_error](){ + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + + REQUIRE(call_fail_with_logic_error); + } + { + bool call_fail_with_logic_error = false; + auto p1 = pr::make_resolved_promise(); + auto p2 = pr::make_resolved_promise(); + + p1.then([&p2](){ + return p2; + }).then([&call_fail_with_logic_error](){ + throw std::logic_error("hello fail"); + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + + REQUIRE(call_fail_with_logic_error); + } + { + bool call_fail_with_logic_error = false; + auto p1 = pr::make_rejected_promise(std::logic_error("hello fail")); + auto p2 = pr::make_resolved_promise(); + + p1.then([&p2](){ + return p2; + }).then([&call_fail_with_logic_error](){ + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + + REQUIRE(call_fail_with_logic_error); + } + { + bool call_fail_with_logic_error = false; + auto p1 = pr::make_resolved_promise(); + auto p2 = pr::make_rejected_promise(std::logic_error("hello fail")); + + p1.then([&p2](){ + return p2; + }).then([&call_fail_with_logic_error](){ + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + + REQUIRE(call_fail_with_logic_error); + } + } }