Skip to content
Merged
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
80 changes: 30 additions & 50 deletions include/stdexec/__detail/__env.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "__meta.hpp"
#include "__query.hpp"
#include "__tag_invoke.hpp"
#include "__tuple.hpp"

#include <exception> // IWYU pragma: keep for std::terminate
#include <functional> // IWYU pragma: keep for unwrap_reference_t
Expand All @@ -29,6 +30,7 @@
STDEXEC_PRAGMA_PUSH()
STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend)
STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")

namespace STDEXEC
{
Expand Down Expand Up @@ -113,7 +115,7 @@ namespace STDEXEC
}
else
{
return env<_Env1, __fwd_env_t<_Env2>>{{static_cast<_Env1 &&>(__env1)},
return env<_Env1, __fwd_env_t<_Env2>>{static_cast<_Env1 &&>(__env1),
__fwd_fn()(static_cast<_Env2 &&>(__env2))};
}
}
Expand Down Expand Up @@ -201,70 +203,48 @@ namespace STDEXEC
STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE
prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>;

//////////////////////////////////////////////////////////////////////
// env
template <class... _Envs>
struct env;

template <>
struct env<>
{
STDEXEC_ATTRIBUTE(nodiscard, host, device)
auto query() const = delete;
};

template <class _Env>
struct env<_Env> : _Env
{};

template <class _Env>
struct env<_Env &>
namespace __detail
{
template <class _Query, class... _Args>
requires __queryable_with<_Env, _Query, _Args...>
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
constexpr auto query(_Query, _Args &&...__args) const
noexcept(__nothrow_queryable_with<_Env, _Query, _Args...>)
-> __query_result_t<_Env, _Query, _Args...>
struct __get_1st_env
{
return __query<_Query>()(__env_, static_cast<_Args &&>(__args)...);
}
template <class _Env>
using __has_query_t = __mbool<__queryable_with<_Env, _Query, _Args...>>;

_Env &__env_;
};
template <class... _Envs>
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
constexpr auto operator()(env<_Envs...> const &__env) const noexcept -> decltype(auto)
{
// count of elements that includes the first env that supports the query and all
// subsequent envs
STDEXEC_CONSTEXPR_LOCAL auto __index =
sizeof...(_Envs) - __mcall<__mfind_if<__q1<__has_query_t>, __msize>, _Envs...>::value;
if constexpr (__index < sizeof...(_Envs))
return STDEXEC::__get<__index>(__env);
}
};
} // namespace __detail

template <class _Env1, class _Env2>
struct env<_Env1, _Env2>
//////////////////////////////////////////////////////////////////////
// env
template <class... _Envs>
struct env : __tuple<_Envs...>
{
template <class _Query, class... _Args>
requires __queryable_with<_Env1, _Query, _Args...>
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
constexpr auto query(_Query, _Args &&...__args) const
noexcept(__nothrow_queryable_with<_Env1, _Query, _Args...>)
-> __query_result_t<_Env1, _Query, _Args...>
{
return __query<_Query>()(__env1_, static_cast<_Args &&>(__args)...);
}
using __1st_env_t = __call_result_t<__detail::__get_1st_env<_Query, _Args...>, env const &>;

template <class _Query, class... _Args>
requires __queryable_with<_Env1, _Query, _Args...>
|| __queryable_with<_Env2, _Query, _Args...>
requires __not_same_as<__1st_env_t<_Query, _Args...>, void>
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
constexpr auto query(_Query, _Args &&...__args) const
noexcept(__nothrow_queryable_with<_Env2, _Query, _Args...>)
-> __query_result_t<_Env2, _Query, _Args...>
noexcept(__nothrow_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...>)
-> __query_result_t<__1st_env_t<_Query, _Args...>, _Query, _Args...>
{
return __query<_Query>()(__env2_, static_cast<_Args &&>(__args)...);
auto const &__env = __detail::__get_1st_env<_Query, _Args...>()(*this);
return __query<_Query>()(__env, static_cast<_Args &&>(__args)...);
}

STDEXEC_ATTRIBUTE(no_unique_address) _Env1 __env1_;
STDEXEC_ATTRIBUTE(no_unique_address) _Env2 __env2_;
};

