Skip to content

Commit

Permalink
Support variant direct storage and inline operation state
Browse files Browse the repository at this point in the history
  • Loading branch information
alandefreitas committed Mar 11, 2022
1 parent e6b9f89 commit 97f05b0
Show file tree
Hide file tree
Showing 21 changed files with 2,770 additions and 780 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:
- name: Archive Single-Header as is
uses: kittaakos/upload-artifact-as-is@v0
with:
path: build/source/single-header/futures/futures.h
path: build/include/single-header/futures/futures.hpp
- name: Archive Installer Packages as is
uses: kittaakos/upload-artifact-as-is@v0
with:
Expand Down
18 changes: 9 additions & 9 deletions docs/adaptors/continuations.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,18 +197,18 @@ The following table describes all unwrapping functions by their priority:

| Future output | Continuation input | Inputs |
|---------------------------------------------------------------|------------------------------------------------|--------|
| `future<T>` | `future<T>` | 1 |
| `future<T>` | `` | 0 |
| `future<T>` | `T` | 1 |
| `future<R>` | `future<R>` | 1 |
| `future<R>` | `` | 0 |
| `future<R>` | `R` | 1 |
| `future<tuple<future<T1>, future<T2>, ...>>` | `future<T1>`, `future<T2>` ... | N |
| `future<tuple<future<T1>, future<T2>, ...>>` | `T1`, `T2` ... | N |
| `future<vector<future<T>>>` | `vector<T>` | 1 |
| `future<vector<future<R>>>` | `vector<R>` | 1 |
| `future<when_any_result<tuple<future<T1>, future<T2>, ...>>>` | `size_t`, `tuple<future<T1>, future<T2>, ...>` | 2 |
| `future<when_any_result<tuple<future<T1>, future<T2>, ...>>>` | `size_t`, `future<T1>`, `future<T2>`, ... | N + 1 |
| `future<when_any_result<tuple<future<T>, future<T>, ...>>>` | `future<T>` | 1 |
| `future<when_any_result<vector<future<T>>>>` | `future<T>` | 1 |
| `future<when_any_result<tuple<future<T>, future<T>, ...>>>` | `T` | 1 |
| `future<when_any_result<vector<future<T>>>>` | `T` | 1 |
| `future<when_any_result<tuple<future<R>, future<R>, ...>>>` | `future<R>` | 1 |
| `future<when_any_result<vector<future<R>>>>` | `future<R>` | 1 |
| `future<when_any_result<tuple<future<R>, future<R>, ...>>>` | `R` | 1 |
| `future<when_any_result<vector<future<R>>>>` | `R` | 1 |

Note that types are very important here. Whenever the continuation has the same number of arguments for the same future
output, a template function or a lambda using `auto` would be ambiguous.
Expand All @@ -219,7 +219,7 @@ In this case, the continuation function will attempt to use the unwrapping with
be `cfuture<int>`. However, this is not always possible if the unwrapping overloads are ambiguous enough.

The continuation with the highest priority is always the safer and usually more verbose continuation. This means a
template continuation will usually unwrap to `future<T>` over `T` continuation input variants. On the other hand, this
template continuation will usually unwrap to `future<R>` over `R` continuation input variants. On the other hand, this
is also useful since the most verbose continuation patterns are the ones that could benefit the most from `auto`.

## Return type unwrapping
Expand Down
62 changes: 62 additions & 0 deletions include/futures/adaptor/detail/make_ready_future.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// Copyright (c) 2021 alandefreitas ([email protected])
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//

#ifndef FUTURES_ADAPTOR_DETAIL_MAKE_READY_FUTURE_HPP
#define FUTURES_ADAPTOR_DETAIL_MAKE_READY_FUTURE_HPP

#include <futures/futures/basic_future.hpp>
#include <futures/futures/promise.hpp>
#include <futures/futures/traits/is_future.hpp>
#include <future>

