blob: a19719c8e78aa90a4f2e46a4ab5d6fabc71923f4 [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 "__detail/__config.hpp"
#include "__detail/__meta.hpp"
#include "concepts.hpp"
#include <cstddef>
#include <functional>
#include <tuple>
#include <type_traits>
namespace stdexec::__std_concepts
{
#if STDEXEC_HAS_STD_CONCEPTS_HEADER()
using std::invocable;
#else
template <class _Fun, class... _As>
concept invocable = //
requires(_Fun&& __f, _As&&... __as) {
std::invoke((_Fun&&)__f, (_As&&)__as...);
};
#endif
} // namespace stdexec::__std_concepts
namespace std
{
using namespace stdexec::__std_concepts;
}
namespace stdexec
{
template <auto _Fun>
struct __function_constant
{
using _FunT = decltype(_Fun);
template <class... _Args>
requires __callable<_FunT, _Args...>
STDEXEC_ATTRIBUTE((always_inline)) //
auto
operator()(_Args&&... __args) const
noexcept(noexcept(_Fun((_Args&&)__args...)))
-> decltype(_Fun((_Args&&)__args...))
{
return _Fun((_Args&&)__args...);
}
};
template <class _Ty, class _Cl, _Ty _Cl::*_MemPtr>
struct __function_constant<_MemPtr>
{
using _FunT = _Ty _Cl::*;
template <class _Arg>
requires requires(_Arg&& __arg) { ((_Arg&&)__arg).*_MemPtr; }
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto
operator()(_Arg&& __arg) const noexcept
-> decltype((((_Arg&&)__arg).*_MemPtr))
{
return ((_Arg&&)__arg).*_MemPtr;
}
};
template <auto _Fun>
inline constexpr __function_constant<_Fun> __function_constant_v{};
template <class _Fun0, class _Fun1>
struct __composed
{
STDEXEC_ATTRIBUTE((no_unique_address)) _Fun0 __t0_;
STDEXEC_ATTRIBUTE((no_unique_address)) _Fun1 __t1_;
template <class... _Ts>
requires __callable<_Fun1, _Ts...> &&
__callable<_Fun0, __call_result_t<_Fun1, _Ts...>>
STDEXEC_ATTRIBUTE((always_inline)) //
__call_result_t<_Fun0, __call_result_t<_Fun1, _Ts...>>
operator()(_Ts&&... __ts) &&
{
return ((_Fun0&&)__t0_)(((_Fun1&&)__t1_)((_Ts&&)__ts...));
}
template <class... _Ts>
requires __callable<const _Fun1&, _Ts...> &&
__callable<const _Fun0&, __call_result_t<const _Fun1&, _Ts...>>
STDEXEC_ATTRIBUTE((always_inline)) //
__call_result_t<_Fun0, __call_result_t<_Fun1, _Ts...>>
operator()(_Ts&&... __ts) const&
{
return __t0_(__t1_((_Ts&&)__ts...));
}
};
inline constexpr struct __compose_t
{
template <class _Fun0, class _Fun1>
STDEXEC_ATTRIBUTE((always_inline)) //
__composed<_Fun0, _Fun1> operator()(_Fun0 __fun0, _Fun1 __fun1) const
{
return {(_Fun0&&)__fun0, (_Fun1&&)__fun1};
}
} __compose{};
namespace __invoke_
{
template <class>
inline constexpr bool __is_refwrap = false;
template <class _Up>
inline constexpr bool __is_refwrap<std::reference_wrapper<_Up>> = true;
struct __funobj
{
template <class _Fun, class... _Args>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto operator()(_Fun&& __fun, _Args&&... __args) const
noexcept(noexcept(((_Fun&&)__fun)((_Args&&)__args...)))
-> decltype(((_Fun&&)__fun)((_Args&&)__args...))
{
return ((_Fun&&)__fun)((_Args&&)__args...);
}
};
struct __memfn
{
template <class _Memptr, class _Ty, class... _Args>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto operator()(_Memptr __mem_ptr, _Ty&& __ty,
_Args&&... __args) const
noexcept(noexcept((((_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...)))
-> decltype((((_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...))
{
return (((_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...);
}
};
struct __memfn_refwrap
{
template <class _Memptr, class _Ty, class... _Args>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto operator()(_Memptr __mem_ptr, _Ty __ty,
_Args&&... __args) const
noexcept(noexcept((__ty.get().*__mem_ptr)((_Args&&)__args...)))
-> decltype((__ty.get().*__mem_ptr)((_Args&&)__args...))
{
return (__ty.get().*__mem_ptr)((_Args&&)__args...);
}
};
struct __memfn_smartptr
{
template <class _Memptr, class _Ty, class... _Args>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto operator()(_Memptr __mem_ptr, _Ty&& __ty,
_Args&&... __args) const
noexcept(noexcept(((*(_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...)))
-> decltype(((*(_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...))
{
return ((*(_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...);
}
};
struct __memobj
{
template <class _Mbr, class _Class, class _Ty>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto operator()(_Mbr _Class::*__mem_ptr,
_Ty&& __ty) const noexcept
-> decltype((((_Ty&&)__ty).*__mem_ptr))
{
return (((_Ty&&)__ty).*__mem_ptr);
}
};
struct __memobj_refwrap
{
template <class _Mbr, class _Class, class _Ty>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto operator()(_Mbr _Class::*__mem_ptr, _Ty __ty) const noexcept
-> decltype((__ty.get().*__mem_ptr))
{
return (__ty.get().*__mem_ptr);
}
};
struct __memobj_smartptr
{
template <class _Mbr, class _Class, class _Ty>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto operator()(_Mbr _Class::*__mem_ptr,
_Ty&& __ty) const noexcept
-> decltype(((*(_Ty&&)__ty).*__mem_ptr))
{
return ((*(_Ty&&)__ty).*__mem_ptr);
}
};
__funobj __invoke_selector(__ignore, __ignore) noexcept;
template <class _Mbr, class _Class, class _Ty>
auto __invoke_selector(_Mbr _Class::*, const _Ty&) noexcept
{
if constexpr (STDEXEC_IS_CONST(_Mbr) || STDEXEC_IS_CONST(const _Mbr))
{
// member function ptr case
if constexpr (STDEXEC_IS_BASE_OF(_Class, _Ty))
{
return __memobj{};
}
else if constexpr (__is_refwrap<_Ty>)
{
return __memobj_refwrap{};
}
else
{
return __memobj_smartptr{};
}
}
else
{
// member object ptr case
if constexpr (STDEXEC_IS_BASE_OF(_Class, _Ty))
{
return __memfn{};
}
else if constexpr (__is_refwrap<_Ty>)
{
return __memfn_refwrap{};
}
else
{
return __memfn_smartptr{};
}
}
}
struct __invoke_t
{
template <class _Fun>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto operator()(_Fun&& __fun) const
noexcept(noexcept(((_Fun&&)__fun)())) -> decltype(((_Fun&&)__fun)())
{
return ((_Fun&&)__fun)();
}
template <class _Fun, class _Ty, class... _Args>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto operator()(_Fun&& __fun, _Ty&& __ty, _Args&&... __args) const
noexcept(noexcept(__invoke_selector(__fun, __ty)((_Fun&&)__fun,
(_Ty&&)__ty,
(_Args&&)__args...)))
-> decltype(__invoke_selector(__fun, __ty)((_Fun&&)__fun,
(_Ty&&)__ty,
(_Args&&)__args...))
{
return decltype(__invoke_selector(__fun, __ty))()(
(_Fun&&)__fun, (_Ty&&)__ty, (_Args&&)__args...);
}
};
} // namespace __invoke_
inline constexpr __invoke_::__invoke_t __invoke{};
template <class _Fun, class... _As>
concept __invocable = //
requires(_Fun&& __f, _As&&... __as) {
__invoke((_Fun&&)__f, (_As&&)__as...);
};
template <class _Fun, class... _As>
concept __nothrow_invocable = //
__invocable<_Fun, _As...> && //
requires(_Fun&& __f, _As&&... __as) {
{
__invoke((_Fun&&)__f, (_As&&)__as...)
} noexcept;
};
template <class _Fun, class... _As>
using __invoke_result_t = //
decltype(__invoke(__declval<_Fun>(), __declval<_As>()...));
namespace __apply_
{
using std::get;
template <std::size_t... _Is, class _Fn, class _Tup>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto __impl(__indices<_Is...>, _Fn&& __fn, _Tup&& __tup) noexcept(
noexcept(__invoke((_Fn&&)__fn, get<_Is>((_Tup&&)__tup)...)))
-> decltype(__invoke((_Fn&&)__fn, get<_Is>((_Tup&&)__tup)...))
{
return __invoke((_Fn&&)__fn, get<_Is>((_Tup&&)__tup)...);
}
template <class _Tup>
using __tuple_indices =
__make_indices<std::tuple_size<std::remove_cvref_t<_Tup>>::value>;
template <class _Fn, class _Tup>
using __result_t = decltype(__apply_::__impl(
__tuple_indices<_Tup>(), __declval<_Fn>(), __declval<_Tup>()));
} // namespace __apply_
template <class _Fn, class _Tup>
concept __applicable = __mvalid<__apply_::__result_t, _Fn, _Tup>;
template <class _Fn, class _Tup>
concept __nothrow_applicable = __applicable<_Fn, _Tup> //
&& noexcept(__apply_::__impl(__apply_::__tuple_indices<_Tup>(),
__declval<_Fn>(), __declval<_Tup>()));
template <class _Fn, class _Tup>
requires __applicable<_Fn, _Tup>
using __apply_result_t = __apply_::__result_t<_Fn, _Tup>;
struct __apply_t
{
template <class _Fn, class _Tup>
requires __applicable<_Fn, _Tup>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto
operator()(_Fn&& __fn, _Tup&& __tup) const
noexcept(__nothrow_applicable<_Fn, _Tup>) -> __apply_result_t<_Fn, _Tup>
{
return __apply_::__impl(__apply_::__tuple_indices<_Tup>(), (_Fn&&)__fn,
(_Tup&&)__tup);
}
};
inline constexpr __apply_t __apply{};
template <class _Tag, class _Ty>
struct __field
{
STDEXEC_ATTRIBUTE((always_inline)) //
_Ty operator()(_Tag) const noexcept(__nothrow_decay_copyable<const _Ty&>)
{
return __t_;
}
_Ty __t_;
};
template <class _Tag>
struct __mkfield_
{
template <class _Ty>
STDEXEC_ATTRIBUTE((always_inline)) //
__field<_Tag, __decay_t<_Ty>> operator()(_Ty&& __ty) const
noexcept(__nothrow_decay_copyable<_Ty>)
{
return {(_Ty&&)__ty};
}
};
template <class _Tag>
inline constexpr __mkfield_<_Tag> __mkfield{};
// [func.tag_invoke], tag_invoke
namespace __tag_invoke
{
void tag_invoke();
// NOT TO SPEC: Don't require tag_invocable to subsume invocable.
// std::invoke is more expensive at compile time than necessary,
// and results in diagnostics that are more verbose than necessary.
template <class _Tag, class... _Args>
concept tag_invocable = //
requires(_Tag __tag, _Args&&... __args) {
tag_invoke((_Tag&&)__tag, (_Args&&)__args...);
};
template <class _Ret, class _Tag, class... _Args>
concept __tag_invocable_r = //
requires(_Tag __tag, _Args&&... __args) {
{
static_cast<_Ret>(tag_invoke((_Tag&&)__tag, (_Args&&)__args...))
};
};
// NOT TO SPEC: nothrow_tag_invocable subsumes tag_invocable
template <class _Tag, class... _Args>
concept nothrow_tag_invocable =
tag_invocable<_Tag, _Args...> && //
requires(_Tag __tag, _Args&&... __args) {
{
tag_invoke((_Tag&&)__tag, (_Args&&)__args...)
} noexcept;
};
template <class _Tag, class... _Args>
using tag_invoke_result_t =
decltype(tag_invoke(__declval<_Tag>(), __declval<_Args>()...));
template <class _Tag, class... _Args>
struct tag_invoke_result
{};
template <class _Tag, class... _Args>
requires tag_invocable<_Tag, _Args...>
struct tag_invoke_result<_Tag, _Args...>
{
using type = tag_invoke_result_t<_Tag, _Args...>;
};
struct tag_invoke_t
{
template <class _Tag, class... _Args>
requires tag_invocable<_Tag, _Args...>
STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
operator()(_Tag __tag, _Args&&... __args) const
noexcept(nothrow_tag_invocable<_Tag, _Args...>)
-> tag_invoke_result_t<_Tag, _Args...>
{
return tag_invoke((_Tag&&)__tag, (_Args&&)__args...);
}
};
} // namespace __tag_invoke
using __tag_invoke::tag_invoke_t;
namespace __ti
{
inline constexpr tag_invoke_t tag_invoke{};
}
using namespace __ti;
template <auto& _Tag>
using tag_t = __decay_t<decltype(_Tag)>;
using __tag_invoke::__tag_invocable_r;
using __tag_invoke::nothrow_tag_invocable;
using __tag_invoke::tag_invocable;
using __tag_invoke::tag_invoke_result;
using __tag_invoke::tag_invoke_result_t;
} // namespace stdexec