Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions include/beman/execution/detail/completion_storage.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// include/beman/execution/detail/completion_storage.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION_STORAGE
#define INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION_STORAGE

#include <beman/execution/detail/common.hpp>
#include <beman/execution/detail/suppress_push.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
import std;
#else
#include <exception>
#include <tuple>
#include <type_traits>
#include <variant>
#endif
#ifdef BEMAN_HAS_MODULES
import beman.execution.detail.completion_signatures;
import beman.execution.detail.receiver_of;
import beman.execution.detail.receiver;
import beman.execution.detail.set_error;
import beman.execution.detail.unreachable;
#else
#include <beman/execution/detail/completion_signatures.hpp>
#include <beman/execution/detail/receiver_of.hpp>
#include <beman/execution/detail/receiver.hpp>
#include <beman/execution/detail/set_error.hpp>
#include <beman/execution/detail/unreachable.hpp>
#endif

// ----------------------------------------------------------------------------

namespace beman::execution::detail {

/// For a signature `Sig` of the form `Tag(Args...)`, compute the corresponding tuple type that can store the
/// completion.
template <typename Sig>
struct tuple_for_signature;

template <typename Tag, typename... Args>
struct tuple_for_signature<Tag(Args...)> {
/// The tuple type corresponding to the signature `Tag(Args...)`.
using type = ::std::tuple<Tag, ::std::remove_cvref_t<Args>...>;

/// Helper function to be used in overloaded scenario to select the correct tuple type from a set of arguments.
static auto select_tuple(Tag t, Args&&... args) -> type;
};

/// The variant storage for a set of completion signatures.
template <typename Signatures>
struct variant_for_signatures;

template <typename... Sigs>
struct variant_for_signatures<::beman::execution::completion_signatures<Sigs...>>
: ::beman::execution::detail::tuple_for_signature<Sigs>... {
/// The type of the variant that can store any of the completions in `Sigs...`.
using type =
::std::variant<std::monostate, typename ::beman::execution::detail::tuple_for_signature<Sigs>::type...>;

/// Stores in `storage` the completion with tag `t` and arguments `args...`.
///
/// If the construction of the tuple throws, it will store a `set_error` completion with the current exception.
template <typename Tag, typename... Args>
static auto store_into_variant(type& storage, Tag t, Args&&... args) noexcept {
using tuple_type = decltype(select_tuple(t, ::std::forward<Args>(args)...));
if constexpr (std::is_nothrow_constructible_v<tuple_type, Tag, Args&&...>) {
storage.template emplace<tuple_type>(t, ::std::forward<Args>(args)...);
} else {
try {
storage.template emplace<tuple_type>(t, ::std::forward<Args>(args)...);
} catch (...) {
store_into_variant(storage, ::beman::execution::set_error_t{}, ::std::current_exception());
}
}
}

/// Completes `receiver` with the completion stored in `storage`.
///
/// The completion receives a copy of the stored tuple, in case the completion destroys the storage.
template <::beman::execution::receiver Receiver>
static void complete_from_variant(type&& storage, Receiver&& receiver) noexcept {
std::visit(
[&](auto&& tuple_data) {
using tuple_type = ::std::remove_cvref_t<decltype(tuple_data)>;
if constexpr (std::is_same_v<tuple_type, std::monostate>) {
::beman::execution::detail::unreachable();
} else {
::std::apply(
[&](const auto tag, auto&&... args) {
tag(::std::forward<Receiver>(receiver), ::std::forward<decltype(args)>(args)...);
},
// Make a copy, in case the tuple is destroyed by the completion call.
tuple_type{::std::move(tuple_data)});
}
},
::std::move(storage));
}

/// Helper to create an overload set to be able to select the right tuple from a set of arguments.
using ::beman::execution::detail::tuple_for_signature<Sigs>::select_tuple...;
};

/// Storage for a set of completions.
template <typename Signatures>
struct completion_storage {
completion_storage() noexcept = default;

/// Stores the completion with tag `t` and arguments `args...`.
template <typename Tag, typename... Args>
void store(Tag t, Args&&... args) noexcept {
variant_impl::store_into_variant(storage, t, ::std::forward<Args>(args)...);
}

/// Completes `receiver` with the completion stored in this storage.
template <::beman::execution::receiver_of<Signatures> Receiver>
void complete(Receiver&& receiver) && noexcept {
variant_impl::complete_from_variant(::std::move(storage), ::std::forward<Receiver>(receiver));
}

private:
using variant_impl = ::beman::execution::detail::variant_for_signatures<Signatures>;
typename variant_impl::type storage;
};

} // namespace beman::execution::detail

