mirror of
https://github.com/BlackMATov/promise.hpp.git
synced 2025-12-13 03:46:29 +07:00
make_tuple_promise, then_tuple
This commit is contained in:
@@ -6,6 +6,10 @@ find_package(Threads REQUIRED)
|
|||||||
file(GLOB test_sources "*.cpp" "*.hpp")
|
file(GLOB test_sources "*.cpp" "*.hpp")
|
||||||
add_executable(${PROJECT_NAME} ${test_sources})
|
add_executable(${PROJECT_NAME} ${test_sources})
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE /bigobj)
|
||||||
|
endif(MSVC)
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME}
|
target_link_libraries(${PROJECT_NAME}
|
||||||
Threads::Threads)
|
Threads::Threads)
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
[promise.hpp][promise] is a header only library. All you need to do is copy the header files (invoke.hpp and promise.hpp) into your project and include this file:
|
[promise.hpp][promise] is a header only library. All you need to do is copy the header files (`invoke.hpp` and `promise.hpp`) into your project and include this file:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include "promise.hpp"
|
#include "promise.hpp"
|
||||||
|
|||||||
@@ -329,6 +329,7 @@ namespace jobber_hpp
|
|||||||
if ( task ) {
|
if ( task ) {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
task->run();
|
task->run();
|
||||||
|
lock.lock();
|
||||||
--active_task_count_;
|
--active_task_count_;
|
||||||
cond_var_.notify_all();
|
cond_var_.notify_all();
|
||||||
}
|
}
|
||||||
|
|||||||
234
promise.hpp
234
promise.hpp
@@ -10,6 +10,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <tuple>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -92,19 +93,30 @@ namespace promise_hpp
|
|||||||
public:
|
public:
|
||||||
storage() = default;
|
storage() = default;
|
||||||
|
|
||||||
~storage() noexcept(std::is_nothrow_destructible<T>::value) {
|
~storage()
|
||||||
|
noexcept(std::is_nothrow_destructible<T>::value)
|
||||||
|
{
|
||||||
if ( initialized_ ) {
|
if ( initialized_ ) {
|
||||||
ptr_()->~T();
|
ptr_()->~T();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename U >
|
template < typename U >
|
||||||
void set(U&& value) noexcept(std::is_nothrow_constructible<T,U&&>::value) {
|
void set(U&& value)
|
||||||
|
noexcept(std::is_nothrow_constructible<T,U&&>::value)
|
||||||
|
{
|
||||||
assert(!initialized_);
|
assert(!initialized_);
|
||||||
::new(ptr_()) T(std::forward<U>(value));
|
::new(ptr_()) T(std::forward<U>(value));
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T get()
|
||||||
|
noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||||
|
{
|
||||||
|
assert(initialized_);
|
||||||
|
return std::move(*ptr_());
|
||||||
|
}
|
||||||
|
|
||||||
const T& value() const noexcept {
|
const T& value() const noexcept {
|
||||||
assert(initialized_);
|
assert(initialized_);
|
||||||
return *ptr_();
|
return *ptr_();
|
||||||
@@ -245,6 +257,18 @@ namespace promise_hpp
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template < typename ResolveF >
|
||||||
|
auto then_tuple(ResolveF&& on_resolve) {
|
||||||
|
return then([
|
||||||
|
f = std::forward<ResolveF>(on_resolve)
|
||||||
|
](auto&& v) mutable {
|
||||||
|
auto r = invoke_hpp::invoke(
|
||||||
|
std::forward<decltype(f)>(f),
|
||||||
|
std::forward<decltype(v)>(v));
|
||||||
|
return make_tuple_promise(std::move(r));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template < typename ResolveF
|
template < typename ResolveF
|
||||||
, typename RejectF
|
, typename RejectF
|
||||||
, typename ResolveFR = invoke_hpp::invoke_result_t<ResolveF,T> >
|
, typename ResolveFR = invoke_hpp::invoke_result_t<ResolveF,T> >
|
||||||
@@ -637,6 +661,17 @@ namespace promise_hpp
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template < typename ResolveF >
|
||||||
|
auto then_tuple(ResolveF&& on_resolve) {
|
||||||
|
return then([
|
||||||
|
f = std::forward<ResolveF>(on_resolve)
|
||||||
|
]() mutable {
|
||||||
|
auto r = invoke_hpp::invoke(
|
||||||
|
std::forward<decltype(f)>(f));
|
||||||
|
return make_tuple_promise(std::move(r));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template < typename ResolveF
|
template < typename ResolveF
|
||||||
, typename RejectF
|
, typename RejectF
|
||||||
, typename ResolveFR = invoke_hpp::invoke_result_t<ResolveF> >
|
, typename ResolveFR = invoke_hpp::invoke_result_t<ResolveF> >
|
||||||
@@ -992,40 +1027,55 @@ namespace promise_hpp
|
|||||||
// make_all_promise
|
// make_all_promise
|
||||||
//
|
//
|
||||||
|
|
||||||
template < typename Iter >
|
namespace impl
|
||||||
auto make_all_promise(Iter begin, Iter end) {
|
{
|
||||||
using child_promise_t = typename Iter::value_type;
|
template < typename ResultType >
|
||||||
using child_promise_value_t = typename child_promise_t::value_type;
|
class all_promise_context_t final : private detail::noncopyable {
|
||||||
using promise_out_container_t = std::vector<child_promise_value_t>;
|
public:
|
||||||
|
all_promise_context_t(std::size_t count)
|
||||||
|
: results_(count) {}
|
||||||
|
|
||||||
struct context_t {
|
template < typename T >
|
||||||
promise_out_container_t results;
|
bool apply_result(std::size_t index, T&& value) {
|
||||||
std::atomic_size_t counter = ATOMIC_VAR_INIT(0);
|
results_[index].set(std::forward<T>(value));
|
||||||
|
return ++counter_ == results_.size();
|
||||||
context_t(std::size_t count)
|
|
||||||
: results(count) {}
|
|
||||||
|
|
||||||
bool apply_result(std::size_t index, const child_promise_value_t& value) {
|
|
||||||
results[index] = value;
|
|
||||||
return ++counter == results.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<ResultType> get_results() {
|
||||||
|
std::vector<ResultType> ret;
|
||||||
|
ret.reserve(results_.size());
|
||||||
|
for ( auto&& v : results_ ) {
|
||||||
|
ret.emplace_back(v.get());
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::atomic_size_t counter_{0};
|
||||||
|
std::vector<detail::storage<ResultType>> results_;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename Iter
|
||||||
|
, typename SubPromise = typename Iter::value_type
|
||||||
|
, typename SubPromiseResult = typename SubPromise::value_type
|
||||||
|
, typename ResultPromiseValueType = std::vector<SubPromiseResult> >
|
||||||
|
promise<ResultPromiseValueType>
|
||||||
|
make_all_promise(Iter begin, Iter end) {
|
||||||
if ( begin == end ) {
|
if ( begin == end ) {
|
||||||
return make_resolved_promise(promise_out_container_t());
|
return make_resolved_promise(ResultPromiseValueType());
|
||||||
}
|
}
|
||||||
|
return make_promise<ResultPromiseValueType>([begin, end](auto&& resolver, auto&& rejector){
|
||||||
return make_promise<promise_out_container_t>([begin, end](auto&& resolver, auto&& rejector){
|
|
||||||
std::size_t result_index = 0;
|
std::size_t result_index = 0;
|
||||||
auto context = std::make_shared<context_t>(std::distance(begin, end));
|
auto context = std::make_shared<impl::all_promise_context_t<
|
||||||
for ( auto iter = begin; iter != end; ++iter, ++result_index ) {
|
SubPromiseResult>>(std::distance(begin, end));
|
||||||
|
for ( Iter iter = begin; iter != end; ++iter, ++result_index ) {
|
||||||
(*iter).then([
|
(*iter).then([
|
||||||
context,
|
context,
|
||||||
resolver,
|
resolver,
|
||||||
result_index
|
result_index
|
||||||
](auto&& v) mutable {
|
](auto&& v) mutable {
|
||||||
if ( context->apply_result(result_index, std::forward<decltype(v)>(v)) ) {
|
if ( context->apply_result(result_index, std::forward<decltype(v)>(v)) ) {
|
||||||
resolver(std::move(context->results));
|
resolver(context->get_results());
|
||||||
}
|
}
|
||||||
}).except(rejector);
|
}).except(rejector);
|
||||||
}
|
}
|
||||||
@@ -1043,18 +1093,18 @@ namespace promise_hpp
|
|||||||
// make_any_promise
|
// make_any_promise
|
||||||
//
|
//
|
||||||
|
|
||||||
template < typename Iter >
|
template < typename Iter
|
||||||
|
, typename SubPromise = typename Iter::value_type
|
||||||
|
, typename SubPromiseResult = typename SubPromise::value_type >
|
||||||
auto make_any_promise(Iter begin, Iter end) {
|
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 ) {
|
if ( begin == end ) {
|
||||||
throw std::logic_error("at least one input promise must be provided for make_any_promise");
|
throw std::logic_error("at least one input promise must be provided for make_any_promise");
|
||||||
}
|
}
|
||||||
|
return make_promise<SubPromiseResult>([begin, end](auto&& resolver, auto&& rejector){
|
||||||
return make_promise<child_promise_value_t>([begin, end](auto&& resolver, auto&& rejector){
|
for ( Iter iter = begin; iter != end; ++iter ) {
|
||||||
for ( auto iter = begin; iter != end; ++iter ) {
|
(*iter)
|
||||||
(*iter).then(resolver).except(rejector);
|
.then(resolver)
|
||||||
|
.except(rejector);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1065,6 +1115,128 @@ namespace promise_hpp
|
|||||||
std::begin(container),
|
std::begin(container),
|
||||||
std::end(container));
|
std::end(container));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// make_tuple_promise
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
template < typename Tuple >
|
||||||
|
struct tuple_promise_result_impl {};
|
||||||
|
|
||||||
|
template < typename... Args >
|
||||||
|
struct tuple_promise_result_impl<std::tuple<promise<Args>...>> {
|
||||||
|
using type = std::tuple<Args...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename Tuple >
|
||||||
|
struct tuple_promise_result {
|
||||||
|
using type = typename tuple_promise_result_impl<std::remove_cv_t<Tuple>>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename Tuple >
|
||||||
|
using tuple_promise_result_t = typename tuple_promise_result<Tuple>::type;
|
||||||
|
|
||||||
|
template < typename... ResultTypes >
|
||||||
|
class tuple_promise_context_t {
|
||||||
|
public:
|
||||||
|
template < std::size_t N, typename T >
|
||||||
|
bool apply_result(T&& value) {
|
||||||
|
std::get<N>(results_).set(std::forward<T>(value));
|
||||||
|
return ++counter_ == sizeof...(ResultTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<ResultTypes...> get_results() {
|
||||||
|
return get_results_impl(
|
||||||
|
std::make_index_sequence<sizeof...(ResultTypes)>());
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
template < std::size_t... Is >
|
||||||
|
std::tuple<ResultTypes...> get_results_impl(std::index_sequence<Is...>) {
|
||||||
|
return std::make_tuple(std::get<Is>(results_).get()...);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::atomic_size_t counter_{0};
|
||||||
|
std::tuple<detail::storage<ResultTypes>...> results_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename... ResultTypes >
|
||||||
|
using tuple_promise_context_ptr = std::shared_ptr<
|
||||||
|
tuple_promise_context_t<ResultTypes...>>;
|
||||||
|
|
||||||
|
template < std::size_t I
|
||||||
|
, typename Tuple
|
||||||
|
, typename Resolver
|
||||||
|
, typename Rejector
|
||||||
|
, typename... ResultTypes >
|
||||||
|
promise<void> make_tuple_sub_promise_impl(
|
||||||
|
Tuple&& tuple,
|
||||||
|
Resolver&& resolver,
|
||||||
|
Rejector&& rejector,
|
||||||
|
const tuple_promise_context_ptr<ResultTypes...>& context)
|
||||||
|
{
|
||||||
|
return std::get<I>(tuple).then([
|
||||||
|
context,
|
||||||
|
resolver
|
||||||
|
](auto&& v) mutable {
|
||||||
|
if (context->template apply_result<I>(std::forward<decltype(v)>(v))) {
|
||||||
|
resolver(context->get_results());
|
||||||
|
}
|
||||||
|
}).except(rejector);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename Tuple
|
||||||
|
, std::size_t... Is
|
||||||
|
, typename ResultTuple = tuple_promise_result_t<std::decay_t<Tuple>> >
|
||||||
|
std::enable_if_t<
|
||||||
|
sizeof...(Is) == 0,
|
||||||
|
promise<ResultTuple>>
|
||||||
|
make_tuple_promise_impl(Tuple&&, std::index_sequence<Is...>) {
|
||||||
|
return make_resolved_promise(ResultTuple());
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename Tuple
|
||||||
|
, std::size_t... Is
|
||||||
|
, typename ResultTuple = tuple_promise_result_t<std::decay_t<Tuple>> >
|
||||||
|
std::enable_if_t<
|
||||||
|
sizeof...(Is) != 0,
|
||||||
|
promise<ResultTuple>>
|
||||||
|
make_tuple_promise_impl(Tuple&& tuple, std::index_sequence<Is...>) {
|
||||||
|
auto result = promise<ResultTuple>();
|
||||||
|
|
||||||
|
auto resolver = [result](auto&& v) mutable {
|
||||||
|
return result.resolve(std::forward<decltype(v)>(v));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto rejector = [result](auto&& e) mutable {
|
||||||
|
return result.reject(std::forward<decltype(e)>(e));
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto context = std::make_shared<tuple_promise_context_t<
|
||||||
|
std::tuple_element_t<Is, ResultTuple>...>>();
|
||||||
|
std::make_tuple(make_tuple_sub_promise_impl<Is>(
|
||||||
|
tuple,
|
||||||
|
resolver,
|
||||||
|
rejector,
|
||||||
|
context)...);
|
||||||
|
} catch (...) {
|
||||||
|
result.reject(std::current_exception());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename Tuple
|
||||||
|
, typename ResultTuple = impl::tuple_promise_result_t<std::decay_t<Tuple>> >
|
||||||
|
promise<ResultTuple>
|
||||||
|
make_tuple_promise(Tuple&& tuple) {
|
||||||
|
return impl::make_tuple_promise_impl(
|
||||||
|
std::forward<Tuple>(tuple),
|
||||||
|
std::make_index_sequence<std::tuple_size<ResultTuple>::value>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
|
|||||||
@@ -29,16 +29,6 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class auto_thread final {
|
class auto_thread final {
|
||||||
public:
|
public:
|
||||||
template < typename F, typename... Args >
|
template < typename F, typename... Args >
|
||||||
@@ -709,6 +699,20 @@ TEST_CASE("promise") {
|
|||||||
|
|
||||||
REQUIRE(call_then_only_once == 1);
|
REQUIRE(call_then_only_once == 1);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
class o_t {
|
||||||
|
public:
|
||||||
|
o_t() = delete;
|
||||||
|
o_t(int) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
pr::promise<>()
|
||||||
|
.then_all([](){
|
||||||
|
return std::vector<pr::promise<o_t>>{
|
||||||
|
pr::make_resolved_promise<o_t>(40),
|
||||||
|
pr::make_resolved_promise<o_t>(2)};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SECTION("make_any_promise") {
|
SECTION("make_any_promise") {
|
||||||
{
|
{
|
||||||
@@ -751,6 +755,87 @@ TEST_CASE("promise") {
|
|||||||
REQUIRE(check_42_int == 42);
|
REQUIRE(check_42_int == 42);
|
||||||
REQUIRE(call_then_only_once == 1);
|
REQUIRE(call_then_only_once == 1);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
class o_t {
|
||||||
|
public:
|
||||||
|
o_t() = delete;
|
||||||
|
o_t(int) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
pr::promise<>()
|
||||||
|
.then_any([](){
|
||||||
|
return std::vector<pr::promise<o_t>>{
|
||||||
|
pr::make_resolved_promise<o_t>(40),
|
||||||
|
pr::make_resolved_promise<o_t>(2)};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SECTION("make_tuple_promise") {
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
std::is_same<
|
||||||
|
pr::impl::tuple_promise_result_t<std::tuple<>>,
|
||||||
|
std::tuple<>>::value,
|
||||||
|
"unit test fail");
|
||||||
|
static_assert(
|
||||||
|
std::is_same<
|
||||||
|
pr::impl::tuple_promise_result_t<std::tuple<pr::promise<int>>>,
|
||||||
|
std::tuple<int>>::value,
|
||||||
|
"unit test fail");
|
||||||
|
static_assert(
|
||||||
|
std::is_same<
|
||||||
|
pr::impl::tuple_promise_result_t<std::tuple<pr::promise<int>, pr::promise<float>>>,
|
||||||
|
std::tuple<int, float>>::value,
|
||||||
|
"unit test fail");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p = pr::make_tuple_promise(std::make_tuple());
|
||||||
|
REQUIRE(p.get() == std::make_tuple());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p1 = pr::promise<int>();
|
||||||
|
auto p2 = pr::make_tuple_promise(std::make_tuple(p1));
|
||||||
|
p1.resolve(42);
|
||||||
|
REQUIRE(p2.get_or_default(std::make_tuple(0)) == std::make_tuple(42));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p1 = pr::promise<int>();
|
||||||
|
auto t0 = std::make_tuple(p1);
|
||||||
|
auto p2 = pr::make_tuple_promise(t0);
|
||||||
|
p1.resolve(42);
|
||||||
|
REQUIRE(p2.get_or_default(std::make_tuple(0)) == std::make_tuple(42));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p1 = pr::promise<int>();
|
||||||
|
auto p2 = pr::promise<float>();
|
||||||
|
auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2));
|
||||||
|
p1.resolve(42);
|
||||||
|
p2.resolve(4.2f);
|
||||||
|
REQUIRE(p3.get_or_default(std::make_tuple(0, 0.f)) == std::make_tuple(42, 4.2f));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p1 = pr::promise<int>();
|
||||||
|
auto p2 = pr::promise<float>();
|
||||||
|
auto p3 = pr::promise<int>();
|
||||||
|
auto p4 = pr::make_tuple_promise(std::make_tuple(p1, p2, p3));
|
||||||
|
p1.resolve(42);
|
||||||
|
p2.resolve(4.2f);
|
||||||
|
p3.resolve(84);
|
||||||
|
REQUIRE(p4.get_or_default(std::make_tuple(0, 0.f, 0)) == std::make_tuple(42, 4.2f, 84));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
class o_t {
|
||||||
|
public:
|
||||||
|
o_t() = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
pr::promise<>()
|
||||||
|
.then_tuple([](){
|
||||||
|
auto p1 = pr::promise<o_t>();
|
||||||
|
auto p2 = pr::promise<o_t>();
|
||||||
|
return std::make_tuple(std::move(p1), std::move(p2));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SECTION("make_all_promise_fail") {
|
SECTION("make_all_promise_fail") {
|
||||||
{
|
{
|
||||||
@@ -810,6 +895,44 @@ TEST_CASE("promise") {
|
|||||||
REQUIRE(call_fail_with_logic_error);
|
REQUIRE(call_fail_with_logic_error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SECTION("make_tuple_promise_fail") {
|
||||||
|
{
|
||||||
|
auto p1 = pr::promise<int>();
|
||||||
|
auto p2 = pr::make_tuple_promise(std::make_tuple(p1));
|
||||||
|
p1.reject(std::logic_error("hello failt"));
|
||||||
|
REQUIRE_THROWS_AS(p2.get(), std::logic_error);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p1 = pr::promise<int>();
|
||||||
|
auto p2 = pr::promise<float>();
|
||||||
|
auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2));
|
||||||
|
p1.resolve(42);
|
||||||
|
p2.reject(std::logic_error("hello failt"));
|
||||||
|
REQUIRE_THROWS_AS(p3.get(), std::logic_error);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p1 = pr::promise<int>();
|
||||||
|
auto p2 = pr::promise<float>();
|
||||||
|
auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2));
|
||||||
|
p1.reject(std::logic_error("hello failt"));
|
||||||
|
p2.resolve(4.2f);
|
||||||
|
REQUIRE_THROWS_AS(p3.get(), std::logic_error);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p1 = pr::promise<int>();
|
||||||
|
auto p2 = pr::promise<float>();
|
||||||
|
auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2));
|
||||||
|
p1.reject(std::logic_error("hello failt"));
|
||||||
|
REQUIRE_THROWS_AS(p3.get(), std::logic_error);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto p1 = pr::promise<int>();
|
||||||
|
auto p2 = pr::promise<float>();
|
||||||
|
auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2));
|
||||||
|
p2.reject(std::logic_error("hello failt"));
|
||||||
|
REQUIRE_THROWS_AS(p3.get(), std::logic_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
SECTION("then_all") {
|
SECTION("then_all") {
|
||||||
{
|
{
|
||||||
int check_42_int = 0;
|
int check_42_int = 0;
|
||||||
@@ -887,6 +1010,44 @@ TEST_CASE("promise") {
|
|||||||
REQUIRE(call_then_only_once == 1);
|
REQUIRE(call_then_only_once == 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SECTION("then_tuple") {
|
||||||
|
{
|
||||||
|
float check_42_float = 0.f;
|
||||||
|
pr::make_resolved_promise()
|
||||||
|
.then_tuple([](){
|
||||||
|
return std::make_tuple(
|
||||||
|
pr::make_resolved_promise(32),
|
||||||
|
pr::make_resolved_promise(10.f));
|
||||||
|
}).then([&check_42_float](const std::tuple<int, float>& t){
|
||||||
|
check_42_float = std::get<0>(t) + std::get<1>(t);
|
||||||
|
});
|
||||||
|
REQUIRE(check_42_float == Approx(42.f).margin(0.01f));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
float check_42_float = 0.f;
|
||||||
|
pr::make_resolved_promise(42)
|
||||||
|
.then_tuple([](int){
|
||||||
|
return std::make_tuple(
|
||||||
|
pr::make_resolved_promise(32),
|
||||||
|
pr::make_resolved_promise(10.f));
|
||||||
|
}).then([&check_42_float](const std::tuple<int, float>& t){
|
||||||
|
check_42_float = std::get<0>(t) + std::get<1>(t);
|
||||||
|
});
|
||||||
|
REQUIRE(check_42_float == Approx(42.f).margin(0.01f));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
bool call_fail_with_logic_error = false;
|
||||||
|
pr::make_resolved_promise(42)
|
||||||
|
.then_tuple([](int){
|
||||||
|
return std::make_tuple(
|
||||||
|
pr::make_resolved_promise(32),
|
||||||
|
pr::make_rejected_promise<float>(std::logic_error("hello fail")));
|
||||||
|
}).except([&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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("get_and_wait") {
|
TEST_CASE("get_and_wait") {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
set SCRIPT_DIR=%~dp0%
|
set SCRIPT_DIR=%~dp0%
|
||||||
%SCRIPT_DIR%\build_debug.bat || goto :error
|
call %SCRIPT_DIR%\build_debug.bat || goto :error
|
||||||
%SCRIPT_DIR%\build_release.bat || goto :error
|
call %SCRIPT_DIR%\build_release.bat || goto :error
|
||||||
|
|
||||||
goto :EOF
|
goto :EOF
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user