Skip to content

Commit

Permalink
Merge pull request #207 from tcbrindle/pr/default_sequence_traits
Browse files Browse the repository at this point in the history
Add default_sequence_traits
  • Loading branch information
tcbrindle authored Aug 21, 2024
2 parents bbdd46f + 4bb55a8 commit 6e4b9dd
Show file tree
Hide file tree
Showing 45 changed files with 243 additions and 183 deletions.
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

0 comments on commit 6e4b9dd

Please sign in to comment.