From c4f2a960237ed29fdfbe491889ec39fbf0b0de15 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 10 Dec 2018 11:28:16 +0700 Subject: [PATCH] then function with both resolve and reject callbacks also fix problem with throw exceptions from fail --- CMakeLists.txt | 9 +- promise.hpp | 344 ++++++++++++++++++++++++++----------------------- tests.cpp | 25 ++++ 3 files changed, 210 insertions(+), 168 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ce05de..0c5177d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,17 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(promise) -if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") -endif(MSVC) - file(GLOB test_sources "*.cpp" "*.hpp") add_executable(${PROJECT_NAME} ${test_sources}) + set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) +if(MSVC) + target_compile_options(${PROJECT_NAME} PRIVATE /bigobj) +endif(MSVC) + enable_testing() add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/promise.hpp b/promise.hpp index 850a9e9..f620289 100644 --- a/promise.hpp +++ b/promise.hpp @@ -133,33 +133,6 @@ namespace promise_hpp promise() : state_(std::make_shared()) {} - 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](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< @@ -177,10 +150,37 @@ namespace promise_hpp v); np.then([n = n]() mutable { n.resolve(); - }).fail([n = n](std::exception_ptr e) mutable { + }, [n = n](std::exception_ptr e) mutable { n.reject(e); }); - }).fail([n = next](std::exception_ptr e) mutable { + }, [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](const typename ResolveFR::value_type& nv) mutable { + n.resolve(nv); + }, [n = n](std::exception_ptr e) mutable { + n.reject(e); + }); + }, [n = next](std::exception_ptr e) mutable { n.reject(e); }); @@ -211,42 +211,53 @@ namespace promise_hpp }); } + template < typename ResolveF + , typename RejectF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + !is_promise::value, + 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 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, + return then( std::forward(on_resolve), [](std::exception_ptr){}); - return next; } template < typename RejectF > promise fail(RejectF&& on_reject) { - promise next; - state_->attach( - next, + return then( [](const T& value) { return value; }, std::forward(on_reject)); - return next; } template < typename U > bool resolve(U&& value) { - return state_->resolve(std::forward(value)); + return state_->resolve( + std::forward(value)); } - bool reject(std::exception_ptr e) { + bool reject(std::exception_ptr e) noexcept { return state_->reject(e); } template < typename E > bool reject(E&& e) { - return state_->reject(std::make_exception_ptr(std::forward(e))); + return state_->reject( + std::make_exception_ptr(std::forward(e))); } private: class state; @@ -268,7 +279,7 @@ namespace promise_hpp return true; } - bool reject(std::exception_ptr e) { + bool reject(std::exception_ptr e) noexcept { std::lock_guard guard(mutex_); if ( status_ != status::pending ) { return false; @@ -280,22 +291,8 @@ namespace promise_hpp } template < typename U, typename ResolveF, typename RejectF > - std::enable_if_t::value, void> + std::enable_if_t::value, void> attach(promise& next, ResolveF&& resolve, RejectF&& reject) { - auto resolve_h = [ - n = next, - f = std::forward(resolve) - ](const T& v) mutable { - try { - auto r = invoke_hpp::invoke( - std::forward(f), - v); - n.resolve(std::move(r)); - } catch (...) { - n.reject(std::current_exception()); - } - }; - auto reject_h = [ n = next, f = std::forward(reject) @@ -310,29 +307,10 @@ namespace promise_hpp } }; - std::lock_guard guard(mutex_); - - if ( status_ == status::resolved ) { - invoke_hpp::invoke( - std::move(resolve_h), - storage_.value()); - } else if ( status_ == status::rejected ) { - invoke_hpp::invoke( - std::move(reject_h), - exception_); - } else { - handlers_.emplace_back( - std::move(resolve_h), - std::move(reject_h)); - } - } - - template < typename U, typename ResolveF, typename RejectF > - std::enable_if_t::value, void> - attach(promise& next, ResolveF&& resolve, RejectF&& reject) { auto resolve_h = [ n = next, - f = std::forward(resolve) + f = std::forward(resolve), + j = reject_h ](const T& v) mutable { try { invoke_hpp::invoke( @@ -340,10 +318,19 @@ namespace promise_hpp v); n.resolve(); } catch (...) { - n.reject(std::current_exception()); + invoke_hpp::invoke( + std::move(j), + std::current_exception()); } }; + std::lock_guard guard(mutex_); + add_handlers_(std::move(resolve_h), std::move(reject_h)); + } + + template < typename U, typename ResolveF, typename RejectF > + std::enable_if_t::value, void> + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { auto reject_h = [ n = next, f = std::forward(reject) @@ -358,23 +345,44 @@ namespace promise_hpp } }; - std::lock_guard guard(mutex_); + auto resolve_h = [ + n = next, + f = std::forward(resolve), + j = reject_h + ](const T& v) mutable { + try { + auto r = invoke_hpp::invoke( + std::forward(f), + v); + n.resolve(std::move(r)); + } catch (...) { + invoke_hpp::invoke( + std::move(j), + std::current_exception()); + } + }; + std::lock_guard guard(mutex_); + add_handlers_(std::move(resolve_h), std::move(reject_h)); + } + private: + template < typename ResolveF, typename RejectF > + void add_handlers_(ResolveF&& resolve, RejectF&& reject) { if ( status_ == status::resolved ) { invoke_hpp::invoke( - std::move(resolve_h), + std::forward(resolve), storage_.value()); } else if ( status_ == status::rejected ) { invoke_hpp::invoke( - std::move(reject_h), + std::forward(reject), exception_); } else { handlers_.emplace_back( - std::move(resolve_h), - std::move(reject_h)); + std::forward(resolve), + std::forward(reject)); } } - private: + void invoke_resolve_handlers_() noexcept { const T& value = storage_.value(); for ( const auto& h : handlers_ ) { @@ -431,32 +439,6 @@ namespace promise_hpp promise() : state_(std::make_shared()) {} - 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](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< @@ -473,10 +455,36 @@ namespace promise_hpp std::forward(f)); np.then([n = n]() mutable { n.resolve(); - }).fail([n = n](std::exception_ptr e) mutable { + }, [n = n](std::exception_ptr e) mutable { n.reject(e); }); - }).fail([n = next](std::exception_ptr e) mutable { + }, [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](const typename ResolveFR::value_type& nv) mutable { + n.resolve(nv); + }, [n = n](std::exception_ptr e) mutable { + n.reject(e); + }); + }, [n = next](std::exception_ptr e) mutable { n.reject(e); }); @@ -505,41 +513,51 @@ namespace promise_hpp }); } + template < typename ResolveF + , typename RejectF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + !is_promise::value, + 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 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, + return then( std::forward(on_resolve), [](std::exception_ptr){}); - return next; } template < typename RejectF > promise fail(RejectF&& on_reject) { - promise next; - state_->attach( - next, + return then( []{}, std::forward(on_reject)); - return next; } bool resolve() { return state_->resolve(); } - bool reject(std::exception_ptr e) { + bool reject(std::exception_ptr e) noexcept { return state_->reject(e); } template < typename E > bool reject(E&& e) { - return state_->reject(std::make_exception_ptr(std::forward(e))); + return state_->reject( + std::make_exception_ptr(std::forward(e))); } private: class state; @@ -559,7 +577,7 @@ namespace promise_hpp return true; } - bool reject(std::exception_ptr e) { + bool reject(std::exception_ptr e) noexcept { std::lock_guard guard(mutex_); if ( status_ != status::pending ) { return false; @@ -571,21 +589,8 @@ namespace promise_hpp } template < typename U, typename ResolveF, typename RejectF > - std::enable_if_t::value, void> + std::enable_if_t::value, void> attach(promise& next, ResolveF&& resolve, RejectF&& reject) { - auto resolve_h = [ - n = next, - f = std::forward(resolve) - ]() mutable { - try { - auto r = invoke_hpp::invoke( - std::forward(f)); - n.resolve(std::move(r)); - } catch (...) { - n.reject(std::current_exception()); - } - }; - auto reject_h = [ n = next, f = std::forward(reject) @@ -600,38 +605,29 @@ namespace promise_hpp } }; - std::lock_guard guard(mutex_); - - if ( status_ == status::resolved ) { - invoke_hpp::invoke( - std::move(resolve_h)); - } else if ( status_ == status::rejected ) { - invoke_hpp::invoke( - std::move(reject_h), - exception_); - } else { - handlers_.emplace_back( - std::move(resolve_h), - std::move(reject_h)); - } - } - - template < typename U, typename ResolveF, typename RejectF > - std::enable_if_t::value, void> - attach(promise& next, ResolveF&& resolve, RejectF&& reject) { auto resolve_h = [ n = next, - f = std::forward(resolve) + f = std::forward(resolve), + j = reject_h ]() mutable { try { invoke_hpp::invoke( std::forward(f)); n.resolve(); } catch (...) { - n.reject(std::current_exception()); + invoke_hpp::invoke( + std::move(j), + std::current_exception()); } }; + std::lock_guard guard(mutex_); + add_handlers_(std::move(resolve_h), std::move(reject_h)); + } + + template < typename U, typename ResolveF, typename RejectF > + std::enable_if_t::value, void> + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { auto reject_h = [ n = next, f = std::forward(reject) @@ -646,22 +642,42 @@ namespace promise_hpp } }; - std::lock_guard guard(mutex_); + auto resolve_h = [ + n = next, + f = std::forward(resolve), + j = reject_h + ]() mutable { + try { + auto r = invoke_hpp::invoke( + std::forward(f)); + n.resolve(std::move(r)); + } catch (...) { + invoke_hpp::invoke( + std::move(j), + std::current_exception()); + } + }; + std::lock_guard guard(mutex_); + add_handlers_(std::move(resolve_h), std::move(reject_h)); + } + private: + template < typename ResolveF, typename RejectF > + void add_handlers_(ResolveF&& resolve, RejectF&& reject) { if ( status_ == status::resolved ) { invoke_hpp::invoke( - std::move(resolve_h)); + std::forward(resolve)); } else if ( status_ == status::rejected ) { invoke_hpp::invoke( - std::move(reject_h), + std::forward(reject), exception_); } else { handlers_.emplace_back( - std::move(resolve_h), - std::move(reject_h)); + std::forward(resolve), + std::forward(reject)); } } - private: + void invoke_resolve_handlers_() noexcept { for ( const auto& h : handlers_ ) { h.resolve_(); diff --git a/tests.cpp b/tests.cpp index 15c4e40..53afae5 100644 --- a/tests.cpp +++ b/tests.cpp @@ -18,6 +18,16 @@ namespace return false; } } + + bool check_hello_fail2_exception(std::exception_ptr e) { + try { + std::rethrow_exception(e); + } catch (std::logic_error& ee) { + return 0 == std::strcmp(ee.what(), "hello fail2"); + } catch (...) { + return false; + } + } } TEST_CASE("is_promise") { @@ -309,6 +319,21 @@ TEST_CASE("promise") { REQUIRE(not_call_then_on_reject); REQUIRE(call_fail_with_logic_error); } + { + bool not_call_then_on_reject = true; + bool call_fail_with_logic_error = false; + auto p = pr::promise(); + p.resolve(42); + p.then([](int){ + throw std::logic_error("hello fail"); + }, [](std::exception_ptr){ + throw std::logic_error("hello fail2"); + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail2_exception(e); + }); + REQUIRE(not_call_then_on_reject); + REQUIRE(call_fail_with_logic_error); + } } SECTION("multi_then") { {