Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add default_sequence_traits #207

Merged
merged 10 commits into from
Aug 21, 2024
161 changes: 116 additions & 45 deletions include/flux/core/concepts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@
#include <compare>
#include <concepts>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <tuple>
#include <type_traits>

// clang-format off

// Workaround GCC11/12 ICE in sequence concept definition below
#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 13)
#define FLUX_COMPILER_IS_GCC12
#endif

#if defined(__cpp_lib_ranges_zip) && (__cpp_lib_ranges_zip >= 202110L)
#define FLUX_HAVE_CPP23_TUPLE_COMMON_REF
#endif
Expand Down Expand Up @@ -46,6 +54,9 @@ FLUX_EXPORT
template <typename T>
struct sequence_traits;

FLUX_EXPORT
struct default_sequence_traits;

namespace detail {

template <typename T>
Expand Down Expand Up @@ -73,29 +84,6 @@ template <has_element_type T>
requires requires { typename traits_t<T>::value_type; }
struct value_type<T> { using type = typename traits_t<T>::value_type; };

template <has_element_type T>
requires requires { traits_t<T>::using_primary_template; } &&
requires { typename T::value_type; }
struct value_type<T> { using type = typename T::value_type; };

template <has_element_type T>
struct rvalue_element_type {
using type = std::conditional_t<std::is_lvalue_reference_v<element_t<T>>,
std::add_rvalue_reference_t<std::remove_reference_t<element_t<T>>>,
element_t<T>>;
};

template <typename Seq>
concept has_move_at = requires (Seq& seq, cursor_t<Seq> const& cur) {
{ traits_t<Seq>::move_at(seq, cur) };
};

template <has_element_type T>
requires has_move_at<T>
struct rvalue_element_type<T> {
using type = decltype(traits_t<T>::move_at(FLUX_DECLVAL(T&), FLUX_DECLVAL(cursor_t<T> const&)));
};

} // namespace detail

FLUX_EXPORT
Expand All @@ -110,7 +98,7 @@ using index_t = flux::config::int_type;

FLUX_EXPORT
template <typename Seq>
using rvalue_element_t = typename detail::rvalue_element_type<Seq>::type;
using rvalue_element_t = decltype(detail::traits_t<Seq>::move_at(FLUX_DECLVAL(Seq&), FLUX_DECLVAL(cursor_t<Seq> const&)));

FLUX_EXPORT
template <typename Seq>
Expand All @@ -136,7 +124,7 @@ template <typename T>
concept can_reference = requires { typename with_ref<T>; };

template <typename Seq, typename Traits = sequence_traits<std::remove_cvref_t<Seq>>>
concept sequence_concept =
concept sequence_requirements =
requires (Seq& seq) {
{ Traits::first(seq) } -> cursor;
} &&
Expand All @@ -146,7 +134,21 @@ concept sequence_concept =
} &&
requires (Seq& seq, cursor_t<Seq>& cur) {
{ Traits::inc(seq, cur) };
};