template <class _Env1, class _Env2, class... _Envs>
struct env<_Env1, _Env2, _Envs...> : env<env<_Env1, _Env2>, _Envs...>
{};

template <class... _Envs>
STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE env(_Envs...) -> env<std::unwrap_reference_t<_Envs>...>;

Expand Down
6 changes: 3 additions & 3 deletions include/stdexec/__detail/__tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,9 @@ namespace STDEXEC
//
namespace __tup
{
template <class _Fn, class _Tuple>
template <__is_tuple _Tuple, class _Fn>
STDEXEC_ATTRIBUTE(host, device, always_inline)
constexpr auto operator<<(_Tuple&& __tup, _Fn __fn) noexcept(__nothrow_move_constructible<_Fn>)
constexpr auto operator%(_Tuple&& __tup, _Fn __fn) noexcept(__nothrow_move_constructible<_Fn>)
{
return [&__tup, __fn = static_cast<_Fn&&>(__fn)]<class... _Us>(_Us&&... __us) noexcept(
__nothrow_applicable<_Fn, _Tuple, _Us...>) -> __apply_result_t<_Fn, _Tuple, _Us...>
Expand All @@ -465,7 +465,7 @@ namespace STDEXEC
template <class _Fn, class... _Tuples>
STDEXEC_ATTRIBUTE(host, device)
constexpr auto operator()(_Fn __fn, _Tuples&&... __tups) const
STDEXEC_AUTO_RETURN((static_cast<_Tuples&&>(__tups) << ... << __fn)())
STDEXEC_AUTO_RETURN((static_cast<_Tuples&&>(__tups) % ... % __fn)())
};
} // namespace __tup

Expand Down
57 changes: 57 additions & 0 deletions test/stdexec/queries/test_env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

namespace ex = STDEXEC;

STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")

namespace
{
template <typename T>
Expand Down Expand Up @@ -106,4 +108,59 @@ namespace
auto sch = ex::get_completion_scheduler<ex::set_value_t>(attrs, ex::env{});
CHECK(std::same_as<decltype(sch), ex::inline_scheduler>);
}

// Before v19, clang could not compile this test because of the large number of nested
// envs.
#if !STDEXEC_CLANG() || STDEXEC_CLANG_VERSION >= 19'00

# define DEFINE_QUERY(name) constexpr struct name ## _t : ex::__query<name ## _t> {} name{}

DEFINE_QUERY(query_0);
DEFINE_QUERY(query_1);
DEFINE_QUERY(query_2);
DEFINE_QUERY(query_3);
DEFINE_QUERY(query_4);
DEFINE_QUERY(query_5);
DEFINE_QUERY(query_6);
DEFINE_QUERY(query_7);
DEFINE_QUERY(query_8);
DEFINE_QUERY(query_9);
DEFINE_QUERY(query_10);
DEFINE_QUERY(query_11);
DEFINE_QUERY(query_12);

TEST_CASE("env supports lots of child envs without exceeding compiler limits", "[queries][env]")
{
auto env = ex::env{
ex::prop{ query_0, 0},
ex::prop{ query_1, 1},
ex::prop{ query_2, 2},
ex::prop{ query_3, 3},
ex::prop{ query_4, 4},
ex::prop{ query_5, 5},
ex::prop{ query_6, 6},
ex::prop{ query_7, 7},
ex::prop{ query_8, 8},
ex::prop{ query_9, 9},
ex::prop{query_10, 10},
ex::prop{query_11, 11},
ex::prop{query_12, 12}
};

CHECK(env.query(query_0) == 0);
CHECK(env.query(query_1) == 1);
CHECK(env.query(query_2) == 2);
CHECK(env.query(query_3) == 3);
CHECK(env.query(query_4) == 4);
CHECK(env.query(query_5) == 5);
CHECK(env.query(query_6) == 6);
CHECK(env.query(query_7) == 7);
CHECK(env.query(query_8) == 8);
CHECK(env.query(query_9) == 9);
CHECK(env.query(query_10) == 10);
CHECK(env.query(query_11) == 11);
CHECK(env.query(query_12) == 12);
}

#endif
} // namespace
Loading