diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e4ac92..2ce05de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ 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 diff --git a/promise.hpp b/promise.hpp index 3ce11d4..850a9e9 100644 --- a/promise.hpp +++ b/promise.hpp @@ -199,6 +199,18 @@ namespace promise_hpp }); } + template < typename ResolveF > + auto then_any(ResolveF&& on_resolve) { + return then([ + f = std::forward(on_resolve) + ](const T& v) mutable { + auto r = invoke_hpp::invoke( + std::forward(f), + v); + return make_any_promise(std::move(r)); + }); + } + template < typename ResolveF , typename ResolveFR = invoke_hpp::invoke_result_t > std::enable_if_t< @@ -224,20 +236,17 @@ namespace promise_hpp } template < typename U > - promise& resolve(U&& value) { - state_->resolve(std::forward(value)); - return *this; + bool resolve(U&& value) { + return state_->resolve(std::forward(value)); } - promise& reject(std::exception_ptr e) { - state_->reject(e); - return *this; + bool reject(std::exception_ptr e) { + return state_->reject(e); } template < typename E > - promise& reject(E&& e) { - state_->reject(std::make_exception_ptr(std::forward(e))); - return *this; + bool reject(E&& e) { + return state_->reject(std::make_exception_ptr(std::forward(e))); } private: class state; @@ -248,24 +257,26 @@ namespace promise_hpp state() = default; template < typename U > - void resolve(U&& value) { + bool resolve(U&& value) { std::lock_guard guard(mutex_); if ( status_ != status::pending ) { - throw std::logic_error("do not try to resolve a resolved or rejected promise"); + return false; } storage_.set(std::forward(value)); status_ = status::resolved; invoke_resolve_handlers_(); + return true; } - void reject(std::exception_ptr e) { + bool reject(std::exception_ptr e) { std::lock_guard guard(mutex_); if ( status_ != status::pending ) { - throw std::logic_error("do not try to reject a resolved or rejected promise"); + return false; } exception_ = e; status_ = status::rejected; invoke_reject_handlers_(); + return true; } template < typename U, typename ResolveF, typename RejectF > @@ -483,6 +494,17 @@ namespace promise_hpp }); } + template < typename ResolveF > + auto then_any(ResolveF&& on_resolve) { + return then([ + f = std::forward(on_resolve) + ]() mutable { + auto r = invoke_hpp::invoke( + std::forward(f)); + return make_any_promise(std::move(r)); + }); + } + template < typename ResolveF , typename ResolveFR = invoke_hpp::invoke_result_t > std::enable_if_t< @@ -507,20 +529,17 @@ namespace promise_hpp return next; } - promise& resolve() { - state_->resolve(); - return *this; + bool resolve() { + return state_->resolve(); } - promise& reject(std::exception_ptr e) { - state_->reject(e); - return *this; + bool reject(std::exception_ptr e) { + return state_->reject(e); } template < typename E > - promise& reject(E&& e) { - state_->reject(std::make_exception_ptr(std::forward(e))); - return *this; + bool reject(E&& e) { + return state_->reject(std::make_exception_ptr(std::forward(e))); } private: class state; @@ -530,23 +549,25 @@ namespace promise_hpp public: state() = default; - void resolve() { + bool resolve() { std::lock_guard guard(mutex_); if ( status_ != status::pending ) { - throw std::logic_error("do not try to resolve a resolved or rejected promise"); + return false; } status_ = status::resolved; invoke_resolve_handlers_(); + return true; } - void reject(std::exception_ptr e) { + bool reject(std::exception_ptr e) { std::lock_guard guard(mutex_); if ( status_ != status::pending ) { - throw std::logic_error("do not try to reject a resolved or rejected promise"); + return false; } exception_ = e; status_ = status::rejected; invoke_reject_handlers_(); + return true; } template < typename U, typename ResolveF, typename RejectF > @@ -693,13 +714,13 @@ namespace promise_hpp auto resolver = [ p = result ](auto&& v) mutable { - p.resolve(std::forward(v)); + return p.resolve(std::forward(v)); }; auto rejector = [ p = result ](auto&& e) mutable { - p.reject(std::forward(e)); + return p.reject(std::forward(e)); }; try { @@ -801,4 +822,35 @@ namespace promise_hpp std::begin(container), std::end(container)); } + + // + // make_any_promise + // + + template < typename Iter > + auto make_any_promise(Iter begin, Iter end) { + using child_promise_t = typename Iter::value_type; + using child_promise_value_t = typename child_promise_t::value_type; + + if ( begin == end ) { + throw std::logic_error("at least one input promise must be provided for make_any_promise"); + } + + return make_promise([begin, end](auto&& resolver, auto&& rejector){ + for ( auto iter = begin; iter != end; ++iter ) { + (*iter).then([resolver](const child_promise_value_t& v) mutable { + resolver(v); + }).fail([rejector](std::exception_ptr e) mutable { + rejector(e); + }); + } + }); + } + + template < typename Container > + auto make_any_promise(Container&& container) { + return make_any_promise( + std::begin(container), + std::end(container)); + } } diff --git a/tests.cpp b/tests.cpp index 9cc9cdd..15c4e40 100644 --- a/tests.cpp +++ b/tests.cpp @@ -74,7 +74,7 @@ TEST_CASE("is_promise_r") { pr::is_promise_r>::value, "unit test fail"); static_assert( - pr::is_promise_r>::value, + pr::is_promise_r>::value, "unit test fail"); } SECTION("negative") { @@ -88,7 +88,7 @@ TEST_CASE("is_promise_r") { !pr::is_promise_r>::value, "unit test fail"); static_assert( - !pr::is_promise_r::value, + !pr::is_promise_r::value, "unit test fail"); } } @@ -97,18 +97,18 @@ TEST_CASE("promise") { SECTION("resolved") { { int check_42_int = 0; - pr::promise() - .resolve(42) - .then([&check_42_int](int value){ + auto p = pr::promise(); + p.resolve(42); + p.then([&check_42_int](int value){ check_42_int = value; }); REQUIRE(check_42_int == 42); } { int check_42_int = 0; - pr::promise() - .resolve(42) - .fail([](std::exception_ptr){ + auto p = pr::promise(); + p.resolve(42); + p.fail([](std::exception_ptr){ }).then([&check_42_int](int value){ check_42_int = value; }); @@ -118,9 +118,9 @@ TEST_CASE("promise") { int check_84_int = 0; bool check_void_call = false; int check_100500_transform = 0; - pr::promise() - .resolve(42) - .then([](int value){ + auto p = pr::promise(); + p.resolve(42); + p.then([](int value){ return value * 2; }).then([&check_84_int](int value){ check_84_int = value; @@ -140,9 +140,9 @@ TEST_CASE("promise") { { bool call_fail_with_logic_error = false; bool not_call_then_on_reject = true; - pr::promise() - .reject(std::logic_error("hello fail")) - .then([¬_call_then_on_reject](int value) { + auto p = pr::promise(); + p.reject(std::logic_error("hello fail")); + p.then([¬_call_then_on_reject](int value) { (void)value; not_call_then_on_reject = false; }).fail([&call_fail_with_logic_error](std::exception_ptr e){ @@ -154,9 +154,9 @@ TEST_CASE("promise") { { std::logic_error ee("hello fail"); bool call_fail_with_logic_error = false; - pr::promise() - .reject(ee) - .then([](int){ + auto p = pr::promise(); + p.reject(ee); + p.then([](int){ }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); }); @@ -165,9 +165,9 @@ TEST_CASE("promise") { { std::logic_error ee("hello fail"); bool call_fail_with_logic_error = false; - pr::promise() - .reject(std::make_exception_ptr(ee)) - .then([](int){ + auto p = pr::promise(); + p.reject(std::make_exception_ptr(ee)); + p.then([](int){ }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); }); @@ -175,9 +175,9 @@ TEST_CASE("promise") { } { int check_multi_fail = 0; - pr::promise<>() - .reject(std::logic_error("hello fail")) - .fail([&check_multi_fail](std::exception_ptr){ + auto p = pr::promise<>(); + p.reject(std::logic_error("hello fail")); + p.fail([&check_multi_fail](std::exception_ptr){ ++check_multi_fail; }).fail([&check_multi_fail](std::exception_ptr){ ++check_multi_fail; @@ -285,7 +285,7 @@ TEST_CASE("promise") { REQUIRE(call_fail_with_logic_error); } { - bool call_fail_with_logic_error = 0; + 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); @@ -297,9 +297,9 @@ TEST_CASE("promise") { { bool not_call_then_on_reject = true; bool call_fail_with_logic_error = false; - pr::promise() - .resolve(42) - .then([](int){ + auto p = pr::promise(); + p.resolve(42); + p.then([](int){ throw std::logic_error("hello fail"); }).then([¬_call_then_on_reject](){ not_call_then_on_reject = false; @@ -379,6 +379,7 @@ TEST_CASE("promise") { auto p2 = pr::make_resolved_promise(84); p1.then([&p2](int v){ + (void)v; return p2; }).then([&check_84_int](int v2){ check_84_int = v2; @@ -405,6 +406,7 @@ TEST_CASE("promise") { auto p2 = pr::make_resolved_promise(); p1.then([&p2](int v){ + (void)v; return p2; }).then([&check_84_int](){ check_84_int = 84; @@ -433,6 +435,7 @@ TEST_CASE("promise") { auto p2 = pr::make_promise(); p1.then([&p2](int v){ + (void)v; return p2; }).then([&check_84_int](int v2){ check_84_int = v2; @@ -455,7 +458,7 @@ TEST_CASE("promise") { (void)v; throw std::logic_error("hello fail"); return p2; - }).then([&call_fail_with_logic_error](int v2){ + }).then([](int v2){ (void)v2; }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); @@ -471,7 +474,7 @@ TEST_CASE("promise") { p1.then([&p2](int v){ (void)v; return p2; - }).then([&call_fail_with_logic_error](int v2){ + }).then([](int v2){ (void)v2; throw std::logic_error("hello fail"); }).fail([&call_fail_with_logic_error](std::exception_ptr e){ @@ -488,7 +491,7 @@ TEST_CASE("promise") { p1.then([&p2](int v){ (void)v; return p2; - }).then([&call_fail_with_logic_error](int v2){ + }).then([](int v2){ (void)v2; }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); @@ -504,7 +507,7 @@ TEST_CASE("promise") { p1.then([&p2](int v){ (void)v; return p2; - }).then([&call_fail_with_logic_error](int v2){ + }).then([](int v2){ (void)v2; }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); @@ -522,7 +525,7 @@ TEST_CASE("promise") { p1.then([&p2](){ throw std::logic_error("hello fail"); return p2; - }).then([&call_fail_with_logic_error](){ + }).then([](){ }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); }); @@ -536,7 +539,7 @@ TEST_CASE("promise") { p1.then([&p2](){ return p2; - }).then([&call_fail_with_logic_error](){ + }).then([](){ 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); @@ -551,7 +554,7 @@ TEST_CASE("promise") { p1.then([&p2](){ return p2; - }).then([&call_fail_with_logic_error](){ + }).then([](){ }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); }); @@ -565,7 +568,7 @@ TEST_CASE("promise") { p1.then([&p2](){ return p2; - }).then([&call_fail_with_logic_error](){ + }).then([](){ }).fail([&call_fail_with_logic_error](std::exception_ptr e){ call_fail_with_logic_error = check_hello_fail_exception(e); }); @@ -601,6 +604,7 @@ TEST_CASE("promise") { int call_then_only_once = 0; pr::make_all_promise(std::vector>{p1, p2}) .then([&call_then_only_once](const std::vector& c){ + (void)c; ++call_then_only_once; }); @@ -610,6 +614,48 @@ TEST_CASE("promise") { REQUIRE(call_then_only_once == 1); } } + SECTION("make_any_promise") { + { + auto p1 = pr::promise(); + auto p2 = pr::promise(); + + int check_42_int = 0; + int call_then_only_once = 0; + pr::make_any_promise(std::vector>{p1, p2}) + .then([&check_42_int, &call_then_only_once](const int& v){ + check_42_int = v; + ++call_then_only_once; + }); + + p1.resolve(42); + REQUIRE(check_42_int == 42); + REQUIRE(call_then_only_once == 1); + + p2.resolve(84); + REQUIRE(check_42_int == 42); + REQUIRE(call_then_only_once == 1); + } + { + auto p1 = pr::promise(); + auto p2 = pr::promise(); + + int check_42_int = 0; + int call_then_only_once = 0; + pr::make_any_promise(std::vector>{p1, p2}) + .then([&check_42_int, &call_then_only_once](const int& v){ + check_42_int = v; + ++call_then_only_once; + }); + + p2.resolve(42); + REQUIRE(check_42_int == 42); + REQUIRE(call_then_only_once == 1); + + p1.resolve(84); + REQUIRE(check_42_int == 42); + REQUIRE(call_then_only_once == 1); + } + } SECTION("make_all_promise_fail") { { bool call_fail_with_logic_error = false; @@ -627,6 +673,26 @@ TEST_CASE("promise") { REQUIRE(call_fail_with_logic_error); } } + SECTION("make_any_promise_fail") { + REQUIRE_THROWS_AS( + pr::make_any_promise(std::vector>{}), + std::logic_error); + { + bool call_fail_with_logic_error = false; + bool not_call_then_on_reject = true; + auto p = pr::make_any_promise(std::vector>{ + pr::make_rejected_promise(std::logic_error("hello fail")), + pr::make_resolved_promise(10) + }).then([¬_call_then_on_reject](const int& c){ + (void)c; + not_call_then_on_reject = false; + }).fail([&call_fail_with_logic_error](std::exception_ptr e){ + call_fail_with_logic_error = check_hello_fail_exception(e); + }); + REQUIRE(not_call_then_on_reject); + REQUIRE(call_fail_with_logic_error); + } + } SECTION("then_all") { { int check_42_int = 0; @@ -636,7 +702,9 @@ TEST_CASE("promise") { pr::make_resolved_promise(32), pr::make_resolved_promise(10)}; }).then([&check_42_int](const std::vector& v){ - check_42_int = std::accumulate(v.begin(), v.end(), 0); + if ( v.size() == 2) { + check_42_int = v[0] + v[1]; + } }); REQUIRE(check_42_int == 42); } @@ -650,10 +718,56 @@ TEST_CASE("promise") { pr::make_resolved_promise(32), pr::make_resolved_promise(10)}; }).then([&check_42_int2](const std::vector& v){ - check_42_int2 = std::accumulate(v.begin(), v.end(), 0); + if ( v.size() == 2) { + check_42_int2 = v[0] + v[1]; + } }); REQUIRE(check_42_int == 42); REQUIRE(check_42_int2 == 42); } } + SECTION("then_any") { + { + int check_42_int = 0; + pr::make_resolved_promise() + .then_any([](){ + return std::vector>{ + pr::make_resolved_promise(42), + pr::make_rejected_promise(std::logic_error("hello fail"))}; + }).then([&check_42_int](const int& v){ + check_42_int = v; + }); + REQUIRE(check_42_int == 42); + } + { + bool call_fail_with_logic_error = false; + pr::make_resolved_promise() + .then_any([](){ + return std::vector>{ + pr::make_rejected_promise(std::logic_error("hello fail")), + pr::make_resolved_promise(42)}; + }).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); + } + { + int check_42_int = 0; + int check_42_int2 = 0; + int call_then_only_once = 0; + pr::make_resolved_promise(42) + .then_any([&check_42_int](int v){ + check_42_int = v; + return std::vector>{ + pr::make_resolved_promise(42), + pr::make_resolved_promise(10)}; + }).then([&call_then_only_once, &check_42_int2](const int& v){ + ++call_then_only_once; + check_42_int2 = v; + }); + REQUIRE(check_42_int == 42); + REQUIRE(check_42_int2 == 42); + REQUIRE(call_then_only_once == 1); + } + } }