// ----------------------------------------------------------------------------

#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_COMPLETION_STORAGE
45 changes: 45 additions & 0 deletions include/beman/execution/detail/elide.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// include/beman/execution/detail/elide.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_ELIDE
#define INCLUDED_BEMAN_EXECUTION_DETAIL_ELIDE

#include <beman/execution/detail/common.hpp>
#include <beman/execution/detail/suppress_push.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
import std;
#else
#include <functional>
#include <type_traits>
#include <utility>
#endif

// ----------------------------------------------------------------------------

namespace beman::execution::detail {

/// Helper to elide move construction of a value when it is only used once.
///
/// Uses a small lambda and conversion operator to elide the move construction.
template <std::invocable F>
struct elide {
/// The resulting type of the elision.
using result_t = std::invoke_result_t<F>;

/// Constructs the elision wrapper from callable `f`.
elide(F&& f) noexcept : f_(::std::forward<F>(f)) {}

/// Evaluates the callable given at construction and returns the result, eliding the move construction of the
/// result.
operator result_t() && noexcept(::std::is_nothrow_invocable_v<F>) { return ::std::invoke(::std::forward<F>(f_)); }

private:
/// The callable to evaluate when the elision is converted to the result type.
F f_;
};

} // namespace beman::execution::detail

// ----------------------------------------------------------------------------

#endif // INCLUDED_BEMAN_EXECUTION_DETAIL_ELIDE
33 changes: 33 additions & 0 deletions include/beman/execution/detail/enter_scope_sender.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// include/beman/execution/detail/enter_scope_sender.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_ENTER_SCOPE_SENDER
#define INCLUDED_BEMAN_EXECUTION_DETAIL_ENTER_SCOPE_SENDER

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
import std;
#else
#include <concepts>
#include <type_traits>
#endif
#ifdef BEMAN_HAS_MODULES
import beman.execution.detail.sender;
#else
#include <beman/execution/detail/sender.hpp>
#endif

// ----------------------------------------------------------------------------

namespace beman::execution {
template <typename Sender>
/*!
* \brief Sender corresponding to the action of entering a scope.
* \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
*/
concept enter_scope_sender = ::beman::execution::sender<Sender>;
} // namespace beman::execution

// ----------------------------------------------------------------------------

#endif
41 changes: 41 additions & 0 deletions include/beman/execution/detail/enter_scope_sender_in.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// include/beman/execution/detail/enter_scope_sender_in.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_ENTER_SCOPE_SENDER_IN
#define INCLUDED_BEMAN_EXECUTION_DETAIL_ENTER_SCOPE_SENDER_IN

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_MODULES
import beman.execution.detail.enter_scope_sender;
import beman.execution.detail.exit_scope_sender_of_t;
import beman.execution.detail.sender_in;
#else
#include <beman/execution/detail/enter_scope_sender.hpp>
#include <beman/execution/detail/exit_scope_sender_of_t.hpp>
#include <beman/execution/detail/sender_in.hpp>
#endif

// ----------------------------------------------------------------------------

namespace beman::execution {
template <typename Sender, typename... Env>
/*!
* \brief An enter scope sender that can operate in the environment `Env...`.
* \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
*
* \details
* Requirements:
* - `Sender` models `enter_scope_sender`.
* - `Sender` models `sender_in<Env...>`.
* - The completion signatures contains exactly one value completion signature, that completion
* signature is unary, and the type of the value models `exit_sender_in<Env...>`.
* - Semantic: The above exit sender undoes the action of the enter scope sender which sent it.
*/
concept enter_scope_sender_in =
::beman::execution::enter_scope_sender<Sender> && ::beman::execution::sender_in<Sender, Env...> &&
requires { typename ::beman::execution::exit_scope_sender_of_t<Sender, Env...>; };
} // namespace beman::execution

// ----------------------------------------------------------------------------

