From b6f5f36dc47999b3d11818b8132e9d63bae06e37 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 30 Dec 2019 03:33:06 +0700 Subject: [PATCH] C++17 refactoring, reference support --- README.md | 4 +- headers/promise.hpp/promise.hpp | 687 ++++++++++++++++---------------- untests/promise_tests.cpp | 39 ++ 3 files changed, 390 insertions(+), 340 deletions(-) diff --git a/README.md b/README.md index 369a3ae..8d10e87 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,14 @@ [badge.travis]: https://img.shields.io/travis/BlackMATov/promise.hpp/master.svg?logo=travis [badge.appveyor]: https://img.shields.io/appveyor/ci/BlackMATov/promise-hpp/master.svg?logo=appveyor [badge.codecov]: https://img.shields.io/codecov/c/github/BlackMATov/promise.hpp/master.svg?logo=codecov -[badge.language]: https://img.shields.io/badge/language-C%2B%2B14-red.svg +[badge.language]: https://img.shields.io/badge/language-C%2B%2B17-yellow.svg [badge.license]: https://img.shields.io/badge/license-MIT-blue.svg [badge.paypal]: https://img.shields.io/badge/donate-PayPal-orange.svg?logo=paypal&colorA=00457C [travis]: https://travis-ci.org/BlackMATov/promise.hpp [appveyor]: https://ci.appveyor.com/project/BlackMATov/promise-hpp [codecov]: https://codecov.io/gh/BlackMATov/promise.hpp -[language]: https://en.wikipedia.org/wiki/C%2B%2B14 +[language]: https://en.wikipedia.org/wiki/C%2B%2B17 [license]: https://en.wikipedia.org/wiki/MIT_License [paypal]: https://www.paypal.me/matov diff --git a/headers/promise.hpp/promise.hpp b/headers/promise.hpp/promise.hpp index 64cc29f..4570a69 100644 --- a/headers/promise.hpp/promise.hpp +++ b/headers/promise.hpp/promise.hpp @@ -53,6 +53,9 @@ namespace promise_hpp struct is_promise : impl::is_promise_impl> {}; + template < typename T > + inline constexpr bool is_promise_v = is_promise::value; + // // is_promise_r // @@ -72,6 +75,9 @@ namespace promise_hpp struct is_promise_r : impl::is_promise_r_impl> {}; + template < typename R, typename T > + inline constexpr bool is_promise_r_v = is_promise_r::value; + // // detail // @@ -93,29 +99,37 @@ namespace promise_hpp storage() = default; ~storage() - noexcept(std::is_nothrow_destructible::value) - { + noexcept(std::is_nothrow_destructible_v) { if ( initialized_ ) { ptr_()->~T(); } } - template < typename U > - void set(U&& value) - noexcept(std::is_nothrow_constructible::value) - { + void set(T&& value) + noexcept(std::is_nothrow_move_constructible_v) { assert(!initialized_); - ::new(ptr_()) T(std::forward(value)); + ::new(ptr_()) T(std::move(value)); + initialized_ = true; + } + + void set(const T& value) + noexcept(std::is_nothrow_copy_constructible_v) { + assert(!initialized_); + ::new(ptr_()) T(value); initialized_ = true; } T get() - noexcept(std::is_nothrow_move_constructible::value) - { + noexcept(std::is_nothrow_move_constructible_v) { assert(initialized_); return std::move(*ptr_()); } + T& value() noexcept { + assert(initialized_); + return *ptr_(); + } + const T& value() const noexcept { assert(initialized_); return *ptr_(); @@ -132,6 +146,37 @@ namespace promise_hpp std::aligned_storage_t data_; bool initialized_ = false; }; + + template < typename T > + class storage final : private noncopyable { + public: + storage() = default; + ~storage() = default; + + void set(T& value) noexcept { + assert(!initialized_); + value_ = &value; + initialized_ = true; + } + + T& get() noexcept { + assert(initialized_); + return *value_; + } + + T& value() noexcept { + assert(initialized_); + return *value_; + } + + const T& value() const noexcept { + assert(initialized_); + return *value_; + } + private: + T* value_{nullptr}; + bool initialized_ = false; + }; } // @@ -142,21 +187,29 @@ namespace promise_hpp no_timeout, timeout }; +} - // - // promise - // +// ----------------------------------------------------------------------------- +// +// promise +// +// ----------------------------------------------------------------------------- +namespace promise_hpp +{ template < typename T > class promise final { public: using value_type = T; - public: + promise() : state_(std::make_shared()) {} - promise(const promise&) noexcept = default; - promise& operator=(const promise&) noexcept = default; + promise(promise&&) = default; + promise& operator=(promise&&) = default; + + promise(const promise&) = default; + promise& operator=(const promise&) = default; void swap(promise& other) noexcept { state_.swap(other.state_); @@ -178,50 +231,80 @@ namespace promise_hpp return l.state_ != r.state_; } - template < typename ResolveF - , typename ResolveFR = std::invoke_result_t > - std::enable_if_t< - is_promise::value && std::is_void::value, - promise> - then(ResolveF&& on_resolve) { - promise next; + // + // get + // - then([ - n = next, - f = std::forward(on_resolve) - ](auto&& v) mutable { - auto np = std::invoke( - std::forward(f), - std::forward(v)); - std::move(np).then([n]() mutable { - n.resolve(); - }).except([n](std::exception_ptr e) mutable { - n.reject(e); - }); - }).except([n = next](std::exception_ptr e) mutable { - n.reject(e); - }); - - return next; + const T& get() const { + return state_->get(); } + template < typename U > + T get_or_default(U&& def) const { + try { + return get(); + } catch (...) { + return std::forward(def); + } + } + + // + // wait + // + + void wait() const noexcept { + state_->wait(); + } + + template < typename Rep, typename Period > + promise_wait_status wait_for(const std::chrono::duration& timeout_duration) const { + return state_->wait_for(timeout_duration); + } + + template < typename Clock, typename Duration > + promise_wait_status wait_until(const std::chrono::time_point& timeout_time) const { + return state_->wait_until(timeout_time); + } + + // + // resolve/reject + // + + template < typename U > + bool resolve(U&& value) { + return state_->resolve(std::forward(value)); + } + + 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))); + } + + // + // then + // + template < typename ResolveF - , typename ResolveFR = std::invoke_result_t > + , typename ResolveR = std::invoke_result_t > std::enable_if_t< - is_promise::value && !std::is_void::value, - promise> + is_promise_v, + promise> then(ResolveF&& on_resolve) { - promise next; + promise next; then([ n = next, f = std::forward(on_resolve) - ](auto&& v) mutable { + ](auto&&... vs) mutable { auto np = std::invoke( std::forward(f), - std::forward(v)); - std::move(np).then([n](auto&& nv) mutable { - n.resolve(std::forward(nv)); + std::forward(vs)...); + std::move(np).then([n](auto&&... nvs) mutable { + n.resolve(std::forward(nvs)...); }).except([n](std::exception_ptr e) mutable { n.reject(e); }); @@ -268,38 +351,48 @@ namespace promise_hpp }); } + // + // then + // + + template < typename ResolveF + , typename ResolveR = std::invoke_result_t > + std::enable_if_t< + !is_promise_v, + promise> + then(ResolveF&& on_resolve) { + promise next; + + state_->attach( + next, + std::forward(on_resolve), + [](std::exception_ptr e) -> ResolveR { std::rethrow_exception(e); }, + false); + + return next; + } + template < typename ResolveF , typename RejectF - , typename ResolveFR = std::invoke_result_t > + , typename ResolveR = std::invoke_result_t > std::enable_if_t< - !is_promise::value, - promise> + !is_promise_v, + promise> then(ResolveF&& on_resolve, RejectF&& on_reject) { - promise next; + promise next; + state_->attach( next, std::forward(on_resolve), std::forward(on_reject), true); + return next; } - template < typename ResolveF - , typename ResolveFR = std::invoke_result_t > - std::enable_if_t< - !is_promise::value, - promise> - then(ResolveF&& on_resolve) { - promise next; - state_->attach( - next, - std::forward(on_resolve), - [](std::exception_ptr e) -> ResolveFR { - std::rethrow_exception(e); - }, - false); - return next; - } + // + // except + // template < typename RejectF > promise except(RejectF&& on_reject) { @@ -307,52 +400,6 @@ namespace promise_hpp [](auto&& v) { return std::forward(v); }, std::forward(on_reject)); } - - template < typename U > - bool resolve(U&& value) { - return state_->resolve(std::forward(value)); - } - - 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))); - } - - const T& get() const { - return state_->get(); - } - - template < typename U > - T get_or_default(U&& def) const { - try { - return get(); - } catch (...) { - return std::forward(def); - } - } - - void wait() const noexcept { - state_->wait(); - } - - template < typename Rep, typename Period > - promise_wait_status wait_for( - const std::chrono::duration& timeout_duration) const - { - return state_->wait_for(timeout_duration); - } - - template < typename Clock, typename Duration > - promise_wait_status wait_until( - const std::chrono::time_point& timeout_time) const - { - return state_->wait_until(timeout_time); - } private: class state; std::shared_ptr state_; @@ -361,33 +408,8 @@ namespace promise_hpp public: state() = default; - template < typename U > - bool resolve(U&& value) { - std::lock_guard guard(mutex_); - if ( status_ != status::pending ) { - return false; - } - storage_.set(std::forward(value)); - status_ = status::resolved; - invoke_resolve_handlers_(); - cond_var_.notify_all(); - return true; - } - - bool reject(std::exception_ptr e) noexcept { - std::lock_guard guard(mutex_); - if ( status_ != status::pending ) { - return false; - } - exception_ = e; - status_ = status::rejected; - invoke_reject_handlers_(); - cond_var_.notify_all(); - return true; - } - const T& get() { - std::unique_lock lock(mutex_); + std::unique_lock lock(mutex_); cond_var_.wait(lock, [this](){ return status_ != status::pending; }); @@ -399,42 +421,56 @@ namespace promise_hpp } void wait() const noexcept { - std::unique_lock lock(mutex_); + std::unique_lock lock(mutex_); cond_var_.wait(lock, [this](){ return status_ != status::pending; }); } template < typename Rep, typename Period > - promise_wait_status wait_for( - const std::chrono::duration& timeout_duration) const - { - std::unique_lock lock(mutex_); + promise_wait_status wait_for(const std::chrono::duration& timeout_duration) const { + std::unique_lock lock(mutex_); return cond_var_.wait_for(lock, timeout_duration, [this](){ return status_ != status::pending; - }) ? promise_wait_status::no_timeout - : promise_wait_status::timeout; + }) ? promise_wait_status::no_timeout : promise_wait_status::timeout; } template < typename Clock, typename Duration > - promise_wait_status wait_until( - const std::chrono::time_point& timeout_time) const - { - std::unique_lock lock(mutex_); + promise_wait_status wait_until(const std::chrono::time_point& timeout_time) const { + std::unique_lock lock(mutex_); return cond_var_.wait_until(lock, timeout_time, [this](){ return status_ != status::pending; - }) ? promise_wait_status::no_timeout - : promise_wait_status::timeout; + }) ? promise_wait_status::no_timeout : promise_wait_status::timeout; } + template < typename U > + bool resolve(U&& value) { + std::lock_guard guard(mutex_); + if ( status_ != status::pending ) { + return false; + } + storage_.set(std::forward(value)); + status_ = status::resolved; + invoke_resolve_handlers_(); + cond_var_.notify_all(); + return true; + } + + bool reject(std::exception_ptr e) noexcept { + std::lock_guard guard(mutex_); + if ( status_ != status::pending ) { + return false; + } + exception_ = e; + status_ = status::rejected; + invoke_reject_handlers_(); + cond_var_.notify_all(); + return true; + } + public: template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach( - promise& next, - ResolveF&& on_resolve, - RejectF&& on_reject, - bool has_reject) - { + attach(promise& next, ResolveF&& on_resolve, RejectF&& on_reject, bool has_reject) { auto reject_h = [ n = next, f = std::forward(on_reject), @@ -468,18 +504,13 @@ namespace promise_hpp } }; - std::lock_guard guard(mutex_); + 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&& on_resolve, - RejectF&& on_reject, - bool has_reject) - { + attach(promise& next, ResolveF&& on_resolve, RejectF&& on_reject, bool has_reject) { auto reject_h = [ n = next, f = std::forward(on_reject), @@ -513,7 +544,7 @@ namespace promise_hpp } }; - std::lock_guard guard(mutex_); + std::lock_guard guard(mutex_); add_handlers_(std::move(resolve_h), std::move(reject_h)); } private: @@ -528,16 +559,15 @@ namespace promise_hpp std::forward(reject), exception_); } else { - handlers_.emplace_back( + handlers_.push_back({ std::forward(resolve), - std::forward(reject)); + std::forward(reject)}); } } void invoke_resolve_handlers_() noexcept { - const T& value = storage_.value(); for ( const auto& h : handlers_ ) { - h.resolve_(value); + h.resolve_(storage_.value()); } handlers_.clear(); } @@ -567,32 +597,35 @@ namespace promise_hpp resolve_t resolve_; reject_t reject_; - - template < typename ResolveF, typename RejectF > - handler(ResolveF&& resolve, RejectF&& reject) - : resolve_(std::forward(resolve)) - , reject_(std::forward(reject)) {} }; std::vector handlers_; detail::storage storage_; }; }; +} - // - // promise - // +// ----------------------------------------------------------------------------- +// +// promise +// +// ----------------------------------------------------------------------------- +namespace promise_hpp +{ template <> class promise final { public: using value_type = void; - public: + promise() : state_(std::make_shared()) {} - promise(const promise&) noexcept = default; - promise& operator=(const promise&) noexcept = default; + promise(promise&&) = default; + promise& operator=(promise&&) = default; + + promise(const promise&) = default; + promise& operator=(const promise&) = default; void swap(promise& other) noexcept { state_.swap(other.state_); @@ -614,48 +647,78 @@ namespace promise_hpp return l.state_ != r.state_; } - template < typename ResolveF - , typename ResolveFR = std::invoke_result_t > - std::enable_if_t< - is_promise::value && std::is_void::value, - promise> - then(ResolveF&& on_resolve) { - promise next; + // + // get + // - then([ - n = next, - f = std::forward(on_resolve) - ]() mutable { - auto np = std::invoke( - std::forward(f)); - std::move(np).then([n]() mutable { - n.resolve(); - }).except([n](std::exception_ptr e) mutable { - n.reject(e); - }); - }).except([n = next](std::exception_ptr e) mutable { - n.reject(e); - }); - - return next; + void get() const { + state_->get(); } + void get_or_default() const { + try { + return get(); + } catch (...) { + // nothing + } + } + + // + // wait + // + + void wait() const noexcept { + state_->wait(); + } + + template < typename Rep, typename Period > + promise_wait_status wait_for(const std::chrono::duration& timeout_duration) const { + return state_->wait_for(timeout_duration); + } + + template < typename Clock, typename Duration > + promise_wait_status wait_until(const std::chrono::time_point& timeout_time) const { + return state_->wait_until(timeout_time); + } + + // + // resolve/reject + // + + bool resolve() { + return state_->resolve(); + } + + 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))); + } + + // + // then + // + template < typename ResolveF - , typename ResolveFR = std::invoke_result_t > + , typename ResolveR = std::invoke_result_t > std::enable_if_t< - is_promise::value && !std::is_void::value, - promise> + is_promise_v, + promise> then(ResolveF&& on_resolve) { - promise next; + promise next; then([ n = next, f = std::forward(on_resolve) - ]() mutable { + ](auto&&... vs) mutable { auto np = std::invoke( - std::forward(f)); - std::move(np).then([n](auto&& nv) mutable { - n.resolve(std::forward(nv)); + std::forward(f), + std::forward(vs)...); + std::move(np).then([n](auto&&... nvs) mutable { + n.resolve(std::forward(nvs)...); }).except([n](std::exception_ptr e) mutable { n.reject(e); }); @@ -699,89 +762,55 @@ namespace promise_hpp }); } + // + // then + // + + template < typename ResolveF + , typename ResolveR = std::invoke_result_t > + std::enable_if_t< + !is_promise_v, + promise> + then(ResolveF&& on_resolve) { + promise next; + + state_->attach( + next, + std::forward(on_resolve), + [](std::exception_ptr e) -> ResolveR { std::rethrow_exception(e); }, + false); + + return next; + } + template < typename ResolveF , typename RejectF - , typename ResolveFR = std::invoke_result_t > + , typename ResolveR = std::invoke_result_t > std::enable_if_t< - !is_promise::value, - promise> + !is_promise_v, + promise> then(ResolveF&& on_resolve, RejectF&& on_reject) { - promise next; + promise next; + state_->attach( next, std::forward(on_resolve), std::forward(on_reject), true); + return next; } - template < typename ResolveF - , typename ResolveFR = std::invoke_result_t > - std::enable_if_t< - !is_promise::value, - promise> - then(ResolveF&& on_resolve) { - promise next; - state_->attach( - next, - std::forward(on_resolve), - [](std::exception_ptr e) -> ResolveFR { - std::rethrow_exception(e); - }, - false); - return next; - } + // + // except + // template < typename RejectF > promise except(RejectF&& on_reject) { return then( - []{}, + [](){}, std::forward(on_reject)); } - - bool resolve() { - return state_->resolve(); - } - - 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))); - } - - void get() const { - state_->get(); - } - - void get_or_default() const { - try { - return get(); - } catch (...) { - // nothing - } - } - - void wait() const noexcept { - state_->wait(); - } - - template < typename Rep, typename Period > - promise_wait_status wait_for( - const std::chrono::duration& timeout_duration) const - { - return state_->wait_for(timeout_duration); - } - - template < typename Clock, typename Duration > - promise_wait_status wait_until( - const std::chrono::time_point& timeout_time) const - { - return state_->wait_until(timeout_time); - } private: class state; std::shared_ptr state_; @@ -790,31 +819,8 @@ namespace promise_hpp public: state() = default; - bool resolve() { - std::lock_guard guard(mutex_); - if ( status_ != status::pending ) { - return false; - } - status_ = status::resolved; - invoke_resolve_handlers_(); - cond_var_.notify_all(); - return true; - } - - bool reject(std::exception_ptr e) noexcept { - std::lock_guard guard(mutex_); - if ( status_ != status::pending ) { - return false; - } - exception_ = e; - status_ = status::rejected; - invoke_reject_handlers_(); - cond_var_.notify_all(); - return true; - } - void get() { - std::unique_lock lock(mutex_); + std::unique_lock lock(mutex_); cond_var_.wait(lock, [this](){ return status_ != status::pending; }); @@ -825,42 +831,54 @@ namespace promise_hpp } void wait() const noexcept { - std::unique_lock lock(mutex_); + std::unique_lock lock(mutex_); cond_var_.wait(lock, [this](){ return status_ != status::pending; }); } template < typename Rep, typename Period > - promise_wait_status wait_for( - const std::chrono::duration& timeout_duration) const - { - std::unique_lock lock(mutex_); + promise_wait_status wait_for(const std::chrono::duration& timeout_duration) const { + std::unique_lock lock(mutex_); return cond_var_.wait_for(lock, timeout_duration, [this](){ return status_ != status::pending; - }) ? promise_wait_status::no_timeout - : promise_wait_status::timeout; + }) ? promise_wait_status::no_timeout : promise_wait_status::timeout; } template < typename Clock, typename Duration > - promise_wait_status wait_until( - const std::chrono::time_point& timeout_time) const - { - std::unique_lock lock(mutex_); + promise_wait_status wait_until(const std::chrono::time_point& timeout_time) const { + std::unique_lock lock(mutex_); return cond_var_.wait_until(lock, timeout_time, [this](){ return status_ != status::pending; - }) ? promise_wait_status::no_timeout - : promise_wait_status::timeout; + }) ? promise_wait_status::no_timeout : promise_wait_status::timeout; } + bool resolve() { + std::lock_guard guard(mutex_); + if ( status_ != status::pending ) { + return false; + } + status_ = status::resolved; + invoke_resolve_handlers_(); + cond_var_.notify_all(); + return true; + } + + bool reject(std::exception_ptr e) noexcept { + std::lock_guard guard(mutex_); + if ( status_ != status::pending ) { + return false; + } + exception_ = e; + status_ = status::rejected; + invoke_reject_handlers_(); + cond_var_.notify_all(); + return true; + } + public: template < typename U, typename ResolveF, typename RejectF > std::enable_if_t::value, void> - attach( - promise& next, - ResolveF&& on_resolve, - RejectF&& on_reject, - bool has_reject) - { + attach(promise& next, ResolveF&& on_resolve, RejectF&& on_reject, bool has_reject) { auto reject_h = [ n = next, f = std::forward(on_reject), @@ -893,18 +911,13 @@ namespace promise_hpp } }; - std::lock_guard guard(mutex_); + 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&& on_resolve, - RejectF&& on_reject, - bool has_reject) - { + attach(promise& next, ResolveF&& on_resolve, RejectF&& on_reject, bool has_reject) { auto reject_h = [ n = next, f = std::forward(on_reject), @@ -937,7 +950,7 @@ namespace promise_hpp } }; - std::lock_guard guard(mutex_); + std::lock_guard guard(mutex_); add_handlers_(std::move(resolve_h), std::move(reject_h)); } private: @@ -951,9 +964,9 @@ namespace promise_hpp std::forward(reject), exception_); } else { - handlers_.emplace_back( + handlers_.push_back({ std::forward(resolve), - std::forward(reject)); + std::forward(reject)}); } } @@ -989,17 +1002,15 @@ namespace promise_hpp resolve_t resolve_; reject_t reject_; - - template < typename ResolveF, typename RejectF > - handler(ResolveF&& resolve, RejectF&& reject) - : resolve_(std::forward(resolve)) - , reject_(std::forward(reject)) {} }; std::vector handlers_; }; }; +} +namespace promise_hpp +{ // // swap // @@ -1099,7 +1110,7 @@ namespace promise_hpp std::vector ret; ret.reserve(results_.size()); for ( auto&& v : results_ ) { - ret.emplace_back(v.get()); + ret.push_back(v.get()); } return ret; } @@ -1209,7 +1220,7 @@ namespace promise_hpp private: template < std::size_t... Is > std::tuple get_results_impl(std::index_sequence) { - return std::make_tuple(std::get(results_).get()...); + return {std::get(results_).get()...}; } private: std::atomic_size_t counter_{0}; @@ -1291,7 +1302,7 @@ namespace promise_hpp make_tuple_promise(Tuple&& tuple) { return impl::make_tuple_promise_impl( std::forward(tuple), - std::make_index_sequence::value>()); + std::make_index_sequence>()); } } diff --git a/untests/promise_tests.cpp b/untests/promise_tests.cpp index 0b38f0c..59df20a 100644 --- a/untests/promise_tests.cpp +++ b/untests/promise_tests.cpp @@ -202,6 +202,30 @@ TEST_CASE("promise") { REQUIRE(check_100500_transform == 100500); } } + SECTION("resolved_ref") { + { + int* check_42_int = nullptr; + auto p = pr::promise(); + int i = 42; + p.resolve(i); + p.then([&check_42_int](int& value) mutable { + check_42_int = &value; + }); + REQUIRE(check_42_int); + REQUIRE(*check_42_int == 42); + } + { + const int* check_42_int = nullptr; + auto p = pr::promise(); + const int i = 42; + p.resolve(i); + p.then([&check_42_int](const int& value) mutable { + check_42_int = &value; + }); + REQUIRE(check_42_int); + REQUIRE(*check_42_int == 42); + } + } SECTION("rejected") { { bool call_fail_with_logic_error = false; @@ -867,6 +891,21 @@ TEST_CASE("promise") { return std::make_tuple(std::move(p1), std::move(p2)); }); } + { + auto p1 = pr::promise(); + auto p2 = pr::promise(); + auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2)); + + int i = 10; + float f = 0.f; + p1.resolve(i); + p2.resolve(f); + + p3.then([&i,&f](const std::tuple& t){ + REQUIRE(&std::get<0>(t) == &i); + REQUIRE(&std::get<1>(t) == &f); + }); + } } SECTION("make_all_promise_fail") { {