template <typename Seq, typename Traits = sequence_traits<std::remove_cvref_t<Seq>>>
concept sequence_concept =
sequence_requirements<Seq> &&
requires (Seq& seq, cursor_t<Seq> const& cur) {
{ Traits::read_at_unchecked(seq, cur) } -> std::same_as<element_t<Seq>>;
{ Traits::move_at(seq, cur) } -> can_reference;
{ Traits::move_at_unchecked(seq, cur) } -> std::same_as<rvalue_element_t<Seq>>;
} &&
#ifndef FLUX_COMPILER_IS_GCC12
requires (Seq& seq, bool (*pred)(element_t<Seq>)) {
{ Traits::for_each_while(seq, pred) } -> std::same_as<cursor_t<Seq>>;
} &&
#endif
#ifdef FLUX_HAVE_CPP23_TUPLE_COMMON_REF
std::common_reference_with<element_t<Seq>&&, value_t<Seq>&> &&
std::common_reference_with<rvalue_element_t<Seq>&&, value_t<Seq> const&> &&
Expand Down Expand Up @@ -180,8 +182,7 @@ concept multipass_sequence =
namespace detail {

template <typename Seq, typename Traits = sequence_traits<std::remove_cvref_t<Seq>>>
concept bidirectional_sequence_concept =
multipass_sequence<Seq> &&
concept bidirectional_sequence_requirements =
requires (Seq& seq, cursor_t<Seq>& cur) {
{ Traits::dec(seq, cur) };
};
Expand All @@ -190,13 +191,13 @@ concept bidirectional_sequence_concept =

FLUX_EXPORT
template <typename Seq>
concept bidirectional_sequence = detail::bidirectional_sequence_concept<Seq>;
concept bidirectional_sequence = multipass_sequence<Seq> && detail::bidirectional_sequence_requirements<Seq>;

namespace detail {

template <typename Seq, typename Traits = sequence_traits<std::remove_cvref_t<Seq>>>
concept random_access_sequence_concept =
bidirectional_sequence<Seq> && ordered_cursor<cursor_t<Seq>> &&
concept random_access_sequence_requirements =
ordered_cursor<cursor_t<Seq>> &&
requires (Seq& seq, cursor_t<Seq>& cur, distance_t offset) {
{ Traits::inc(seq, cur, offset) };
} &&
Expand All @@ -208,13 +209,14 @@ concept random_access_sequence_concept =

FLUX_EXPORT
template <typename Seq>
concept random_access_sequence = detail::random_access_sequence_concept<Seq>;
concept random_access_sequence =
bidirectional_sequence<Seq> &&
detail::random_access_sequence_requirements<Seq>;

namespace detail {

template <typename Seq, typename Traits = sequence_traits<std::remove_cvref_t<Seq>>>
concept bounded_sequence_concept =
sequence<Seq> &&
concept bounded_sequence_requirements =
requires (Seq& seq) {
{ Traits::last(seq) } -> std::same_as<cursor_t<Seq>>;
};
Expand All @@ -223,14 +225,12 @@ concept bounded_sequence_concept =

FLUX_EXPORT
template <typename Seq>
concept bounded_sequence = detail::bounded_sequence_concept<Seq>;
concept bounded_sequence = sequence<Seq> && detail::bounded_sequence_requirements<Seq>;

namespace detail {

template <typename Seq, typename Traits = sequence_traits<std::remove_cvref_t<Seq>>>
concept contiguous_sequence_concept =
random_access_sequence<Seq> &&
bounded_sequence<Seq> &&
concept contiguous_sequence_requirements =
std::is_lvalue_reference_v<element_t<Seq>> &&
std::same_as<value_t<Seq>, std::remove_cvref_t<element_t<Seq>>> &&
requires (Seq& seq) {
Expand All @@ -241,24 +241,24 @@ concept contiguous_sequence_concept =

FLUX_EXPORT
template <typename Seq>
concept contiguous_sequence = detail::contiguous_sequence_concept<Seq>;
concept contiguous_sequence =
random_access_sequence<Seq> &&
bounded_sequence<Seq> &&
detail::contiguous_sequence_requirements<Seq>;

namespace detail {

template <typename Seq, typename Traits = sequence_traits<std::remove_cvref_t<Seq>>>
concept sized_sequence_concept =
sequence<Seq> &&
(requires (Seq& seq) {
concept sized_sequence_requirements =
requires (Seq& seq) {
{ Traits::size(seq) } -> std::convertible_to<distance_t>;
} || (
random_access_sequence<Seq> && bounded_sequence<Seq>
));
};

} // namespace detail

FLUX_EXPORT
template <typename Seq>
concept sized_sequence = detail::sized_sequence_concept<Seq>;
concept sized_sequence = sequence<Seq> && detail::sized_sequence_requirements<Seq>;

FLUX_EXPORT
template <typename Seq, typename T>
Expand Down Expand Up @@ -366,6 +366,77 @@ concept derived_from_inline_sequence_base = requires(T t) {
/*
* Default sequence_traits implementation
*/

struct default_sequence_traits {

template <typename Self>
requires detail::sequence_requirements<Self>
static constexpr auto read_at_unchecked(Self& self, cursor_t<Self> const& cur)
-> decltype(detail::traits_t<Self>::read_at(self, cur))
{
return detail::traits_t<Self>::read_at(self, cur);
}

template <typename Self>
requires detail::sequence_requirements<Self>
static constexpr auto move_at(Self& self, cursor_t<Self> const& cur)
-> std::conditional_t<std::is_lvalue_reference_v<element_t<Self>>,
std::add_rvalue_reference_t<std::remove_reference_t<element_t<Self>>>,
element_t<Self>>
{
using Traits = detail::traits_t<Self>;
if constexpr (std::is_lvalue_reference_v<element_t<Self>>) {
return std::move(Traits::read_at(self, cur));
} else {
return Traits::read_at(self, cur);
}
}

template <typename Self>
requires detail::sequence_requirements<Self>
static constexpr auto move_at_unchecked(Self& self, cursor_t<Self> const& cur)
-> decltype(detail::traits_t<Self>::move_at(self, cur))
{
return detail::traits_t<Self>::move_at(self, cur);
}

template <typename Self>
requires detail::random_access_sequence_requirements<Self> &&
detail::bounded_sequence_requirements<Self>
static constexpr auto size(Self& self) -> distance_t
{
using Traits = detail::traits_t<Self>;
return Traits::distance(self, Traits::first(self), Traits::last(self));
}

template <typename Self, typename Pred>
requires detail::sequence_requirements<Self>
static constexpr auto for_each_while(Self& self, Pred&& pred) -> cursor_t<Self>
{
using Traits = detail::traits_t<Self>;

auto cur = Traits::first(self);
if constexpr (bounded_sequence<Self> && regular_cursor<cursor_t<Self>>) {
auto const last = Traits::last(self);
while (cur != last) {
if (!std::invoke(pred, Traits::read_at(self, cur))) {
break;
}
Traits::inc(self, cur);
}
} else {
while (!Traits::is_last(self, cur)) {
if (!std::invoke(pred, Traits::read_at(self, cur))) {
break;
}
Traits::inc(self, cur);
}
}
return cur;
}

};

namespace detail {

template <typename T>
Expand Down
6 changes: 3 additions & 3 deletions include/flux/core/default_impls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace flux {
* Default implementation for C arrays of known bound
*/
template <typename T, index_t N>
struct sequence_traits<T[N]> {
struct sequence_traits<T[N]> : default_sequence_traits {

static constexpr auto first(auto const&) -> index_t { return index_t{0}; }

Expand Down Expand Up @@ -82,7 +82,7 @@ struct sequence_traits<T[N]> {
* Default implementation for std::reference_wrapper<T>
*/
template <sequence Seq>
struct sequence_traits<std::reference_wrapper<Seq>> {
struct sequence_traits<std::reference_wrapper<Seq>> : default_sequence_traits {

using self_t = std::reference_wrapper<Seq>;

Expand Down Expand Up @@ -172,7 +172,7 @@ template <typename R>
std::ranges::sized_range<R> &&
std::ranges::contiguous_range<R const> &&
std::ranges::sized_range<R const>)
struct sequence_traits<R> {
struct sequence_traits<R> : default_sequence_traits {

using value_type = std::ranges::range_value_t<R>;

Expand Down
54 changes: 4 additions & 50 deletions include/flux/core/sequence_access.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,7 @@ struct size_fn {
[[nodiscard]]
constexpr auto operator()(Seq&& seq) const -> distance_t
{
if constexpr (requires { traits_t<Seq>::size(seq); }) {
return traits_t<Seq>::size(seq);
} else {
static_assert(bounded_sequence<Seq> && random_access_sequence<Seq>);
return distance_fn{}(seq, first_fn{}(seq), last_fn{}(seq));
}
return traits_t<Seq>::size(seq);
}
};

Expand All @@ -135,74 +130,33 @@ struct usize_fn {
}
};

template <typename Seq>
concept has_custom_move_at =
sequence<Seq> &&
requires (Seq& seq, cursor_t<Seq> const& cur) {
{ traits_t<Seq>::move_at(seq, cur) };
};

struct move_at_fn {
template <sequence Seq>
[[nodiscard]]
constexpr auto operator()(Seq& seq, cursor_t<Seq> const& cur) const
-> rvalue_element_t<Seq>
{
if constexpr (has_custom_move_at<Seq>) {
return traits_t<Seq>::move_at(seq, cur);
} else {
if constexpr (std::is_lvalue_reference_v<element_t<Seq>>) {
return std::move(read_at_fn{}(seq, cur));
} else {
return read_at_fn{}(seq, cur);
}
}
return traits_t<Seq>::move_at(seq, cur);
}
};

template <typename Seq>
concept has_custom_read_at_unchecked =
sequence<Seq> &&
requires (Seq& seq, cursor_t<Seq> const& cur) {
{ traits_t<Seq>::read_at_unchecked(seq, cur) } -> std::same_as<element_t<Seq>>;
};

struct read_at_unchecked_fn {
template <sequence Seq>
[[nodiscard]]
constexpr auto operator()(Seq& seq, cursor_t<Seq> const& cur) const
-> element_t<Seq>
{
if constexpr (has_custom_read_at_unchecked<Seq>) {
return traits_t<Seq>::read_at_unchecked(seq, cur);
} else {
return read_at_fn{}(seq, cur);
}
return traits_t<Seq>::read_at_unchecked(seq, cur);
}
};

template <typename Seq>
concept has_custom_move_at_unchecked =
sequence<Seq> &&
requires (Seq& seq, cursor_t<Seq> const& cur) {
{ traits_t<Seq>::move_at_unchecked(seq, cur) } -> std::same_as<rvalue_element_t<Seq>>;
};

struct move_at_unchecked_fn {
template <sequence Seq>
[[nodiscard]]
constexpr auto operator()(Seq& seq, cursor_t<Seq> const& cur) const
-> rvalue_element_t<Seq>
{
if constexpr (has_custom_move_at_unchecked<Seq>) {
return traits_t<Seq>::move_at_unchecked(seq, cur);
} else if constexpr (has_custom_move_at<Seq>) {
return move_at_fn{}(seq, cur);
} else if constexpr (std::is_lvalue_reference_v<element_t<Seq>>){
return std::move(read_at_unchecked_fn{}(seq, cur));
} else {
return read_at_unchecked_fn{}(seq, cur);
}
return traits_t<Seq>::move_at_unchecked(seq, cur);
}
};

Expand Down
2 changes: 1 addition & 1 deletion include/flux/op/adjacent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace flux {
namespace detail {

template <typename Base, distance_t N>
struct adjacent_sequence_traits_base {
struct adjacent_sequence_traits_base : default_sequence_traits {
protected:
struct cursor_type {
std::array<cursor_t<Base>, N> arr{};
Expand Down
Loading