#endif
35 changes: 35 additions & 0 deletions include/beman/execution/detail/exit_scope_sender.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// include/beman/execution/detail/exit_scope_sender.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_EXIT_SCOPE_SENDER
#define INCLUDED_BEMAN_EXECUTION_DETAIL_EXIT_SCOPE_SENDER

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
import std;
#else
#include <concepts>
#include <type_traits>
#endif
#ifdef BEMAN_HAS_MODULES
import beman.execution.detail.sender;
#else
#include <beman/execution/detail/sender.hpp>
#endif

// ----------------------------------------------------------------------------

namespace beman::execution {
/*!
* \brief A sender that does not throw when decay-copied or moved.
* \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
*/
template <typename Sender>
concept exit_scope_sender =
::beman::execution::sender<Sender> && std::is_nothrow_constructible_v<::std::remove_cvref_t<Sender>, Sender> &&
std::is_nothrow_move_constructible_v<::std::remove_cvref_t<Sender>>;
} // namespace beman::execution

// ----------------------------------------------------------------------------

#endif
85 changes: 85 additions & 0 deletions include/beman/execution/detail/exit_scope_sender_in.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// include/beman/execution/detail/exit_scope_sender_in.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_BEMAN_EXECUTION_DETAIL_EXIT_SCOPE_SENDER_IN
#define INCLUDED_BEMAN_EXECUTION_DETAIL_EXIT_SCOPE_SENDER_IN

#include <beman/execution/detail/common.hpp>
#ifdef BEMAN_HAS_IMPORT_STD
import std;
#else
#include <concepts>
#include <type_traits>
#endif
#ifdef BEMAN_HAS_MODULES
import beman.execution.detail.completion_signatures_of_t;
import beman.execution.detail.completion_signatures;
import beman.execution.detail.connect;
import beman.execution.detail.env;
import beman.execution.detail.exit_scope_sender;
import beman.execution.detail.nothrow_callable;
import beman.execution.detail.receiver;
import beman.execution.detail.sender_in;
import beman.execution.detail.set_value;
import beman.execution.detail.unreachable;
#else
#include <beman/execution/detail/completion_signatures_of_t.hpp>
#include <beman/execution/detail/completion_signatures.hpp>
#include <beman/execution/detail/connect.hpp>
#include <beman/execution/detail/env.hpp>
#include <beman/execution/detail/exit_scope_sender.hpp>
#include <beman/execution/detail/nothrow_callable.hpp>
#include <beman/execution/detail/receiver.hpp>
#include <beman/execution/detail/sender_in.hpp>
#include <beman/execution/detail/set_value.hpp>
#include <beman/execution/detail/unreachable.hpp>
#endif

// ----------------------------------------------------------------------------

namespace beman::execution::detail {

/// A receiver with environment `Env` that accepts every possible completion.
template <typename Env = ::beman::execution::env<>>
struct generic_receiver {
using receiver_concept = ::beman::execution::receiver_tag;

template <typename... T>
void set_value(T&&...) && noexcept {}
template <typename E>
void set_error(E&&) && noexcept {}
void set_stopped() && noexcept {}

auto get_env() const noexcept -> Env { ::beman::execution::detail::unreachable(); }
};

/// A sender that doesn't throw when connected to a receiver.
template <typename Sender, typename... Env>
concept nothrow_connectable =
::beman::execution::detail::nothrow_callable<::beman::execution::connect_t, Sender, generic_receiver<Env...>>;

} // namespace beman::execution::detail

namespace beman::execution {
/*!
* \brief An exit scope sender that can operate in the environment `Env...`.
* \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
*
* \details
* Requirements:
* - `Sender` models `exit_scope_sender`.
* - `Sender` models `sender_in<Env...>`.
* - `Sender` is nothrow connectable to any receiver with environment `Env...`.
* - The completion signatures of `Sender` in `Env...` is exactly `completion_signatures<set_value_t()>`.
*/
template <typename Sender, typename... Env>
concept exit_scope_sender_in =
::beman::execution::exit_scope_sender<Sender> && ::beman::execution::sender_in<Sender, Env...> &&
::beman::execution::detail::nothrow_connectable<Sender, Env...> &&
std::is_same_v<::beman::execution::completion_signatures_of_t<Sender, Env...>,
::beman::execution::completion_signatures<::beman::execution::set_value_t()>>;
} // namespace beman::execution

// ----------------------------------------------------------------------------

#endif
Loading
Loading