namespace futures::detail {
struct make_ready_future_impl
{
template <typename T>
basic_future<typename std::decay_t<T>, future_options<>>
make_ready_future(T &&value) {
basic_future<std::decay_t<T>, future_options<>> result(
std::forward<T>(value));
return result;
}

template <typename T>
basic_future<T &, future_options<>>
make_ready_future(std::reference_wrapper<T> value) {
promise<T &, future_options<>> p;
basic_future<T &, future_options<>> result = p.get_future();
p.set_value(value);
return result;
}

basic_future<void, future_options<>>
make_ready_future() {
promise<void, future_options<>> p;
basic_future<void, future_options<>> result = p.get_future();
p.set_value();
return result;
}

template <typename T = void>
basic_future<T, future_options<>>
make_exceptional_future(std::exception_ptr ex) {
promise<T, future_options<>> p;
p.set_exception(ex);
return p.get_future();
}

template <class T = void, class E>
basic_future<T, future_options<>>
make_exceptional_future(E ex) {
promise<T, future_options<>> p;
p.set_exception(std::make_exception_ptr(ex));
return p.get_future();
}
};
} // namespace futures::detail

#endif // FUTURES_ADAPTOR_DETAIL_MAKE_READY_FUTURE_HPP
85 changes: 50 additions & 35 deletions include/futures/adaptor/detail/unwrap_and_continue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <futures/futures/future_options.hpp>
#include <futures/adaptor/detail/unwrap_and_continue_traits.hpp>
#include <futures/detail/algorithm/tuple_algorithm.hpp>
#include <futures/detail/container/small_vector.hpp>
#include <futures/detail/traits/is_single_type_tuple.hpp>
#include <futures/detail/traits/is_tuple_invocable.hpp>
#include <futures/detail/traits/is_when_any_result.hpp>
Expand Down Expand Up @@ -207,20 +208,24 @@ namespace futures::detail {
std::tuple<PrefixArgs...>,
unwrapped_elements>>;
if constexpr (tuple_explode_unwrap) {
return transform_and_apply(
continuation,
[](auto &&el) {
auto future_to_value = [](auto &&el) {
if constexpr (!is_future_v<std::decay_t<decltype(el)>>)
{
return el;
} else {
return el.get();
}
},
};
auto prefix_as_tuple = std::make_tuple(
std::forward<PrefixArgs>(prefix_args)...);
auto futures_tuple = before_future.get();
// transform each tuple with future_to_value
return transform_and_apply(
continuation,
future_to_value,
std::tuple_cat(
std::make_tuple(
std::forward<PrefixArgs>(prefix_args)...),
before_future.get()));
prefix_as_tuple,
std::move(futures_tuple)));
} else {
detail::throw_exception<std::logic_error>(
"Continuation unwrapping not possible");
Expand Down Expand Up @@ -537,26 +542,24 @@ namespace futures::detail {
template <class Executor, class Function, class Future>
struct continuation_traits_helper
{
// The return type of unwrap and continue function
// The possible return types of unwrap and continue function
using unwrap_result = result_of_unwrap_t<Future, Function>;

using unwrap_result_with_token_prefix
= result_of_unwrap_with_token_t<Future, Function>;

// Whether the continuation expects a token
static constexpr bool is_valid_without_stop_token
= !std::is_same_v<unwrap_result, unwrapping_failure_t>;

static constexpr bool is_valid_with_stop_token = !std::is_same_v<
unwrap_result_with_token_prefix,
unwrapping_failure_t>;

// Whether the continuation is valid
// Whether the continuation is valid at all
static constexpr bool is_valid = is_valid_without_stop_token
|| is_valid_with_stop_token;

// The result type of unwrap and continue for the valid version, with or
// without token
// The result type of unwrap and continue for the valid overload
// (with or without the token)
using next_value_type = std::conditional_t<
is_valid_with_stop_token,
unwrap_result_with_token_prefix,
Expand All @@ -565,7 +568,7 @@ namespace futures::detail {
// Stop token for the continuation function
constexpr static bool expects_stop_token = is_valid_with_stop_token;

// Check if the stop token should be inherited from previous future
// Check if the stop token can be inherited from to next future
constexpr static bool previous_future_has_stop_token = has_stop_token_v<
Future>;
constexpr static bool previous_future_is_shared = is_shared_future_v<
Expand All @@ -574,33 +577,45 @@ namespace futures::detail {
= previous_future_has_stop_token && (!previous_future_is_shared);

// Continuation future should have stop token
// note: this is separate from `expects_stop_token` because (in the
// future), the continuation might reuse the stop source without
// actually containing a function that expects the token.
constexpr static bool after_has_stop_token = expects_stop_token;

// The result type of unwrap and continue for the valid version, with or
// without token
using base_future_options = std::conditional_t<
// The result type of unwrap and continue for the valid unwrap overload
// (with or without token)

// Next needs to inherit the constructor from previous future
// Next needs continuation source if previous is eager
using next_maybe_continuable_future_options = std::conditional_t<
!is_always_deferred_v<Future>,
future_options<executor_opt<Executor>, continuable_opt>,
future_options<executor_opt<Executor>>>;

using eager_future_options = conditional_append_future_option_t<
after_has_stop_token,
stoppable_opt,
base_future_options>;

using maybe_deferred_future_options = conditional_append_future_option_t<
is_always_deferred_v<Future>,
always_deferred_opt,
eager_future_options>;

using maybe_function_type_future_options = conditional_append_future_option_t<
is_always_deferred_v<Future>,
deferred_function_opt<detail::unwrap_and_continue_task<Future, Function>>,
maybe_deferred_future_options>;

// The result type of unwrap and continue for the valid version, with or
// without token
using next_future_options = maybe_function_type_future_options;
// Next is stoppable if we identified the function expects a token
using next_maybe_stoppable_future_options
= conditional_append_future_option_t<
after_has_stop_token,
stoppable_opt,
next_maybe_continuable_future_options>;

// Next needs the always_deferred_opt if it's deferred
using next_maybe_deferred_future_options
= conditional_append_future_option_t<
is_always_deferred_v<Future>,
always_deferred_opt,
next_maybe_stoppable_future_options>;

// Next needs the continuation function type if it's deferred
using next_maybe_function_type_future_options
= conditional_append_future_option_t<
is_always_deferred_v<Future>,
deferred_function_opt<
detail::unwrap_and_continue_task<Future, Function>>,
next_maybe_deferred_future_options>;

// The result options type of unwrap and continue
using next_future_options = next_maybe_function_type_future_options;
};

template <class Executor, class Function, class Future>
Expand Down
33 changes: 11 additions & 22 deletions include/futures/adaptor/make_ready_future.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
#ifndef FUTURES_ADAPTOR_MAKE_READY_FUTURE_HPP
#define FUTURES_ADAPTOR_MAKE_READY_FUTURE_HPP

#include <futures/futures/basic_future.hpp>
#include <futures/futures/promise.hpp>
#include <futures/futures/traits/is_future.hpp>
#include <future>
#include <futures/adaptor/detail/make_ready_future.hpp>

namespace futures {
/** @addtogroup adaptors Adaptors
Expand All @@ -26,10 +23,8 @@ namespace futures {
template <typename T>
basic_future<typename std::decay_t<T>, future_options<>>
make_ready_future(T &&value) {
promise<std::decay_t<T>, future_options<>> p;
basic_future<std::decay_t<T>, future_options<>> result = p.get_future();
p.set_value(std::forward<T>(value));
return result;
return detail::make_ready_future_impl{}.template make_ready_future<T>(
std::forward<T>(value));
}

/// Make a placeholder future object that is ready from a reference
Expand All @@ -40,10 +35,8 @@ namespace futures {
template <typename T>
basic_future<T &, future_options<>>
make_ready_future(std::reference_wrapper<T> value) {
promise<T &, future_options<>> p;
basic_future<T &, future_options<>> result = p.get_future();
p.set_value(value);
return result;
return detail::make_ready_future_impl{}.template make_ready_future<T>(
value);
}

/// Make a placeholder void future object that is ready
Expand All @@ -53,10 +46,7 @@ namespace futures {
/// @return A future associated with the shared state that is created.
inline basic_future<void, future_options<>>
make_ready_future() {
promise<void, future_options<>> p;
basic_future<void, future_options<>> result = p.get_future();
p.set_value();
return result;
return detail::make_ready_future_impl{}.make_ready_future();
}

/// Make a placeholder future object that is ready with an exception
Expand All @@ -69,9 +59,8 @@ namespace futures {
template <typename T = void>
basic_future<T, future_options<>>
make_exceptional_future(std::exception_ptr ex) {
promise<T, future_options<>> p;
p.set_exception(ex);
return p.get_future();
return detail::make_ready_future_impl{}
.template make_exceptional_future<T>(ex);
}

/// Make a placeholder future object that is ready with from any
Expand All @@ -84,10 +73,10 @@ namespace futures {
template <class T = void, class E>
basic_future<T, future_options<>>
make_exceptional_future(E ex) {
promise<T, future_options<>> p;
p.set_exception(std::make_exception_ptr(ex));
return p.get_future();
return detail::make_ready_future_impl{}
.template make_exceptional_future<T, E>(ex);
}

/** @} */
} // namespace futures

Expand Down
16 changes: 8 additions & 8 deletions include/futures/detail/container/small_vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ namespace futures::detail {
template <
class T,
size_t N
= std::max(std::size_t(5), (sizeof(T *) + sizeof(size_t)) / sizeof(T)),
= (std::max)(std::size_t(5), (sizeof(T *) + sizeof(size_t)) / sizeof(T)),
class Allocator = std::allocator<T>,
class AllowHeap = std::true_type,
class SizeType = size_t,
Expand Down Expand Up @@ -1426,7 +1426,7 @@ namespace futures::detail {
const auto old_capacity = capacity();
// Set the initial capacity
if (old_capacity == 0) {
return std::max(64 / sizeof(value_type), size_type(5));
return (std::max)(64 / sizeof(value_type), size_type(5));
}
// Blocks larger than or equal to 4096 bytes can be expanded in place
constexpr size_t min_in_place_expansion = 4096;
Expand Down Expand Up @@ -1791,8 +1791,8 @@ namespace futures::detail {
template <
class T,
size_t N_INPUT,
size_t N_OUTPUT = std::max(
std::max(std::size_t(5), (sizeof(T *) + sizeof(size_t)) / sizeof(T)),
size_t N_OUTPUT = (std::max)(
(std::max)(std::size_t(5), (sizeof(T *) + sizeof(size_t)) / sizeof(T)),
N_INPUT)>
constexpr small_vector<std::remove_cv_t<T>, N_OUTPUT>
to_small_vector(T (&a)[N_INPUT]) {
Expand All @@ -1804,8 +1804,8 @@ namespace futures::detail {
template <
class T,
size_t N_INPUT,
size_t N_OUTPUT = std::max(
std::max(std::size_t(5), (sizeof(T *) + sizeof(size_t)) / sizeof(T)),
size_t N_OUTPUT = (std::max)(
(std::max)(std::size_t(5), (sizeof(T *) + sizeof(size_t)) / sizeof(T)),
N_INPUT)>
constexpr small_vector<std::remove_cv_t<T>, N_OUTPUT>
to_small_vector(T(&&a)[N_INPUT]) {
Expand All @@ -1815,7 +1815,7 @@ namespace futures::detail {
template <
class T,
size_t N
= std::max((sizeof(std::vector<T>) * 2) / sizeof(T), std::size_t(5)),
= (std::max)((sizeof(std::vector<T>) * 2) / sizeof(T), std::size_t(5)),
class Allocator = std::allocator<T>,
class SizeType = size_t>
using max_size_small_vector
Expand All @@ -1824,7 +1824,7 @@ namespace futures::detail {
template <
class T,
size_t N
= std::max((sizeof(std::vector<T>) * 2) / sizeof(T), std::size_t(5)),
= (std::max)((sizeof(std::vector<T>) * 2) / sizeof(T), std::size_t(5)),
class Allocator = std::allocator<T>,
class SizeType = size_t>
using inline_small_vector
Expand Down
Loading

0 comments on commit 97f05b0

Please sign in to comment.