blob: 5f02635d102496885c03cbff078536c611deb217 [file] [log] [blame]
/*
* Copyright (c) 2021-2022 NVIDIA Corporation
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "../concepts.hpp"
#include "__concepts.hpp"
#include "__config.hpp"
#include "__env.hpp"
#include "__execution_fwd.hpp"
#include "__meta.hpp"
#include "__tuple.hpp"
#include "__type_traits.hpp"
#include <cstddef>
#include <type_traits>
#include <utility> // for tuple_size/tuple_element
namespace stdexec
{
/////////////////////////////////////////////////////////////////////////////
// Generic __sender type
namespace __detail
{
template <class _Sender>
using __impl_of = decltype((__declval<_Sender>().__impl_));
struct __get_tag
{
template <class _Tag, class... _Rest>
STDEXEC_ATTRIBUTE((always_inline))
_Tag operator()(_Tag, _Rest&&...) const noexcept
{
return {};
}
};
struct __get_data
{
template <class _Data, class... _Rest>
STDEXEC_ATTRIBUTE((always_inline))
_Data&& operator()(__ignore, _Data&& __data, _Rest&&...) const noexcept
{
return (_Data&&)__data;
}
};
template <class _Continuation>
struct __get_children
{
template <class... _Child>
STDEXEC_ATTRIBUTE((always_inline))
auto operator()(__ignore, __ignore, _Child&&...) const noexcept
-> __mtype<__minvoke<_Continuation, _Child...>> (*)()
{
return nullptr;
}
};
template <class _Tag, class _Data, class... _Child>
struct __desc
{
using __tag = _Tag;
using __data = _Data;
using __children = __types<_Child...>;
};
template <class _Fn>
struct __sexpr_uncurry_fn
{
template <class _Tag, class _Data, class... _Child>
requires __minvocable<_Fn, _Tag, _Data, _Child...>
constexpr auto operator()(_Tag, _Data&&, _Child&&...) const noexcept
-> __minvoke<_Fn, _Tag, _Data, _Child...>;
};
template <class _Sender, class _Fn>
using __sexpr_uncurry =
__call_result_t<__impl_of<_Sender>, __copy_cvref_fn<_Sender>,
__sexpr_uncurry_fn<_Fn>>;
template <class _Sender>
using __desc_of = __sexpr_uncurry<_Sender, __q<__desc>>;
using __get_desc = __sexpr_uncurry_fn<__q<__desc>>;
template <class _Sender>
extern __q<__midentity> __name_of_v;
template <class _Sender>
using __name_of_fn = decltype(__name_of_v<_Sender>);
template <class _Sender>
using __name_of = __minvoke<__name_of_fn<_Sender>, _Sender>;
} // namespace __detail
template <class _Sender>
using tag_of_t = typename __detail::__desc_of<_Sender>::__tag;
template <class _Sender>
using __data_of = typename __detail::__desc_of<_Sender>::__data;
template <class _Sender, class _Continuation = __q<__types>>
using __children_of = //
__mapply<_Continuation, typename __detail::__desc_of<_Sender>::__children>;
template <class _Ny, class _Sender>
using __nth_child_of = __children_of<_Sender, __mbind_front_q<__m_at, _Ny>>;
template <std::size_t _Ny, class _Sender>
using __nth_child_of_c =
__children_of<_Sender, __mbind_front_q<__m_at, __msize_t<_Ny>>>;
template <class _Sender>
using __child_of = __children_of<_Sender, __q<__mfront>>;
template <class _Sender>
inline constexpr std::size_t __nbr_children_of =
__v<__children_of<_Sender, __msize>>;
template <class _Fn, class _Tp>
requires __mvalid<tag_of_t, _Tp> &&
__mvalid<__detail::__sexpr_uncurry, _Tp, _Fn>
struct __uncurry_<_Fn, _Tp>
{
using __t = __detail::__sexpr_uncurry<_Tp, _Fn>;
};
template <class _Tag>
struct __sexpr_impl;
template <class _Sender>
using __name_of = __detail::__name_of<_Sender>;
namespace __detail
{
template <class _Sexpr, class _Receiver>
struct __op_state;
template <class _Sexpr, class _Receiver>
struct __connect_fn;
template <class _Tag, class _Sexpr, class _Receiver>
using __state_type_t =
__decay_t<__result_of<__sexpr_impl<_Tag>::get_state, _Sexpr, _Receiver&>>;
template <class _Tag, class _Index, class _Sexpr, class _Receiver>
using __env_type_t =
__result_of<__sexpr_impl<_Tag>::get_env, _Index,
__state_type_t<_Tag, _Sexpr, _Receiver>&, _Receiver&>;
template <class _Sexpr, class _Receiver>
concept __connectable =
__callable<__impl_of<_Sexpr>, __copy_cvref_fn<_Sexpr>,
__connect_fn<_Sexpr, _Receiver>> &&
__mvalid<__state_type_t, tag_of_t<_Sexpr>, _Sexpr, _Receiver>;
// // Note: This is UB. UBSAN allows it for now.
// template <class _Parent, class _Child>
// _Parent* __parent_from_child(_Child* __child, _Child _Parent::*__mbr_ptr)
// noexcept {
// alignas(_Parent) char __buf[sizeof(_Parent)];
// _Parent* __parent = (_Parent*) &__buf;
// const std::ptrdiff_t __offset = (char*) &(__parent->*__mbr_ptr) - __buf;
// return (_Parent*) ((char*) __child - __offset);
// }
inline constexpr auto __get_attrs = //
[](__ignore, const auto&... __child) noexcept -> decltype(auto) {
if constexpr (sizeof...(__child) == 1)
{
return stdexec::get_env(
__child...); // BUGBUG: should be only the forwarding queries
}
else
{
return empty_env();
}
STDEXEC_UNREACHABLE();
};
inline constexpr auto __get_env = //
[]<class _Receiver>(__ignore, __ignore, const _Receiver& __rcvr) noexcept
-> env_of_t<const _Receiver&> { return stdexec::get_env(__rcvr); };
inline constexpr auto __get_state = //
[]<class _Sender>(_Sender&& __sndr, __ignore) noexcept -> decltype(auto) {
return STDEXEC_CALL_EXPLICIT_THIS_MEMFN((_Sender&&)__sndr,
apply)(__get_data());
};
inline constexpr auto __connect = //
[]<class _Sender, class _Receiver>(
_Sender&& __sndr, _Receiver __rcvr) -> __op_state<_Sender, _Receiver>
requires __connectable<_Sender, _Receiver>
{
return __op_state<_Sender, _Receiver>{(_Sender&&)__sndr,
(_Receiver&&)__rcvr};
};
inline constexpr auto __start = //
[]<class _StartTag = start_t, class... _ChildOps>(
__ignore, __ignore, _ChildOps&... __ops) noexcept
{
(_StartTag()(__ops), ...);
};
inline constexpr auto __complete = //
[]<class _Index, class _Receiver, class _SetTag, class... _Args>(
_Index, __ignore, _Receiver& __rcvr, _SetTag,
_Args&&... __args) noexcept {
static_assert(__v<_Index> == 0,
"I don't know how to complete this operation.");
_SetTag()(std::move(__rcvr), (_Args&&)__args...);
};
inline constexpr auto __get_completion_signagures = //
[](__ignore, __ignore) noexcept { return void(); };
template <class _ReceiverId, class _Sexpr, class _Idx>
struct __receiver
{
struct __t
{
using receiver_concept = receiver_t;
using _Receiver = stdexec::__t<_ReceiverId>;
using __sexpr = _Sexpr;
using __index = _Idx;
using __id = __receiver;
using __parent_op_t = __op_state<_Sexpr, _Receiver>;
using __tag_t = tag_of_t<_Sexpr>;
// A pointer to the parent operation state, which contains the one
// created with this receiver.
__parent_op_t* __op_;
// template <class _ChildSexpr, class _ChildReceiver>
// static __t __from_op_state(__op_state<_ChildSexpr, _ChildReceiver>*
// __child) noexcept {
// using __parent_op_t = __op_state<_Sexpr, _Receiver>;
// std::ptrdiff_t __offset = __parent_op_t::template
// __get_child_op_offset<__v<_Idx>>();
// __parent_op_t* __parent = (__parent_op_t*) ((char*) __child -
// __offset); return __t{__parent};
// }
template <__completion_tag _Tag, class... _Args>
STDEXEC_ATTRIBUTE((always_inline))
friend void tag_invoke(_Tag, __t&& __self, _Args&&... __args) noexcept
{
__self.__op_->__complete(_Idx(), _Tag(), (_Args&&)__args...);
}
template <same_as<get_env_t> _Tag, class _SexprTag = __tag_t>
STDEXEC_ATTRIBUTE((always_inline))
friend auto tag_invoke(_Tag, const __t& __self) noexcept
-> __env_type_t<_SexprTag, _Idx, _Sexpr, _Receiver>
{
return __self.__op_->__get_env(_Idx());
}
};
};
template <class _Receiver>
using __sexpr_connected_with =
__mapply<__mbind_front_q<__m_at, typename _Receiver::__index>,
typename __call_result_t<__impl_of<typename _Receiver::__sexpr>,
__cp, __get_desc>::__children>;
template <class _Sexpr, class _Receiver>
struct __op_base : __immovable
{
using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_;
__op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) :
__rcvr_((_Receiver&&)__rcvr),
__state_(__sexpr_impl<__tag_t>::get_state((_Sexpr&&)__sndr, __rcvr_))
{}
_Receiver& __rcvr() & noexcept
{
return __rcvr_;
}
};
// template <class _Sexpr, class _Receiver>
// requires __is_instance_of<__id<_Receiver>, __receiver>
// && __decays_to<_Sexpr, __sexpr_connected_with<_Receiver>>
// struct __op_base<_Sexpr, _Receiver> : __immovable {
// using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
// using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
// STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_;
// __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr)
// : __state_(__sexpr_impl<__tag_t>::get_state((_Sexpr&&) __sndr, __rcvr)) {
// STDEXEC_ASSERT(this->__rcvr().__op_ == __rcvr.__op_);
// }
// _Receiver __rcvr() const noexcept {
// return _Receiver::__from_op_state( //
// static_cast<__op_state<_Sexpr, _Receiver>*>( //
// const_cast<__op_base*>(this)));
// }
// };
STDEXEC_PRAGMA_PUSH()
STDEXEC_PRAGMA_IGNORE_GNU("-Winvalid-offsetof")
STDEXEC_PRAGMA_IGNORE_EDG(offset_in_non_POD_nonstandard)
template <class _Sexpr, class _Receiver>
struct __enable_receiver_from_this
{
using __op_base_t = __op_base<_Sexpr, _Receiver>;
decltype(auto) __receiver() noexcept
{
using __derived_t = decltype(__op_base_t::__state_);
__derived_t* __derived = static_cast<__derived_t*>(this);
constexpr std::size_t __offset = offsetof(__op_base_t, __state_);
__op_base_t* __base = (__op_base_t*)((char*)__derived - __offset);
return __base->__rcvr();
}
};
STDEXEC_PRAGMA_POP()
STDEXEC_PRAGMA_PUSH()
STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
template <class _Sexpr, class _Receiver>
struct __connect_fn
{
template <std::size_t _Idx>
using __receiver_t =
__t<__receiver<__id<_Receiver>, _Sexpr, __mconstant<_Idx>>>;
__op_state<_Sexpr, _Receiver>* __op_;
struct __impl
{
__op_state<_Sexpr, _Receiver>* __op_;
template <std::size_t... _Is, class _Tag, class _Data, class... _Child>
auto operator()(__indices<_Is...>, _Tag, _Data&&,
_Child&&... __child) const
-> __tup::__tuple<__indices<_Is...>,
connect_result_t<_Child, __receiver_t<_Is>>...>
{
return __tuple{
connect((_Child&&)__child, __receiver_t<_Is>{__op_})...};
}
};
template <class _Tag, class _Data, class... _Child>
auto operator()(_Tag, _Data&& __data, _Child&&... __child) const
-> __call_result_t<__impl, __indices_for<_Child...>, _Tag, _Data,
_Child...>
{
return __impl{__op_}(__indices_for<_Child...>(), _Tag(),
(_Data&&)__data, (_Child&&)__child...);
}
};
STDEXEC_PRAGMA_POP()
template <class _Sexpr, class _Receiver>
struct __op_state : __op_base<_Sexpr, _Receiver>
{
using __desc_t = typename __decay_t<_Sexpr>::__desc_t;
using __tag_t = typename __desc_t::__tag;
using __data_t = typename __desc_t::__data;
using __children_t = typename __desc_t::__children;
using __state_t = typename __op_state::__state_t;
using __inner_ops_t =
__result_of<__sexpr_apply, _Sexpr, __connect_fn<_Sexpr, _Receiver>>;
__inner_ops_t __inner_ops_;
// template <std::size_t _Idx>
// static std::ptrdiff_t __get_child_op_offset() noexcept {
// __op_state* __self = (__op_state*) &__self;
// return (std::ptrdiff_t)((char*)
// &__tup::__get<_Idx>(__self->__inner_ops_) - (char*) __self);
// }
__op_state(_Sexpr&& __sexpr, _Receiver __rcvr) :
__op_state::__op_base{(_Sexpr&&)__sexpr, (_Receiver&&)__rcvr},
__inner_ops_(__sexpr_apply((_Sexpr&&)__sexpr,
__connect_fn<_Sexpr, _Receiver>{this}))
{}
template <same_as<start_t> _Tag2>
STDEXEC_ATTRIBUTE((always_inline))
friend void tag_invoke(_Tag2, __op_state& __self) noexcept
{
using __tag_t = typename __op_state::__tag_t;
auto&& __rcvr = __self.__rcvr();
__tup::__apply(
[&](auto&... __ops) noexcept {
__sexpr_impl<__tag_t>::start(__self.__state_, __rcvr, __ops...);
},
__self.__inner_ops_);
}
template <class _Index, class _Tag2, class... _Args>
STDEXEC_ATTRIBUTE((always_inline))
void __complete(_Index, _Tag2, _Args&&... __args) noexcept
{
using __tag_t = typename __op_state::__tag_t;
auto&& __rcvr = this->__rcvr();
__sexpr_impl<__tag_t>::complete(_Index(), this->__state_, __rcvr,
_Tag2(), (_Args&&)__args...);
}
template <class _Index>
STDEXEC_ATTRIBUTE((always_inline)) //
auto __get_env(_Index) noexcept
-> __env_type_t<__tag_t, _Index, _Sexpr, _Receiver>
{
const auto& __rcvr = this->__rcvr();
return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state_, __rcvr);
}
};
inline constexpr auto __drop_front = //
[]<class _Fn>(_Fn __fn) noexcept {
return
[__fn = std::move(__fn)]<class... _Rest>(
auto&&, _Rest&&... __rest) noexcept(__nothrow_callable<const _Fn&,
_Rest...>)
-> __call_result_t<const _Fn&, _Rest...> {
return __fn((_Rest&&)__rest...);
};
};
} // namespace __detail
struct __sexpr_defaults
{
static constexpr auto get_attrs = __detail::__get_attrs;
static constexpr auto get_env = __detail::__get_env;
static constexpr auto get_state = __detail::__get_state;
static constexpr auto connect = __detail::__connect;
static constexpr auto start = __detail::__start;
static constexpr auto complete = __detail::__complete;
static constexpr auto get_completion_signagures =
__detail::__get_completion_signagures;
};
template <class Tag>
struct __sexpr_impl : __sexpr_defaults
{};
using __detail::__enable_receiver_from_this;
template <class _Tag>
using __get_attrs_fn = __result_of<__detail::__drop_front,
__mtypeof<__sexpr_impl<_Tag>::get_attrs>>;
//////////////////////////////////////////////////////////////////////////////////////////////////
// __sexpr
template <class...>
struct __sexpr
{
using __id = __sexpr;
using __t = __sexpr;
};
template <class _ImplFn>
struct __sexpr<_ImplFn>
{
using sender_concept = sender_t;
using __t = __sexpr;
using __id = __sexpr;
using __desc_t = __call_result_t<_ImplFn, __cp, __detail::__get_desc>;
using __tag_t = typename __desc_t::__tag;
using __data_t = typename __desc_t::__data;
using __children_t = typename __desc_t::__children;
using __arity_t = __mapply<__msize, __children_t>;
template <class _Tag>
using __impl = __sexpr_impl<__meval<__msecond, _Tag, __tag_t>>;
STDEXEC_ATTRIBUTE((always_inline)) //
static __tag_t __tag() noexcept
{
return {};
}
mutable _ImplFn __impl_;
STDEXEC_ATTRIBUTE((host, device, always_inline))
explicit __sexpr(_ImplFn __impl) : __impl_((_ImplFn&&)__impl) {}
template <same_as<get_env_t> _Tag, same_as<__sexpr> _Self>
STDEXEC_ATTRIBUTE((always_inline)) //
friend auto tag_invoke(_Tag, const _Self& __self) noexcept //
-> __msecond<
__if_c<same_as<_Tag, get_env_t> && same_as<_Self, __sexpr>>, //
__result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>>>
{
return __sexpr_apply(__self,
__detail::__drop_front(__impl<_Tag>::get_attrs));
}
template <same_as<get_completion_signatures_t> _Tag,
__decays_to<__sexpr> _Self, class _Env>
STDEXEC_ATTRIBUTE((always_inline)) //
friend auto tag_invoke(_Tag, _Self&& __self, _Env&& __env) noexcept //
-> __msecond<
__if_c<same_as<_Tag, get_completion_signatures_t> &&
__decays_to<_Self, __sexpr>>,
__result_of<__impl<_Tag>::get_completion_signatures, _Self, _Env>>
{
return {};
}
// BUGBUG fix receiver constraint here:
template <same_as<connect_t> _Tag, __decays_to<__sexpr> _Self,
/*receiver*/ class _Receiver>
STDEXEC_ATTRIBUTE((always_inline)) //
friend auto tag_invoke(_Tag, _Self&& __self, _Receiver&& __rcvr) //
noexcept(noexcept(__impl<_Tag>::connect((_Self&&)__self,
(_Receiver&&)__rcvr))) //
-> __msecond<
__if_c<same_as<_Tag, connect_t> && __decays_to<_Self, __sexpr>>,
__result_of<__impl<_Tag>::connect, _Self, _Receiver>>
{
return __impl<_Tag>::connect((_Self&&)__self, (_Receiver&&)__rcvr);
}
template <class _Sender, class _ApplyFn>
STDEXEC_ATTRIBUTE((always_inline)) //
STDEXEC_DEFINE_EXPLICIT_THIS_MEMFN(auto apply)(this _Sender&& __sndr,
_ApplyFn&& __fun) //
noexcept(__nothrow_callable<__detail::__impl_of<_Sender>,
__copy_cvref_fn<_Sender>, _ApplyFn>) //
-> __call_result_t<__detail::__impl_of<_Sender>,
__copy_cvref_fn<_Sender>, _ApplyFn>
{ //
return ((_Sender&&)__sndr)
.__impl_(__copy_cvref_fn<_Sender>(), (_ApplyFn&&)__fun); //
}
template <std::size_t _Idx, __decays_to_derived_from<__sexpr> _Self>
STDEXEC_ATTRIBUTE((always_inline))
friend decltype(auto) get(_Self&& __self) noexcept
requires(_Idx < (__v<__arity_t> + 2))
{
if constexpr (_Idx == 0)
{
return __tag_t();
}
else
{
return __self.__impl_(__copy_cvref_fn<_Self>(),
__nth_pack_element<_Idx>);
}
STDEXEC_UNREACHABLE();
}
};
template <class _ImplFn>
STDEXEC_ATTRIBUTE((host, device))
__sexpr(_ImplFn) -> __sexpr<_ImplFn>;
//////////////////////////////////////////////////////////////////////////////////////////////////
// __make_sexpr
namespace __detail
{
template <class _Tag>
struct __make_sexpr_t
{
template <class _Data = __, class... _Child>
constexpr auto operator()(_Data __data = {}, _Child... __child) const;
};
#if STDEXEC_NVHPC() || (STDEXEC_GCC() && __GNUC__ < 13)
// The NVIDIA HPC compiler and gcc prior to v13 struggle with capture
// initializers for a parameter pack. As a workaround, we use a wrapper that
// performs moves when non-const lvalues are copied. That constructor is
// only used when capturing the variables, never when the resulting lambda
// is copied or moved.
// Move-by-copy
template <class _Ty>
struct __mbc
{
template <class _Cvref>
using __f = __minvoke<_Cvref, _Ty>;
_Ty __value;
STDEXEC_ATTRIBUTE((always_inline))
explicit __mbc(_Ty& __v) noexcept(
std::is_nothrow_move_constructible_v<_Ty>) :
__value((_Ty&&)__v)
{}
// This is a template so as to not be considered a copy/move constructor.
// Therefore, it doesn't suppress the generation of the default copy/move
// constructors.
STDEXEC_ATTRIBUTE((always_inline))
__mbc(same_as<__mbc> auto& __that) noexcept(
std::is_nothrow_move_constructible_v<_Ty>) :
__value(static_cast<_Ty&&>(__that.__value))
{}
};
// Rather strange definition of the lambda return type below is to reap the
// benefits of SFINAE without nvc++ encoding the whole return type into the
// symbol name.
template <class _Ty>
extern _Ty (*__f)();
// Anonymous namespace here is to avoid symbol name collisions with the
// lambda functions returned by __make_tuple.
namespace
{
constexpr auto __make_tuple = //
[]<class _Tag, class... _Captures>(_Tag, _Captures&&... __captures) {
return
[=]<class _Cvref, class _Fun>(_Cvref __cvref, _Fun&& __fun) mutable //
noexcept(
__nothrow_callable<_Fun, _Tag, __minvoke<_Captures, _Cvref>...>) //
-> decltype(__f<__call_result_t<_Fun, _Tag,
__minvoke<_Captures, _Cvref>...>>())
requires __callable<_Fun, _Tag, __minvoke<_Captures, _Cvref>...>
{
return ((_Fun&&)__fun)(
_Tag(),
const_cast<__minvoke<_Captures, _Cvref>&&>(__captures.__value)...);
};
};
} // anonymous namespace
template <class _Tag>
template <class _Data, class... _Child>
constexpr auto __make_sexpr_t<_Tag>::operator()(_Data __data,
_Child... __child) const
{
return __sexpr{__make_tuple(_Tag(), __detail::__mbc(__data),
__detail::__mbc(__child)...)};
}
#else
// Anonymous namespace here is to avoid symbol name collisions with the
// lambda functions returned by __make_tuple.
namespace
{
constexpr auto __make_tuple = //
[]<class _Tag, class... _Captures>(_Tag, _Captures&&... __captures) {
return
[... __captures = (_Captures&&)__captures]<class _Cvref, class _Fun>(
_Cvref, _Fun&& __fun) mutable //
noexcept(
__nothrow_callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>) //
-> __call_result_t<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
requires __callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
{
return ((_Fun&&)__fun)(
_Tag(), const_cast<__minvoke<_Cvref, _Captures>&&>(__captures)...);
};
};
} // anonymous namespace
template <class _Tag>
template <class _Data, class... _Child>
constexpr auto __make_sexpr_t<_Tag>::operator()(_Data __data,
_Child... __child) const
{
return __sexpr{__make_tuple(_Tag(), (_Data&&)__data, (_Child&&)__child...)};
};
#endif
template <class _Tag>
inline constexpr __make_sexpr_t<_Tag> __make_sexpr{};
} // namespace __detail
using __detail::__make_sexpr;
template <class _Tag, class _Data, class... _Child>
using __sexpr_t = __result_of<__make_sexpr<_Tag>, _Data, _Child...>;
namespace __detail
{
struct __sexpr_apply_t
{
template <class _Sender, class _ApplyFn>
STDEXEC_ATTRIBUTE((always_inline)) //
auto operator()(_Sender&& __sndr, _ApplyFn&& __fun) const //
noexcept(noexcept(STDEXEC_CALL_EXPLICIT_THIS_MEMFN(
((_Sender&&)__sndr), apply)((_ApplyFn&&)__fun))) //
-> decltype(STDEXEC_CALL_EXPLICIT_THIS_MEMFN(((_Sender&&)__sndr),
apply)((_ApplyFn&&)__fun))
{
return STDEXEC_CALL_EXPLICIT_THIS_MEMFN(((_Sender&&)__sndr),
apply)((_ApplyFn&&)__fun); //
}
};
} // namespace __detail
using __detail::__sexpr_apply_t;
inline constexpr __sexpr_apply_t __sexpr_apply{};
template <class _Sender, class _ApplyFn>
using __sexpr_apply_result_t =
__call_result_t<__sexpr_apply_t, _Sender, _ApplyFn>;
template <class _Sender>
concept sender_expr = //
__mvalid<tag_of_t, _Sender>;
template <class _Sender, class _Tag>
concept sender_expr_for = //
sender_expr<_Sender> && same_as<tag_of_t<_Sender>, _Tag>;
// The __name_of utility defined below is used to pretty-print the type names of
// senders in compiler diagnostics.
namespace __detail
{
struct __basic_sender_name
{
template <class _Sender>
using __f = //
__call_result_t<__sexpr_apply_result_t<_Sender, __basic_sender_name>>;
template <class _Tag, class _Data, class... _Child>
auto operator()(_Tag, _Data&&, _Child&&...) const //
-> __sexpr<_Tag, _Data, __name_of<_Child>...> (*)();
};
struct __id_name
{
template <class _Sender>
using __f = __name_of<__id<_Sender>>;
};
template <class _Sender>
extern __mcompose<__cplr, __name_of_fn<_Sender>> __name_of_v<_Sender&>;
template <class _Sender>
extern __mcompose<__cprr, __name_of_fn<_Sender>> __name_of_v<_Sender&&>;
template <class _Sender>
extern __mcompose<__cpclr, __name_of_fn<_Sender>> __name_of_v<const _Sender&>;
template <class _Impl>
extern __basic_sender_name __name_of_v<__sexpr<_Impl>>;
template <__has_id _Sender>
requires(!same_as<__id<_Sender>, _Sender>)
extern __id_name __name_of_v<_Sender>;
} // namespace __detail
} // namespace stdexec
namespace std
{
template <class _Impl>
struct tuple_size<stdexec::__sexpr<_Impl>> :
integral_constant<
size_t, stdexec::__v<typename stdexec::__sexpr<_Impl>::__arity_t> + 2>
{};
template <size_t _Idx, class _Impl>
struct tuple_element<_Idx, stdexec::__sexpr<_Impl>>
{
using type = stdexec::__remove_rvalue_reference_t<stdexec::__call_result_t<
_Impl, stdexec::__cp, stdexec::__nth_pack_element_t<_Idx>>>;
};
} // namespace std