From 95346e375db70d42bf9fc2233afd9c0bbc5c6d87 Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Tue, 13 May 2025 10:57:11 +0100 Subject: [PATCH 01/66] Rename distance_t -> int_t --- .../multidimensional_memset_benchmark.cpp | 18 +++---- ...tidimensional_memset_benchmark_kernels.cpp | 27 ++++++----- include/flux/adaptor/adjacent.hpp | 34 ++++++------- include/flux/adaptor/cartesian_base.hpp | 48 +++++++++---------- include/flux/adaptor/cartesian_power.hpp | 6 +-- include/flux/adaptor/cartesian_power_map.hpp | 6 +-- include/flux/adaptor/chain.hpp | 19 ++++---- include/flux/adaptor/chunk.hpp | 33 +++++++------ include/flux/adaptor/cursors.hpp | 6 +-- include/flux/adaptor/cycle.hpp | 26 +++++----- include/flux/adaptor/drop.hpp | 9 ++-- include/flux/adaptor/reverse.hpp | 6 +-- include/flux/adaptor/scan.hpp | 4 +- include/flux/adaptor/scan_first.hpp | 2 +- include/flux/adaptor/slide.hpp | 21 ++++---- include/flux/adaptor/stride.hpp | 36 +++++++------- include/flux/adaptor/take.hpp | 18 +++---- include/flux/adaptor/zip.hpp | 4 +- include/flux/algorithm/count.hpp | 14 +++--- include/flux/algorithm/detail/heap_ops.hpp | 9 ++-- include/flux/algorithm/detail/pdqsort.hpp | 13 +++-- include/flux/algorithm/ends_with.hpp | 4 +- include/flux/core/concepts.hpp | 10 ++-- include/flux/core/default_impls.hpp | 32 ++++++------- include/flux/core/inline_sequence_base.hpp | 12 +++-- include/flux/core/operation_requirements.hpp | 4 +- include/flux/core/ref.hpp | 2 +- include/flux/core/sequence_access.hpp | 18 ++++--- include/flux/core/sequence_iterator.hpp | 2 +- include/flux/sequence/array_ptr.hpp | 22 +++------ include/flux/sequence/empty.hpp | 3 +- include/flux/sequence/iota.hpp | 25 ++++------ include/flux/sequence/range.hpp | 16 +++---- include/flux/sequence/repeat.hpp | 14 +++--- include/flux/sequence/single.hpp | 3 +- test/test_apply.cpp | 5 +- test/test_cartesian_product.cpp | 38 ++++++++------- test/test_cartesian_product_map.cpp | 2 +- test/test_chunk.cpp | 4 +- test/test_cycle.cpp | 4 +- test/test_range_iface.cpp | 8 ++-- test/test_repeat.cpp | 4 +- test/test_sort.cpp | 11 +++-- test/test_stride.cpp | 4 +- 44 files changed, 291 insertions(+), 315 deletions(-) diff --git a/benchmark/multidimensional_memset_benchmark.cpp b/benchmark/multidimensional_memset_benchmark.cpp index 5174bc22..bf1f88a4 100644 --- a/benchmark/multidimensional_memset_benchmark.cpp +++ b/benchmark/multidimensional_memset_benchmark.cpp @@ -18,19 +18,21 @@ namespace an = ankerl::nanobench; // Kernels are placed in a separate translation unit to prevent compilers from // optimizing them based on the input that we'll be giving them and to make it // easier to study their compiled assembly. -extern void memset_2d_reference(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_2d_std_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_2d_flux_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_diagonal_2d_reference(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M); -extern void memset_diagonal_2d_flux_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M); +extern void memset_2d_reference(double* A, flux::int_t N, flux::int_t M); +extern void memset_2d_std_cartesian_product_iota(double* A, flux::int_t N, flux::int_t M); +extern void memset_2d_flux_cartesian_product_iota(double* A, flux::int_t N, flux::int_t M); +extern void memset_diagonal_2d_reference(double* A, flux::int_t N, flux::int_t M); +extern void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::int_t N, + flux::int_t M); +extern void memset_diagonal_2d_flux_cartesian_product_iota_filter(double* A, flux::int_t N, + flux::int_t M); int main(int argc, char** argv) { int const n_iters = argc > 1 ? std::atoi(argv[1]) : 40; - constexpr flux::distance_t N = 1024; - constexpr flux::distance_t M = 2048; + constexpr flux::int_t N = 1024; + constexpr flux::int_t M = 2048; std::vector A(N * M); const auto run_benchmark = diff --git a/benchmark/multidimensional_memset_benchmark_kernels.cpp b/benchmark/multidimensional_memset_benchmark_kernels.cpp index 4b3706ce..69ce8ba6 100644 --- a/benchmark/multidimensional_memset_benchmark_kernels.cpp +++ b/benchmark/multidimensional_memset_benchmark_kernels.cpp @@ -18,14 +18,16 @@ #include #include -void memset_2d_reference(double* A, flux::distance_t N, flux::distance_t M) +void memset_2d_reference(double* A, flux::int_t N, flux::int_t M) { - for (flux::distance_t i = 0; i != N; ++i) - for (flux::distance_t j = 0; j != M; ++j) + for (flux::int_t i = 0; i != N; ++i) { + for (flux::int_t j = 0; j != M; ++j) { A[i * M + j] = 0.0; + } + } } -void memset_2d_std_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M) +void memset_2d_std_cartesian_product_iota(double* A, flux::int_t N, flux::int_t M) { std::ranges::for_each( std::views::cartesian_product(std::views::iota(0, N), std::views::iota(0, M)), @@ -34,7 +36,7 @@ void memset_2d_std_cartesian_product_iota(double* A, flux::distance_t N, flux::d })); } -void memset_2d_flux_cartesian_product_iota(double* A, flux::distance_t N, flux::distance_t M) +void memset_2d_flux_cartesian_product_iota(double* A, flux::int_t N, flux::int_t M) { flux::for_each( flux::cartesian_product(flux::ints(0, N), flux::ints(0, M)), @@ -43,14 +45,17 @@ void memset_2d_flux_cartesian_product_iota(double* A, flux::distance_t N, flux:: })); } -void memset_diagonal_2d_reference(double* A, flux::distance_t N, flux::distance_t M) +void memset_diagonal_2d_reference(double* A, flux::int_t N, flux::int_t M) { - for (flux::distance_t i = 0; i != N; ++i) - for (flux::distance_t j = 0; j != M; ++j) - if (i == j) A[i * M + j] = 0.0; + for (flux::int_t i = 0; i != N; ++i) { + for (flux::int_t j = 0; j != M; ++j) { + if (i == j) + A[i * M + j] = 0.0; + } + } } -void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M) +void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::int_t N, flux::int_t M) { std::ranges::for_each( std::views::cartesian_product(std::views::iota(0, N), std::views::iota(0, M)) @@ -60,7 +65,7 @@ void memset_diagonal_2d_std_cartesian_product_iota_filter(double* A, flux::dista })); } -void memset_diagonal_2d_flux_cartesian_product_iota_filter(double* A, flux::distance_t N, flux::distance_t M) +void memset_diagonal_2d_flux_cartesian_product_iota_filter(double* A, flux::int_t N, flux::int_t M) { flux::for_each( flux::cartesian_product(flux::ints(0, N), flux::ints(0, M)) diff --git a/include/flux/adaptor/adjacent.hpp b/include/flux/adaptor/adjacent.hpp index 5c5223da..acabacb9 100644 --- a/include/flux/adaptor/adjacent.hpp +++ b/include/flux/adaptor/adjacent.hpp @@ -19,7 +19,7 @@ namespace flux { namespace detail { -template +template struct adjacent_sequence_traits_base : default_sequence_traits { protected: struct cursor_type { @@ -91,7 +91,7 @@ struct adjacent_sequence_traits_base : default_sequence_traits { }, cur.arr); } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) -> void + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) -> void requires random_access_sequence { std::apply([&self, offset](auto&... curs) { @@ -100,21 +100,21 @@ struct adjacent_sequence_traits_base : default_sequence_traits { } static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t + -> int_t requires random_access_sequence { return flux::distance(self.base_, from.arr.back(), to.arr.back()); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { auto s = (flux::size(self.base_) - N) + 1; - return (cmp::max)(s, distance_t{0}); + return (cmp::max)(s, int_t {0}); } }; -template +template struct adjacent_adaptor : inline_sequence_base> { private: Base base_; @@ -175,7 +175,7 @@ struct adjacent_adaptor : inline_sequence_base> { }; }; -template +template struct adjacent_map_adaptor : inline_sequence_base> { private: Base base_; @@ -202,7 +202,7 @@ struct adjacent_map_adaptor : inline_sequence_base +template struct adjacent_fn { template requires multipass_sequence @@ -213,7 +213,7 @@ struct adjacent_fn { } }; -template +template struct adjacent_map_fn { template requires multipass_sequence && @@ -229,21 +229,21 @@ struct adjacent_map_fn { } // namespace detail FLUX_EXPORT -template - requires (N > 0) -inline constexpr auto adjacent = detail::adjacent_fn{}; +template + requires(N > 0) +inline constexpr auto adjacent = detail::adjacent_fn {}; FLUX_EXPORT inline constexpr auto pairwise = adjacent<2>; FLUX_EXPORT -template - requires (N > 0) -inline constexpr auto adjacent_map = detail::adjacent_map_fn{}; +template + requires(N > 0) +inline constexpr auto adjacent_map = detail::adjacent_map_fn {}; FLUX_EXPORT inline constexpr auto pairwise_map = adjacent_map<2>; template -template +template constexpr auto inline_sequence_base::adjacent() && requires multipass_sequence { @@ -258,7 +258,7 @@ constexpr auto inline_sequence_base::pairwise() && } template -template +template requires multipass_sequence constexpr auto inline_sequence_base::adjacent_map(Func func) && { diff --git a/include/flux/adaptor/cartesian_base.hpp b/include/flux/adaptor/cartesian_base.hpp index c61e4a42..2ee0b469 100644 --- a/include/flux/adaptor/cartesian_base.hpp +++ b/include/flux/adaptor/cartesian_base.hpp @@ -36,7 +36,7 @@ struct tuple_repeated { using repeater = T; template - static auto make_tuple(std::index_sequence) -> std::tuple...>; + static auto make_tuple(std::index_sequence) -> std::tuple...>; using type = decltype(make_tuple(std::make_index_sequence{})); }; @@ -109,8 +109,8 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto ra_inc_impl(Self& self, cursor_t& cur, distance_t offset) - -> cursor_t& + static constexpr auto ra_inc_impl(Self& self, cursor_t& cur, int_t offset) + -> cursor_t& { if (offset == 0) { return cur; @@ -129,7 +129,7 @@ struct cartesian_traits_base_impl : default_sequence_traits { // Correct for negative index which may happen when underflowing. if (new_index < 0) { new_index = num::add(new_index, this_size); - offset = num::sub(offset, flux::distance_t(1)); + offset = num::sub(offset, flux::int_t(1)); } // Call the next level down if necessary. @@ -146,9 +146,8 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto distance_impl(Self& self, - cursor_t const& from, - cursor_t const& to) -> distance_t + static constexpr auto distance_impl(Self& self, cursor_t const& from, + cursor_t const& to) -> int_t { if constexpr (I == 0) { return flux::distance(get_base<0>(self), std::get<0>(from), std::get<0>(to)); @@ -231,21 +230,21 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto size(Self& self) -> distance_t - requires (CartesianKind == cartesian_kind::product - && (sized_sequence && ...)) + static constexpr auto size(Self& self) -> int_t + requires(CartesianKind == cartesian_kind::product && (sized_sequence && ...)) { - return std::apply([](auto& base0, auto&... bases) { - distance_t sz = flux::size(base0); - ((sz = num::mul(sz, flux::size(bases))), ...); - return sz; - }, self.bases_); + return std::apply( + [](auto& base0, auto&... bases) { + int_t sz = flux::size(base0); + ((sz = num::mul(sz, flux::size(bases))), ...); + return sz; + }, + self.bases_); } template - static constexpr auto size(Self& self) -> distance_t - requires (CartesianKind == cartesian_kind::power - && (sized_sequence && ...)) + static constexpr auto size(Self& self) -> int_t + requires(CartesianKind == cartesian_kind::power && (sized_sequence && ...)) { return checked_pow(flux::size(self.base_), Arity); } @@ -267,9 +266,8 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto inc(Self& self, cursor_t& cur, distance_t offset) -> cursor_t& - requires ((random_access_sequence && ...) && - (sized_sequence && ...)) + static constexpr auto inc(Self& self, cursor_t& cur, int_t offset) -> cursor_t& + requires((random_access_sequence && ...) && (sized_sequence && ...)) { return ra_inc_impl(self, cur, offset); } @@ -301,11 +299,9 @@ struct cartesian_traits_base_impl : default_sequence_traits { } template - static constexpr auto distance(Self& self, - cursor_t const& from, - cursor_t const& to) -> distance_t - requires ((random_access_sequence && ...) && - (sized_sequence && ...)) + static constexpr auto distance(Self& self, cursor_t const& from, cursor_t const& to) + -> int_t + requires((random_access_sequence && ...) && (sized_sequence && ...)) { return distance_impl(self, from, to); } diff --git a/include/flux/adaptor/cartesian_power.hpp b/include/flux/adaptor/cartesian_power.hpp index 5dd2f5c5..419c4f01 100644 --- a/include/flux/adaptor/cartesian_power.hpp +++ b/include/flux/adaptor/cartesian_power.hpp @@ -61,9 +61,9 @@ struct cartesian_power_fn { } // end namespace detail FLUX_EXPORT -template - requires (N >= 0) -inline constexpr auto cartesian_power = detail::cartesian_power_fn{}; +template + requires(N >= 0) +inline constexpr auto cartesian_power = detail::cartesian_power_fn {}; } // end namespace flux diff --git a/include/flux/adaptor/cartesian_power_map.hpp b/include/flux/adaptor/cartesian_power_map.hpp index 41737c47..1eaffa59 100644 --- a/include/flux/adaptor/cartesian_power_map.hpp +++ b/include/flux/adaptor/cartesian_power_map.hpp @@ -55,9 +55,9 @@ struct cartesian_power_map_fn } // namespace detail FLUX_EXPORT -template - requires (N >= 0) -inline constexpr auto cartesian_power_map = detail::cartesian_power_map_fn{}; +template + requires(N >= 0) +inline constexpr auto cartesian_power_map = detail::cartesian_power_map_fn {}; } // namespace flux diff --git a/include/flux/adaptor/chain.hpp b/include/flux/adaptor/chain.hpp index 9051dd76..754428d7 100644 --- a/include/flux/adaptor/chain.hpp +++ b/include/flux/adaptor/chain.hpp @@ -208,9 +208,7 @@ struct sequence_traits> : default_sequence_trait } template - static constexpr auto inc_ra_impl(Self& self, cursor_type& cur, - distance_t offset) - -> cursor_type& + static constexpr auto inc_ra_impl(Self& self, cursor_type& cur, int_t offset) -> cursor_type& { if constexpr (N < End) { if (N < cur.index()) { @@ -300,11 +298,10 @@ struct sequence_traits> : default_sequence_trait } template - static constexpr auto distance(Self& self, cursor_type const& from, - cursor_type const& to) - -> distance_t - requires (random_access_sequence> && ...) && - (bounded_sequence> && ...) + static constexpr auto distance(Self& self, cursor_type const& from, cursor_type const& to) + -> int_t + requires(random_access_sequence> && ...) + && (bounded_sequence> && ...) { if (from.index() <= to.index()) { return distance_impl<0>(self, from, to); @@ -314,9 +311,9 @@ struct sequence_traits> : default_sequence_trait } template - static constexpr auto inc(Self& self, cursor_type& cur, distance_t offset) - requires (random_access_sequence> && ...) && - (bounded_sequence> && ...) + static constexpr auto inc(Self& self, cursor_type& cur, int_t offset) + requires(random_access_sequence> && ...) + && (bounded_sequence> && ...) { inc_ra_impl<0>(self, cur, offset); } diff --git a/include/flux/adaptor/chunk.hpp b/include/flux/adaptor/chunk.hpp index e215e91b..473356cb 100644 --- a/include/flux/adaptor/chunk.hpp +++ b/include/flux/adaptor/chunk.hpp @@ -19,12 +19,12 @@ template struct chunk_adaptor : inline_sequence_base> { private: Base base_; - distance_t chunk_sz_; + int_t chunk_sz_; optional> cur_ = nullopt; - distance_t rem_ = chunk_sz_; + int_t rem_ = chunk_sz_; public: - constexpr chunk_adaptor(decays_to auto&& base, distance_t chunk_sz) + constexpr chunk_adaptor(decays_to auto&& base, int_t chunk_sz) : base_(FLUX_FWD(base)), chunk_sz_(chunk_sz) {} @@ -124,7 +124,7 @@ struct chunk_adaptor : inline_sequence_base> { return value_type(self); } - static constexpr auto size(self_t& self) -> distance_t + static constexpr auto size(self_t& self) -> int_t requires sized_sequence { auto s = flux::size(self.base_); @@ -137,10 +137,10 @@ template struct chunk_adaptor : inline_sequence_base> { private: Base base_; - distance_t chunk_sz_; + int_t chunk_sz_; public: - constexpr chunk_adaptor(decays_to auto&& base, distance_t chunk_sz) + constexpr chunk_adaptor(decays_to auto&& base, int_t chunk_sz) : base_(FLUX_FWD(base)), chunk_sz_(chunk_sz) {} @@ -176,7 +176,7 @@ struct chunk_adaptor : inline_sequence_base> { return flux::last(self.base_); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { auto s = flux::size(self.base_); @@ -189,10 +189,10 @@ template struct chunk_adaptor : inline_sequence_base> { private: Base base_; - distance_t chunk_sz_; + int_t chunk_sz_; public: - constexpr chunk_adaptor(decays_to auto&& base, distance_t chunk_sz) + constexpr chunk_adaptor(decays_to auto&& base, int_t chunk_sz) : base_(FLUX_FWD(base)), chunk_sz_(chunk_sz) {} @@ -201,7 +201,7 @@ struct chunk_adaptor : inline_sequence_base> { private: struct cursor_type { cursor_t cur{}; - distance_t missing = 0; + int_t missing = 0; friend constexpr auto operator==(cursor_type const& lhs, cursor_type const& rhs) -> bool { @@ -259,15 +259,15 @@ struct chunk_adaptor : inline_sequence_base> { static constexpr auto last(auto& self) -> cursor_type requires bounded_sequence && sized_sequence { - distance_t missing = - (self.chunk_sz_ - flux::size(self.base_) % self.chunk_sz_) % self.chunk_sz_; + int_t missing + = (self.chunk_sz_ - flux::size(self.base_) % self.chunk_sz_) % self.chunk_sz_; return cursor_type{ .cur = flux::last(self.base_), .missing = missing }; } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { auto s = flux::size(self.base_); @@ -275,13 +275,13 @@ struct chunk_adaptor : inline_sequence_base> { } static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t + -> int_t requires random_access_sequence { return (flux::distance(self.base_, from.cur, to.cur) - from.missing + to.missing)/self.chunk_sz_; } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) -> void + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) -> void requires random_access_sequence { if (offset > 0) { @@ -301,8 +301,7 @@ struct chunk_fn { -> sequence auto { FLUX_ASSERT(chunk_sz > 0); - return chunk_adaptor>(FLUX_FWD(seq), - num::checked_cast(chunk_sz)); + return chunk_adaptor>(FLUX_FWD(seq), num::checked_cast(chunk_sz)); } }; diff --git a/include/flux/adaptor/cursors.hpp b/include/flux/adaptor/cursors.hpp index d77e1966..68f794fe 100644 --- a/include/flux/adaptor/cursors.hpp +++ b/include/flux/adaptor/cursors.hpp @@ -53,14 +53,14 @@ struct cursors_adaptor : inline_sequence_base> { flux::dec(self.base_, cur); } - static constexpr auto inc(auto& self, cursor_t& cur, distance_t offset) -> void + static constexpr auto inc(auto& self, cursor_t& cur, int_t offset) -> void requires random_access_sequence { flux::inc(self.base_, cur, offset); } static constexpr auto distance(auto& self, cursor_t const& from, - cursor_t const& to) -> distance_t + cursor_t const& to) -> int_t requires random_access_sequence { return flux::distance(self.base_, from, to); @@ -72,7 +72,7 @@ struct cursors_adaptor : inline_sequence_base> { return flux::last(self.base_); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { return flux::size(self.base_); diff --git a/include/flux/adaptor/cycle.hpp b/include/flux/adaptor/cycle.hpp index a0e98d71..fd64b9e1 100644 --- a/include/flux/adaptor/cycle.hpp +++ b/include/flux/adaptor/cycle.hpp @@ -156,9 +156,9 @@ struct cycle_adaptor : inline_sequence_base> { flux::dec(self.base_, cur.base_cur); } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) - requires random_access_sequence && - bounded_sequence + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) + requires random_access_sequence + && bounded_sequence { auto const first = flux::first(self.base_); @@ -180,13 +180,12 @@ struct cycle_adaptor : inline_sequence_base> { cur.base_cur = flux::next(self.base_, first, off); } - static constexpr auto distance(auto& self, - cursor_type const& from, - cursor_type const& to) -> distance_t - requires random_access_sequence && - sized_sequence + static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) + -> int_t + requires random_access_sequence + && sized_sequence { - auto dist = num::cast(to.n) - num::cast(from.n); + auto dist = num::cast(to.n) - num::cast(from.n); dist = num::mul(dist, flux::size(self.base_)); return num::add(dist, flux::distance(self.base_, from.base_cur, to.base_cur)); @@ -200,11 +199,10 @@ struct cycle_adaptor : inline_sequence_base> { .n = self.data_.count}; } - static constexpr auto size(auto& self) -> distance_t - requires (!IsInfinite && sized_sequence) + static constexpr auto size(auto& self) -> int_t + requires(!IsInfinite && sized_sequence) { - return num::mul(flux::size(self.base_), - num::cast(self.data_.count)); + return num::mul(flux::size(self.base_), num::cast(self.data_.count)); } }; }; @@ -228,7 +226,7 @@ struct cycle_fn { constexpr auto operator()(Seq&& seq, num::integral auto count) const -> multipass_sequence auto { - auto c = num::checked_cast(count); + auto c = num::checked_cast(count); if (c < 0) { runtime_error("Negative count passed to cycle()"); } diff --git a/include/flux/adaptor/drop.hpp b/include/flux/adaptor/drop.hpp index 52173b34..1e796cef 100644 --- a/include/flux/adaptor/drop.hpp +++ b/include/flux/adaptor/drop.hpp @@ -17,10 +17,10 @@ template struct drop_adaptor : inline_sequence_base> { private: FLUX_NO_UNIQUE_ADDRESS Base base_; - distance_t count_; + int_t count_; public: - constexpr drop_adaptor(decays_to auto&& base, distance_t count) + constexpr drop_adaptor(decays_to auto&& base, int_t count) : base_(FLUX_FWD(base)), count_(count) {} @@ -43,8 +43,7 @@ struct drop_adaptor : inline_sequence_base> { static constexpr auto size(auto& self) requires sized_sequence { - return (cmp::max)(num::sub(flux::size(self.base()), self.count_), - distance_t{0}); + return (cmp::max)(num::sub(flux::size(self.base()), self.count_), int_t {0}); } static constexpr auto data(auto& self) @@ -65,7 +64,7 @@ struct drop_fn { [[nodiscard]] constexpr auto operator()(Seq&& seq, num::integral auto count) const { - auto count_ = num::checked_cast(count); + auto count_ = num::checked_cast(count); if (count_ < 0) { runtime_error("Negative argument passed to drop()"); } diff --git a/include/flux/adaptor/reverse.hpp b/include/flux/adaptor/reverse.hpp index 2296692f..acf312a4 100644 --- a/include/flux/adaptor/reverse.hpp +++ b/include/flux/adaptor/reverse.hpp @@ -100,18 +100,18 @@ struct reverse_adaptor : inline_sequence_base> flux::inc(self.base_, cur.base_cur); } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t dist) -> void + static constexpr auto inc(auto& self, cursor_type& cur, int_t dist) -> void { flux::inc(self.base_, cur.base_cur, num::neg(dist)); } static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t + -> int_t { return flux::distance(self.base_, to.base_cur, from.base_cur); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { return flux::size(self.base_); diff --git a/include/flux/adaptor/scan.hpp b/include/flux/adaptor/scan.hpp index d3ac9fb7..46b81040 100644 --- a/include/flux/adaptor/scan.hpp +++ b/include/flux/adaptor/scan.hpp @@ -128,11 +128,11 @@ struct scan_adaptor : inline_sequence_base> { return cur; } - static constexpr auto size(self_t& self) -> distance_t + static constexpr auto size(self_t& self) -> int_t requires sized_sequence { if constexpr (Mode == scan_mode::exclusive) { - return num::add(flux::size(self.base_), distance_t{1}); + return num::add(flux::size(self.base_), int_t {1}); } else { return flux::size(self.base_); } diff --git a/include/flux/adaptor/scan_first.hpp b/include/flux/adaptor/scan_first.hpp index 1c781e71..6ae8a74a 100644 --- a/include/flux/adaptor/scan_first.hpp +++ b/include/flux/adaptor/scan_first.hpp @@ -89,7 +89,7 @@ struct scan_first_adaptor : inline_sequence_base distance_t + static constexpr auto size(self_t& self) -> int_t requires sized_sequence { return flux::size(self.base_); diff --git a/include/flux/adaptor/slide.hpp b/include/flux/adaptor/slide.hpp index 038358c3..25329642 100644 --- a/include/flux/adaptor/slide.hpp +++ b/include/flux/adaptor/slide.hpp @@ -17,10 +17,10 @@ template struct slide_adaptor : inline_sequence_base> { private: Base base_; - distance_t win_sz_; + int_t win_sz_; public: - constexpr slide_adaptor(decays_to auto&& base, distance_t win_sz) + constexpr slide_adaptor(decays_to auto&& base, int_t win_sz) : base_(FLUX_FWD(base)), win_sz_(win_sz) {} @@ -49,7 +49,7 @@ struct slide_adaptor : inline_sequence_base> { static constexpr auto first(auto& self) -> cursor_type { auto cur = flux::first(self.base_); auto end = cur; - advance(self.base_, end, num::sub(self.win_sz_, distance_t{1})); + advance(self.base_, end, num::sub(self.win_sz_, int_t {1})); return cursor_type{.from = std::move(cur), .to = std::move(end)}; } @@ -76,7 +76,7 @@ struct slide_adaptor : inline_sequence_base> { { auto end = flux::last(self.base_); auto cur = end; - advance(self.base_, cur, num::sub(distance_t{1}, self.win_sz_)); + advance(self.base_, cur, num::sub(int_t {1}, self.win_sz_)); return cursor_type{.from = std::move(cur), .to = std::move(end)}; } @@ -87,7 +87,7 @@ struct slide_adaptor : inline_sequence_base> { flux::dec(self.base_, cur.to); } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) -> void + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) -> void requires random_access_sequence { flux::inc(self.base_, cur.from, offset); @@ -95,17 +95,17 @@ struct slide_adaptor : inline_sequence_base> { } static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t + -> int_t requires random_access_sequence { return flux::distance(self.base_, from.from, to.from); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { - auto s = num::add(num::sub(flux::size(self.base_), self.win_sz_), distance_t{1}); - return (cmp::max)(s, distance_t{0}); + auto s = num::add(num::sub(flux::size(self.base_), self.win_sz_), int_t {1}); + return (cmp::max)(s, int_t {0}); } }; }; @@ -117,8 +117,7 @@ struct slide_fn { constexpr auto operator()(Seq&& seq, num::integral auto win_sz) const -> sequence auto { - return slide_adaptor>(FLUX_FWD(seq), - num::checked_cast(win_sz)); + return slide_adaptor>(FLUX_FWD(seq), num::checked_cast(win_sz)); } }; diff --git a/include/flux/adaptor/stride.hpp b/include/flux/adaptor/stride.hpp index c5f623f8..611dad32 100644 --- a/include/flux/adaptor/stride.hpp +++ b/include/flux/adaptor/stride.hpp @@ -15,10 +15,10 @@ namespace detail { // This is a Flux-ified version of ranges::advance. inline constexpr struct advance_fn { template - constexpr auto operator()(Seq& seq, cursor_t& cur, distance_t offset) const -> distance_t + constexpr auto operator()(Seq& seq, cursor_t& cur, int_t offset) const -> int_t { if (offset > 0) { - distance_t counter = 0; + int_t counter = 0; while (offset-- > 0 && !flux::is_last(seq, cur)) { flux::inc(seq, cur); ++counter; @@ -44,7 +44,7 @@ inline constexpr struct advance_fn { template requires bounded_sequence - constexpr auto operator()(Seq& seq, cursor_t& cur, distance_t offset) const -> distance_t + constexpr auto operator()(Seq& seq, cursor_t& cur, int_t offset) const -> int_t { if (offset > 0) { auto dist = (cmp::min)(flux::distance(seq, cur, flux::last(seq)), offset); @@ -65,10 +65,10 @@ template struct stride_adaptor : inline_sequence_base> { private: Base base_; - distance_t stride_; + int_t stride_; public: - constexpr stride_adaptor(decays_to auto&& base, distance_t stride) + constexpr stride_adaptor(decays_to auto&& base, int_t stride) : base_(FLUX_FWD(base)), stride_(stride) {} @@ -89,7 +89,7 @@ struct stride_adaptor : inline_sequence_base> { // This version of stride is never bidir static void dec(...) = delete; - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { auto s = flux::size(self.base_); @@ -99,7 +99,7 @@ struct stride_adaptor : inline_sequence_base> { static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_t requires sequence { - distance_t n = self.stride_; + int_t n = self.stride_; return flux::for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { if (++n < s) { return true; @@ -117,10 +117,10 @@ template struct stride_adaptor : inline_sequence_base> { private: Base base_; - distance_t stride_; + int_t stride_; public: - constexpr stride_adaptor(decays_to auto&& base, distance_t stride) + constexpr stride_adaptor(decays_to auto&& base, int_t stride) : base_(FLUX_FWD(base)), stride_(stride) {} @@ -129,7 +129,7 @@ struct stride_adaptor : inline_sequence_base> { private: struct cursor_type { cursor_t cur{}; - distance_t missing = 0; + int_t missing = 0; friend constexpr auto operator==(cursor_type const& lhs, cursor_type const& rhs) -> bool { @@ -193,8 +193,7 @@ struct stride_adaptor : inline_sequence_base> { static constexpr auto last(auto& self) -> cursor_type requires bounded_sequence && sized_sequence { - distance_t missing = - (self.stride_ - flux::size(self.base_) % self.stride_) % self.stride_; + int_t missing = (self.stride_ - flux::size(self.base_) % self.stride_) % self.stride_; return cursor_type{ .cur = flux::last(self.base_), .missing = missing @@ -208,21 +207,21 @@ struct stride_adaptor : inline_sequence_base> { cur.missing = 0; } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires sized_sequence { auto s = flux::size(self.base_); return s/self.stride_ + (s % self.stride_ == 0 ? 0 : 1); } - static constexpr auto distance(auto& self, cursor_type const& from, - cursor_type const& to) -> distance_t + static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) + -> int_t requires random_access_sequence { return (flux::distance(self.base_, from.cur, to.cur) - from.missing + to.missing)/self.stride_; } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) -> void + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) -> void requires random_access_sequence { if (offset > 0) { @@ -237,7 +236,7 @@ struct stride_adaptor : inline_sequence_base> { static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_type requires sequence { - distance_t n = self.stride_; + int_t n = self.stride_; auto c = flux::for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { if (++n < s) { return true; @@ -257,8 +256,7 @@ struct stride_fn { constexpr auto operator()(Seq&& seq, num::integral auto by) const { FLUX_ASSERT(by > 0); - return stride_adaptor>(FLUX_FWD(seq), - num::checked_cast(by)); + return stride_adaptor>(FLUX_FWD(seq), num::checked_cast(by)); } }; diff --git a/include/flux/adaptor/take.hpp b/include/flux/adaptor/take.hpp index 025a1c73..d6c88f43 100644 --- a/include/flux/adaptor/take.hpp +++ b/include/flux/adaptor/take.hpp @@ -17,10 +17,10 @@ struct take_adaptor : inline_sequence_base> { private: Base base_; - distance_t count_; + int_t count_; public: - constexpr take_adaptor(decays_to auto&& base, distance_t count) + constexpr take_adaptor(decays_to auto&& base, int_t count) : base_(FLUX_FWD(base)), count_(count) {} @@ -32,7 +32,7 @@ struct take_adaptor : inline_sequence_base> private: struct cursor_type { cursor_t base_cur; - distance_t length; + int_t length; friend bool operator==(cursor_type const&, cursor_type const&) = default; friend auto operator<=>(cursor_type const& lhs, cursor_type const& rhs) = default; @@ -55,7 +55,7 @@ struct take_adaptor : inline_sequence_base> static constexpr auto inc(auto& self, cursor_type& cur) { flux::inc(self.base_, cur.base_cur); - cur.length = num::sub(cur.length, distance_t{1}); + cur.length = num::sub(cur.length, int_t {1}); } static constexpr auto read_at(auto& self, cursor_type const& cur) @@ -86,10 +86,10 @@ struct take_adaptor : inline_sequence_base> requires bidirectional_sequence { flux::dec(self.base_, cur.base_cur); - cur.length = num::add(cur.length, distance_t{1}); + cur.length = num::add(cur.length, int_t {1}); } - static constexpr auto inc(auto& self, cursor_type& cur, distance_t offset) + static constexpr auto inc(auto& self, cursor_type& cur, int_t offset) requires random_access_sequence { flux::inc(self.base_, cur.base_cur, offset); @@ -97,7 +97,7 @@ struct take_adaptor : inline_sequence_base> } static constexpr auto distance(auto& self, cursor_type const& from, cursor_type const& to) - -> distance_t + -> int_t requires random_access_sequence { return (cmp::min)(flux::distance(self.base_, from.base_cur, to.base_cur), @@ -133,7 +133,7 @@ struct take_adaptor : inline_sequence_base> static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_type { - distance_t len = self.count_; + int_t len = self.count_; auto cur = flux::for_each_while(self.base_, [&](auto&& elem) { return (len-- > 0) && std::invoke(pred, FLUX_FWD(elem)); }); @@ -148,7 +148,7 @@ struct take_fn { [[nodiscard]] constexpr auto operator()(Seq&& seq, num::integral auto count) const { - auto count_ = num::checked_cast(count); + auto count_ = num::checked_cast(count); if (count_ < 0) { runtime_error("Negative argument passed to take()"); } diff --git a/include/flux/adaptor/zip.hpp b/include/flux/adaptor/zip.hpp index 67c756b1..b45162b9 100644 --- a/include/flux/adaptor/zip.hpp +++ b/include/flux/adaptor/zip.hpp @@ -92,8 +92,8 @@ struct zip_traits_base : default_sequence_traits { } template - requires (random_access_sequence> && ...) - static constexpr auto& inc(Self& self, cursor_t& cur, distance_t offset) + requires(random_access_sequence> && ...) + static constexpr auto& inc(Self& self, cursor_t& cur, int_t offset) { [&](std::index_sequence) { (flux::inc(std::get(self.bases_), std::get(cur), offset), ...); diff --git a/include/flux/algorithm/count.hpp b/include/flux/algorithm/count.hpp index 756fd079..9be202bd 100644 --- a/include/flux/algorithm/count.hpp +++ b/include/flux/algorithm/count.hpp @@ -15,12 +15,12 @@ namespace detail { struct count_fn { template [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> distance_t + constexpr auto operator()(Seq&& seq) const -> int_t { if constexpr (sized_sequence) { return flux::size(seq); } else { - distance_t counter = 0; + int_t counter = 0; flux::for_each_while(seq, [&](auto&&) { ++counter; return true; @@ -34,10 +34,9 @@ struct count_eq_fn { template requires std::equality_comparable_with, Value const&> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Value const& value) const - -> distance_t + constexpr auto operator()(Seq&& seq, Value const& value) const -> int_t { - distance_t counter = 0; + int_t counter = 0; flux::for_each_while(seq, [&](auto&& elem) { if (value == FLUX_FWD(elem)) { ++counter; @@ -52,10 +51,9 @@ struct count_if_fn { template requires std::predicate> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Pred pred) const - -> distance_t + constexpr auto operator()(Seq&& seq, Pred pred) const -> int_t { - distance_t counter = 0; + int_t counter = 0; flux::for_each_while(seq, [&](auto&& elem) { if (std::invoke(pred, FLUX_FWD(elem))) { ++counter; diff --git a/include/flux/algorithm/detail/heap_ops.hpp b/include/flux/algorithm/detail/heap_ops.hpp index 43fcd0d4..e97de05a 100644 --- a/include/flux/algorithm/detail/heap_ops.hpp +++ b/include/flux/algorithm/detail/heap_ops.hpp @@ -33,7 +33,7 @@ namespace flux::detail { template -constexpr void sift_up_n(Seq& seq, distance_t n, Comp& comp) +constexpr void sift_up_n(Seq& seq, int_t n, Comp& comp) { cursor_t first = flux::first(seq); @@ -58,8 +58,7 @@ constexpr void sift_up_n(Seq& seq, distance_t n, Comp& comp) } template -constexpr void sift_down_n(Seq& seq, distance_t n, cursor_t start, - Comp& comp) +constexpr void sift_down_n(Seq& seq, int_t n, cursor_t start, Comp& comp) { cursor_t first = flux::first(seq); @@ -120,7 +119,7 @@ constexpr void sift_down_n(Seq& seq, distance_t n, cursor_t start, template constexpr void make_heap(Seq& seq, Comp& comp) { - distance_t n = flux::size(seq); + int_t n = flux::size(seq); auto first = flux::first(seq); if (n > 1) { @@ -131,7 +130,7 @@ constexpr void make_heap(Seq& seq, Comp& comp) } template -constexpr void pop_heap(Seq& seq, distance_t n, Comp& comp) +constexpr void pop_heap(Seq& seq, int_t n, Comp& comp) { auto first = flux::first(seq); if (n > 1) { diff --git a/include/flux/algorithm/detail/pdqsort.hpp b/include/flux/algorithm/detail/pdqsort.hpp index 31753f21..b60c9390 100644 --- a/include/flux/algorithm/detail/pdqsort.hpp +++ b/include/flux/algorithm/detail/pdqsort.hpp @@ -128,7 +128,7 @@ constexpr bool partial_insertion_sort(Seq& seq, Cur const begin, Cur const end, return true; } - distance_t limit = 0; + int_t limit = 0; for (auto cur = next(seq, begin); cur != end; inc(seq, cur)) { if (limit > pqdsort_partial_insertion_sort_limit) { @@ -322,9 +322,8 @@ partition_right_branchless(Seq& seq, Cur const begin, Cur const end, Comp& comp) inc(seq, last, -pdqsort_block_size); } - distance_t l_size = 0, r_size = 0; - distance_t unknown_left = - distance(seq, first, last) - ((num_r || num_l) ? pdqsort_block_size : 0); + int_t l_size = 0, r_size = 0; + int_t unknown_left = distance(seq, first, last) - ((num_r || num_l) ? pdqsort_block_size : 0); if (num_r) { // Handle leftover block by assigning the unknown elements to the other // block. @@ -343,7 +342,7 @@ partition_right_branchless(Seq& seq, Cur const begin, Cur const end, Comp& comp) if (unknown_left && !num_l) { start_l = 0; Cur cur = first; - for (unsigned char i = 0; static_cast(i) < l_size;) { + for (unsigned char i = 0; static_cast(i) < l_size;) { offsets_l[num_l] = i++; num_l += !comp(read_at(seq, cur), pivot); inc(seq, cur); @@ -352,7 +351,7 @@ partition_right_branchless(Seq& seq, Cur const begin, Cur const end, Comp& comp) if (unknown_left && !num_r) { start_r = 0; Cur cur = last; - for (unsigned char i = 0; static_cast(i) < r_size;) { + for (unsigned char i = 0; static_cast(i) < r_size;) { offsets_r[num_r] = ++i; num_r += comp(read_at(seq, dec(seq, cur)), pivot); } @@ -496,7 +495,7 @@ template ) { return bidir_impl(haystack, needle, cmp); } else { - distance_t len1 = flux::count(haystack); - distance_t len2 = flux::count(needle); + int_t len1 = flux::count(haystack); + int_t len2 = flux::count(needle); if (len1 < len2) { return false; diff --git a/include/flux/core/concepts.hpp b/include/flux/core/concepts.hpp index 21201932..b04f2124 100644 --- a/include/flux/core/concepts.hpp +++ b/include/flux/core/concepts.hpp @@ -91,7 +91,7 @@ template using value_t = typename detail::value_type::type; FLUX_EXPORT -using distance_t = flux::config::int_type; +using int_t = flux::config::int_type; FLUX_EXPORT using index_t = flux::config::int_type; @@ -198,11 +198,11 @@ namespace detail { template >> concept random_access_sequence_requirements = ordered_cursor> && - requires (Seq& seq, cursor_t& cur, distance_t offset) { + requires (Seq& seq, cursor_t& cur, int_t offset) { { Traits::inc(seq, cur, offset) }; } && requires (Seq& seq, cursor_t const& cur) { - { Traits::distance(seq, cur, cur) } -> std::convertible_to; + { Traits::distance(seq, cur, cur) } -> std::convertible_to; }; } // namespace detail @@ -251,7 +251,7 @@ namespace detail { template >> concept sized_sequence_requirements = requires (Seq& seq) { - { Traits::size(seq) } -> std::convertible_to; + { Traits::size(seq) } -> std::convertible_to; }; } // namespace detail @@ -403,7 +403,7 @@ struct default_sequence_traits { template requires detail::random_access_sequence_requirements && detail::bounded_sequence_requirements - static constexpr auto size(Self& self) -> distance_t + static constexpr auto size(Self& self) -> int_t { using Traits = detail::traits_t; return Traits::distance(self, Traits::first(self), Traits::last(self)); diff --git a/include/flux/core/default_impls.hpp b/include/flux/core/default_impls.hpp index de2ef191..2c7bc1b4 100644 --- a/include/flux/core/default_impls.hpp +++ b/include/flux/core/default_impls.hpp @@ -38,7 +38,7 @@ struct sequence_traits : default_sequence_traits { static constexpr auto inc(auto const&, index_t& idx) { FLUX_DEBUG_ASSERT(idx < N); - idx = num::add(idx, distance_t{1}); + idx = num::add(idx, int_t {1}); } static constexpr auto last(auto const&) -> index_t { return N; } @@ -46,24 +46,24 @@ struct sequence_traits : default_sequence_traits { static constexpr auto dec(auto const&, index_t& idx) { FLUX_DEBUG_ASSERT(idx > 0); - idx = num::sub(idx, distance_t{1}); + idx = num::sub(idx, int_t {1}); } - static constexpr auto inc(auto const&, index_t& idx, distance_t offset) + static constexpr auto inc(auto const&, index_t& idx, int_t offset) { FLUX_DEBUG_ASSERT(num::add(idx, offset) <= N); FLUX_DEBUG_ASSERT(num::add(idx, offset) >= 0); idx = num::add(idx, offset); } - static constexpr auto distance(auto const&, index_t from, index_t to) -> distance_t + static constexpr auto distance(auto const&, index_t from, index_t to) -> int_t { return num::sub(to, from); } static constexpr auto data(auto& self) -> auto* { return self; } - static constexpr auto size(auto const&) -> distance_t { return N; } + static constexpr auto size(auto const&) -> int_t { return N; } static constexpr auto for_each_while(auto& self, auto&& pred) -> index_t { @@ -125,16 +125,14 @@ struct sequence_traits> : default_sequence_traits { return flux::dec(self.get(), cur); } - static constexpr auto inc(self_t self, cursor_t& cur, distance_t offset) - -> cursor_t& + static constexpr auto inc(self_t self, cursor_t& cur, int_t offset) -> cursor_t& requires random_access_sequence { return flux::inc(self.get(), cur, offset); } - static constexpr auto distance(self_t self, cursor_t const& from, - cursor_t const& to) - -> distance_t + static constexpr auto distance(self_t self, cursor_t const& from, cursor_t const& to) + -> int_t requires random_access_sequence { return flux::distance(self.get(), from, to); @@ -152,7 +150,7 @@ struct sequence_traits> : default_sequence_traits { return flux::last(self.get()); } - static constexpr auto size(self_t self) -> distance_t + static constexpr auto size(self_t self) -> int_t requires sized_sequence { return flux::size(self.get()); @@ -186,7 +184,7 @@ struct sequence_traits : default_sequence_traits { static constexpr auto inc(auto& self, index_t& idx) { FLUX_DEBUG_ASSERT(idx < size(self)); - idx = num::add(idx, distance_t{1}); + idx = num::add(idx, int_t {1}); } static constexpr auto read_at(auto& self, index_t idx) -> decltype(auto) @@ -203,26 +201,26 @@ struct sequence_traits : default_sequence_traits { static constexpr auto dec(auto&, index_t& idx) { FLUX_DEBUG_ASSERT(idx > 0); - idx = num::sub(idx, distance_t{1}); + idx = num::sub(idx, int_t {1}); } static constexpr auto last(auto& self) -> index_t { return size(self); } - static constexpr auto inc(auto& self, index_t& idx, distance_t offset) + static constexpr auto inc(auto& self, index_t& idx, int_t offset) { FLUX_DEBUG_ASSERT(num::add(idx, offset) <= size(self)); FLUX_DEBUG_ASSERT(num::add(idx, offset) >= 0); idx = num::add(idx, offset); } - static constexpr auto distance(auto&, index_t from, index_t to) -> distance_t + static constexpr auto distance(auto&, index_t from, index_t to) -> int_t { return num::sub(to, from); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t { - return num::cast(std::ranges::ssize(self)); + return num::cast(std::ranges::ssize(self)); } static constexpr auto data(auto& self) -> auto* diff --git a/include/flux/core/inline_sequence_base.hpp b/include/flux/core/inline_sequence_base.hpp index 735287d7..3cbbeb5a 100644 --- a/include/flux/core/inline_sequence_base.hpp +++ b/include/flux/core/inline_sequence_base.hpp @@ -82,7 +82,10 @@ struct inline_sequence_base { /// Increments the given cursor by `offset` places template D = Derived> requires random_access_sequence - constexpr auto& inc(cursor_t& cur, distance_t offset) { return flux::inc(derived(), cur, offset); } + constexpr auto& inc(cursor_t& cur, int_t offset) + { + return flux::inc(derived(), cur, offset); + } /// Returns the number of times `from` must be incremented to reach `to` /// @@ -221,9 +224,10 @@ struct inline_sequence_base { * Adaptors */ - template + template [[nodiscard]] - constexpr auto adjacent() && requires multipass_sequence; + constexpr auto adjacent() && + requires multipass_sequence; template requires multipass_sequence && @@ -231,7 +235,7 @@ struct inline_sequence_base { [[nodiscard]] constexpr auto adjacent_filter(Pred pred) &&; - template + template requires multipass_sequence [[nodiscard]] constexpr auto adjacent_map(Func func) &&; diff --git a/include/flux/core/operation_requirements.hpp b/include/flux/core/operation_requirements.hpp index a80d9b21..f5f4c9b3 100644 --- a/include/flux/core/operation_requirements.hpp +++ b/include/flux/core/operation_requirements.hpp @@ -23,7 +23,7 @@ concept foldable_ = std::convertible_to && std::assignable_from>>; -template +template struct repeated_invocable_helper { template using repeater = E; @@ -33,7 +33,7 @@ struct repeated_invocable_helper { }(std::make_index_sequence{}); }; -template +template concept repeated_invocable = repeated_invocable_helper::value; template diff --git a/include/flux/core/ref.hpp b/include/flux/core/ref.hpp index 81ed1449..39c5ff2d 100644 --- a/include/flux/core/ref.hpp +++ b/include/flux/core/ref.hpp @@ -51,7 +51,7 @@ struct passthrough_traits_base : default_sequence_traits { } template - static constexpr auto inc(Self& self, auto& cur, distance_t dist) + static constexpr auto inc(Self& self, auto& cur, int_t dist) -> decltype(flux::inc(self.base(), cur, dist)) { return flux::inc(self.base(), cur, dist); diff --git a/include/flux/core/sequence_access.hpp b/include/flux/core/sequence_access.hpp index 86bbc3b8..0e3a0d66 100644 --- a/include/flux/core/sequence_access.hpp +++ b/include/flux/core/sequence_access.hpp @@ -54,7 +54,7 @@ struct inc_fn { } template - constexpr auto operator()(Seq& seq, cursor_t& cur, distance_t offset) const + constexpr auto operator()(Seq& seq, cursor_t& cur, int_t offset) const noexcept(noexcept(traits_t::inc(seq, cur, offset))) -> cursor_t& { (void) traits_t::inc(seq, cur, offset); @@ -75,14 +75,13 @@ struct dec_fn { struct distance_fn { template [[nodiscard]] - constexpr auto operator()(Seq& seq, cursor_t const& from, - cursor_t const& to) const - -> distance_t + constexpr auto operator()(Seq& seq, cursor_t const& from, cursor_t const& to) const + -> int_t { if constexpr (random_access_sequence) { return traits_t::distance(seq, from, to); } else { - distance_t n = 0; + int_t n = 0; auto from_ = from; while (from_ != to) { inc_fn{}(seq, from_); @@ -116,7 +115,7 @@ struct last_fn { struct size_fn { template [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> distance_t + constexpr auto operator()(Seq&& seq) const -> int_t { return traits_t::size(seq); } @@ -202,13 +201,12 @@ struct next_fn { template [[nodiscard]] - constexpr auto operator()(Seq& seq, cursor_t cur, distance_t offset) const - -> cursor_t + constexpr auto operator()(Seq& seq, cursor_t cur, int_t offset) const -> cursor_t { if constexpr (random_access_sequence) { return inc(seq, cur, offset); } else if constexpr (bidirectional_sequence) { - auto const zero = distance_t{0}; + auto const zero = int_t {0}; if (offset > zero) { while (offset-- > zero) { inc(seq, cur); @@ -220,7 +218,7 @@ struct next_fn { } return cur; } else { - while (offset-- > distance_t{0}) { + while (offset-- > int_t {0}) { inc(seq, cur); } return cur; diff --git a/include/flux/core/sequence_iterator.hpp b/include/flux/core/sequence_iterator.hpp index 845b6b13..6b5dec04 100644 --- a/include/flux/core/sequence_iterator.hpp +++ b/include/flux/core/sequence_iterator.hpp @@ -39,7 +39,7 @@ struct sequence_iterator { public: using value_type = value_t; - using difference_type = distance_t; + using difference_type = int_t; using element_type = value_t; // Yes, really using iterator_concept = decltype(get_iterator_tag()); diff --git a/include/flux/sequence/array_ptr.hpp b/include/flux/sequence/array_ptr.hpp index 6ccc75e5..dbcd3de4 100644 --- a/include/flux/sequence/array_ptr.hpp +++ b/include/flux/sequence/array_ptr.hpp @@ -25,14 +25,11 @@ template struct array_ptr : inline_sequence_base> { private: T* data_ = nullptr; - distance_t sz_ = 0; + int_t sz_ = 0; friend struct detail::make_array_ptr_unchecked_fn; - constexpr array_ptr(T* ptr, distance_t sz) noexcept - : data_(ptr), - sz_(sz) - {} + constexpr array_ptr(T* ptr, int_t sz) noexcept : data_(ptr), sz_(sz) { } public: array_ptr() = default; @@ -88,7 +85,7 @@ struct array_ptr : inline_sequence_base> { static constexpr auto inc(array_ptr const& self, index_t& idx) -> void { FLUX_DEBUG_ASSERT(idx < self.sz_); - idx = num::add(idx, distance_t{1}); + idx = num::add(idx, int_t {1}); } static constexpr auto read_at(array_ptr const& self, index_t idx) -> T& @@ -110,8 +107,7 @@ struct array_ptr : inline_sequence_base> { static constexpr auto last(array_ptr const& self) -> index_t { return self.sz_; } - static constexpr auto inc(array_ptr const& self, index_t& idx, distance_t offset) - -> void + static constexpr auto inc(array_ptr const& self, index_t& idx, int_t offset) -> void { index_t nxt = num::add(idx, offset); FLUX_DEBUG_ASSERT(nxt >= 0); @@ -119,16 +115,12 @@ struct array_ptr : inline_sequence_base> { idx = nxt; } - static constexpr auto distance(array_ptr const&, index_t from, index_t to) - -> distance_t + static constexpr auto distance(array_ptr const&, index_t from, index_t to) -> int_t { return num::sub(to, from); } - static constexpr auto size(array_ptr const& self) -> distance_t - { - return self.sz_; - } + static constexpr auto size(array_ptr const& self) -> int_t { return self.sz_; } static constexpr auto data(array_ptr const& self) -> T* { return self.data_; } @@ -158,7 +150,7 @@ struct make_array_ptr_unchecked_fn { requires (std::is_object_v && !std::is_abstract_v) constexpr auto operator()(T* ptr, num::integral auto size) const -> array_ptr { - return array_ptr(ptr, num::checked_cast(size)); + return array_ptr(ptr, num::checked_cast(size)); } }; diff --git a/include/flux/sequence/empty.hpp b/include/flux/sequence/empty.hpp index a32b5654..4ca1f527 100644 --- a/include/flux/sequence/empty.hpp +++ b/include/flux/sequence/empty.hpp @@ -26,8 +26,7 @@ struct empty_sequence : inline_sequence_base> { static constexpr auto last(empty_sequence) -> cursor_type { return {}; } static constexpr auto is_last(empty_sequence, cursor_type) -> bool { return true; } - static constexpr auto inc(empty_sequence, cursor_type& cur, distance_t = 0) - -> cursor_type& + static constexpr auto inc(empty_sequence, cursor_type& cur, int_t = 0) -> cursor_type& { return cur; } diff --git a/include/flux/sequence/iota.hpp b/include/flux/sequence/iota.hpp index 774e281b..97792c93 100644 --- a/include/flux/sequence/iota.hpp +++ b/include/flux/sequence/iota.hpp @@ -40,7 +40,7 @@ concept advancable = T(u + o); T(o + u); T(u - o); - { u - u } -> std::convertible_to; + { u - u } -> std::convertible_to; }; struct iota_traits { @@ -94,8 +94,7 @@ struct iota_sequence_traits : default_sequence_traits { return --cur; } - static constexpr auto inc(auto&, cursor_type& cur, distance_t offset) - -> cursor_type& + static constexpr auto inc(auto&, cursor_type& cur, int_t offset) -> cursor_type& requires advancable { return cur += num::cast>(offset); @@ -104,13 +103,13 @@ struct iota_sequence_traits : default_sequence_traits { static constexpr auto distance(auto&, cursor_type const& from, cursor_type const& to) requires advancable { - return from <= to ? num::cast(to - from) : -num::cast(from - to); + return from <= to ? num::cast(to - from) : -num::cast(from - to); } - static constexpr auto size(auto& self) -> distance_t + static constexpr auto size(auto& self) -> int_t requires advancable && (Traits.has_start && Traits.has_end) { - return num::cast(self.end_ - self.start_); + return num::cast(self.end_ - self.start_); } }; @@ -168,19 +167,13 @@ struct iota_fn { }; struct ints_fn { - inline constexpr auto operator()() const - { - return basic_iota_sequence(); - } + inline constexpr auto operator()() const { return basic_iota_sequence(); } - inline constexpr auto operator()(distance_t from) const - { - return iota_sequence(from); - } + inline constexpr auto operator()(int_t from) const { return iota_sequence(from); } - inline constexpr auto operator()(distance_t from, distance_t to) const + inline constexpr auto operator()(int_t from, int_t to) const { - return bounded_iota_sequence(from, to); + return bounded_iota_sequence(from, to); } }; diff --git a/include/flux/sequence/range.hpp b/include/flux/sequence/range.hpp index 7623ba83..a3d4d699 100644 --- a/include/flux/sequence/range.hpp +++ b/include/flux/sequence/range.hpp @@ -112,8 +112,8 @@ struct range_sequence : inline_sequence_base> { } template - requires (!std::is_const_v || can_const_iterate) - static constexpr auto inc(Self& self, cursor_type& cur, distance_t offset) + requires(!std::is_const_v || can_const_iterate) + static constexpr auto inc(Self& self, cursor_type& cur, int_t offset) requires std::ranges::random_access_range { if (offset < 0) { @@ -126,12 +126,12 @@ struct range_sequence : inline_sequence_base> { } template - requires (!std::is_const_v || can_const_iterate) + requires(!std::is_const_v || can_const_iterate) static constexpr auto distance(Self&, cursor_type const& from, cursor_type const& to) - -> distance_t + -> int_t requires std::ranges::random_access_range { - return num::cast(std::ranges::distance(from.iter, to.iter)); + return num::cast(std::ranges::distance(from.iter, to.iter)); } template @@ -155,11 +155,11 @@ struct range_sequence : inline_sequence_base> { } template - requires (!std::is_const_v || can_const_iterate) - static constexpr auto size(Self& self) -> distance_t + requires(!std::is_const_v || can_const_iterate) + static constexpr auto size(Self& self) -> int_t requires std::ranges::sized_range { - return num::cast(std::ranges::ssize(self.rng_)); + return num::cast(std::ranges::ssize(self.rng_)); } template diff --git a/include/flux/sequence/repeat.hpp b/include/flux/sequence/repeat.hpp index ebe4b464..5d7e937b 100644 --- a/include/flux/sequence/repeat.hpp +++ b/include/flux/sequence/repeat.hpp @@ -70,14 +70,14 @@ struct repeat_sequence : inline_sequence_base> --cur; } - static constexpr auto inc(self_t const&, std::size_t& cur, distance_t offset) -> void + static constexpr auto inc(self_t const&, std::size_t& cur, int_t offset) -> void { cur += static_cast(offset); } - static constexpr auto distance(self_t const&, std::size_t from, std::size_t to) -> distance_t + static constexpr auto distance(self_t const&, std::size_t from, std::size_t to) -> int_t { - return num::cast(to) - num::cast(from); + return num::cast(to) - num::cast(from); } static constexpr auto for_each_while(self_t const& self, auto&& pred) -> std::size_t @@ -107,10 +107,10 @@ struct repeat_sequence : inline_sequence_base> return self.data_.count; } - static constexpr auto size(self_t const& self) -> distance_t - requires (!IsInfinite) + static constexpr auto size(self_t const& self) -> int_t + requires(!IsInfinite) { - return num::cast(self.data_.count); + return num::cast(self.data_.count); } }; }; @@ -127,7 +127,7 @@ struct repeat_fn { requires std::movable> constexpr auto operator()(T&& obj, num::integral auto count) const { - auto c = num::checked_cast(count); + auto c = num::checked_cast(count); if (c < 0) { runtime_error("Negative count passed to repeat()"); } diff --git a/include/flux/sequence/single.hpp b/include/flux/sequence/single.hpp index 273e8169..00009215 100644 --- a/include/flux/sequence/single.hpp +++ b/include/flux/sequence/single.hpp @@ -93,8 +93,7 @@ struct sequence_traits> : default_sequence_traits return cur; } - static constexpr auto inc(self_t const&, cursor_type& cur, distance_t off) - -> cursor_type& + static constexpr auto inc(self_t const&, cursor_type& cur, int_t off) -> cursor_type& { if (off > 0) { FLUX_DEBUG_ASSERT(cur == cursor_type::valid && off == 1); diff --git a/test/test_apply.cpp b/test/test_apply.cpp index f72bfe5c..f06ae9b3 100644 --- a/test/test_apply.cpp +++ b/test/test_apply.cpp @@ -5,9 +5,7 @@ #include "test_utils.hpp" // We don't have an in-place rotate yet, so let's borrow the STL's -auto rotate_by = [](Seq&& seq, flux::distance_t places) - -> Seq&& -{ +auto rotate_by = [](Seq&& seq, flux::int_t places) -> Seq&& { auto const sz = flux::size(seq); while (places < 0) { @@ -22,7 +20,6 @@ auto rotate_by = [](Seq&& seq, flux::distance_ return FLUX_FWD(seq); }; - constexpr bool test_apply() { // Checking lvalue sequence diff --git a/test/test_cartesian_product.cpp b/test/test_cartesian_product.cpp index 284c2beb..81751ace 100644 --- a/test/test_cartesian_product.cpp +++ b/test/test_cartesian_product.cpp @@ -356,13 +356,19 @@ constexpr bool test_cartesian_product() static_assert(flux::bounded_sequence); static_assert(flux::sized_sequence); - static_assert(std::same_as, std::tuple>); - static_assert(std::same_as, std::tuple>); - static_assert(std::same_as, std::tuple>); - - static_assert(std::same_as, std::tuple>); - static_assert(std::same_as, std::tuple>); - static_assert(std::same_as, std::tuple>); + static_assert( + std::same_as, std::tuple>); + static_assert( + std::same_as, std::tuple>); + static_assert(std::same_as, + std::tuple>); + + static_assert(std::same_as, + std::tuple>); + static_assert(std::same_as, + std::tuple>); + static_assert(std::same_as, + std::tuple>); STATIC_CHECK(flux::size(cart) == 4 * 2 * 3); @@ -421,14 +427,14 @@ constexpr bool test_cartesian_product() STATIC_CHECK(flux::next(cart, flux::next(cart, cart.first(), 11), -5) == std::tuple{1, 0, 0}); } - flux::distance_t sum_i = 0; - flux::distance_t sum_j = 0; - flux::distance_t sum_k = 0; - cart.for_each(flux::unpack([&] (flux::distance_t i, flux::distance_t j, flux::distance_t k) { - sum_i += i; - sum_j += j; - sum_k += k; - })); + flux::int_t sum_i = 0; + flux::int_t sum_j = 0; + flux::int_t sum_k = 0; + cart.for_each(flux::unpack([&](flux::int_t i, flux::int_t j, flux::int_t k) { + sum_i += i; + sum_j += j; + sum_k += k; + })); constexpr auto triangular_number = [] (auto n) { return (n * (n + 1)) / 2; }; STATIC_CHECK(sum_i == triangular_number(4 - 1) * 2 * 3); STATIC_CHECK(sum_j == 4 * triangular_number(2 - 1) * 3); @@ -501,7 +507,7 @@ static_assert(test_cartesian_product()); void issue_167() { // Check that overflowing size() is correctly caught - auto ints = flux::ints(0, std::numeric_limits::max()); + auto ints = flux::ints(0, std::numeric_limits::max()); auto prod = flux::cartesian_product(ints, ints, ints); diff --git a/test/test_cartesian_product_map.cpp b/test/test_cartesian_product_map.cpp index 07e3dc76..20cceb5c 100644 --- a/test/test_cartesian_product_map.cpp +++ b/test/test_cartesian_product_map.cpp @@ -133,7 +133,7 @@ static_assert(test_cartesian_product_map()); void issue_167() { // Check that overflowing size() is correctly caught - auto ints = flux::ints(0, std::numeric_limits::max()); + auto ints = flux::ints(0, std::numeric_limits::max()); auto prod = flux::cartesian_product_map([](auto...) { return 0; }, ints, ints, ints); diff --git a/test/test_chunk.cpp b/test/test_chunk.cpp index 005c5f6f..1c840677 100644 --- a/test/test_chunk.cpp +++ b/test/test_chunk.cpp @@ -243,7 +243,7 @@ constexpr bool test_chunk_multipass() { std::array arr{1, 2, 3, 4, 5}; - constexpr auto max = std::numeric_limits::max(); + constexpr auto max = std::numeric_limits::max(); auto seq = NotBidir(arr).chunk(max); @@ -372,7 +372,7 @@ constexpr bool test_chunk_bidir() { std::array arr{1, 2, 3, 4, 5}; - constexpr auto max = std::numeric_limits::max(); + constexpr auto max = std::numeric_limits::max(); auto seq = flux::chunk(arr,max); diff --git a/test/test_cycle.cpp b/test/test_cycle.cpp index 6d41d245..41f16c2f 100644 --- a/test/test_cycle.cpp +++ b/test/test_cycle.cpp @@ -187,7 +187,7 @@ constexpr bool test_cycle() { // Go a long way back from the start cur = seq.first(); - seq.inc(cur, std::numeric_limits::lowest()); + seq.inc(cur, std::numeric_limits::lowest()); (void) seq[cur]; } @@ -331,7 +331,7 @@ TEST_CASE("cycle") SUBCASE("over-large sizes are caught") { - constexpr auto max_dist = std::numeric_limits::max(); + constexpr auto max_dist = std::numeric_limits::max(); auto seq = flux::ints(0, max_dist).cycle(max_dist); diff --git a/test/test_range_iface.cpp b/test/test_range_iface.cpp index ed170e03..78e30f31 100644 --- a/test/test_range_iface.cpp +++ b/test/test_range_iface.cpp @@ -27,20 +27,20 @@ constexpr bool test_range_iface() static_assert(rng::common_range); static_assert(std::same_as, int&>); - static_assert(std::same_as, flux::distance_t>); + static_assert(std::same_as, flux::int_t>); static_assert(std::same_as, int>); static_assert(std::same_as, int&&>); - static_assert(std::same_as, flux::distance_t>); + static_assert(std::same_as, flux::int_t>); static_assert(rng::random_access_range); static_assert(rng::sized_range); static_assert(rng::common_range); static_assert(std::same_as, int const&>); - static_assert(std::same_as, flux::distance_t>); + static_assert(std::same_as, flux::int_t>); static_assert(std::same_as, int>); static_assert(std::same_as, int const&&>); - static_assert(std::same_as, flux::distance_t>); + static_assert(std::same_as, flux::int_t>); STATIC_CHECK(std::ranges::equal(seq, std::array{1, 2, 3, 4, 5})); diff --git a/test/test_repeat.cpp b/test/test_repeat.cpp index e2528fa5..a87070b8 100644 --- a/test/test_repeat.cpp +++ b/test/test_repeat.cpp @@ -114,8 +114,8 @@ constexpr bool test_repeat() { auto seq = flux::repeat(1.0); - constexpr auto max_idx = std::numeric_limits::max(); - constexpr auto min_idx = std::numeric_limits::lowest(); + constexpr auto max_idx = std::numeric_limits::max(); + constexpr auto min_idx = std::numeric_limits::lowest(); auto cur = flux::next(seq, seq.first(), max_idx); diff --git a/test/test_sort.cpp b/test/test_sort.cpp index b2ae5512..0e8d70d3 100644 --- a/test/test_sort.cpp +++ b/test/test_sort.cpp @@ -26,16 +26,17 @@ struct span_seq { static constexpr T& read_at(span_seq const& self, std::size_t i) { return self.ptr_[i]; } static constexpr std::size_t last(span_seq const& self) { return self.sz_; } static constexpr std::size_t& dec(span_seq const&, std::size_t& i) { return --i; } - static constexpr std::size_t& inc(span_seq const&, std::size_t& i, flux::distance_t o) + static constexpr std::size_t& inc(span_seq const&, std::size_t& i, flux::int_t o) { return i += static_cast(o); } - static constexpr flux::distance_t distance(span_seq const&, std::size_t from, std::size_t to) + static constexpr flux::int_t distance(span_seq const&, std::size_t from, std::size_t to) { - return static_cast(to) - static_cast(from); + return static_cast(to) - static_cast(from); } - static constexpr flux::distance_t size(span_seq const& self) { - return static_cast(self.sz_); + static constexpr flux::int_t size(span_seq const& self) + { + return static_cast(self.sz_); } static constexpr T* data(span_seq const& self) { return self.ptr_; } }; diff --git a/test/test_stride.cpp b/test/test_stride.cpp index 851b90ac..1cdb3b46 100644 --- a/test/test_stride.cpp +++ b/test/test_stride.cpp @@ -116,7 +116,7 @@ constexpr bool test_stride_non_bidir() // Stride of distance_t::max doesn't break stuff { std::array arr{0, 1, 2, 3, 4, 5}; - auto seq = NotBidir(arr).stride(std::numeric_limits::max()); + auto seq = NotBidir(arr).stride(std::numeric_limits::max()); auto cur = seq.first(); STATIC_CHECK(!seq.is_last(cur)); @@ -259,7 +259,7 @@ constexpr bool test_stride_bidir() // Stride of distance_t::max doesn't break stuff { std::array arr{0, 1, 2, 3, 4, 5}; - auto seq = flux::ref(arr).stride(std::numeric_limits::max()); + auto seq = flux::ref(arr).stride(std::numeric_limits::max()); auto cur = seq.first(); STATIC_CHECK(!seq.is_last(cur)); From 8ee1431a405e436894e062f35b7ac2a039d4e11e Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Tue, 13 May 2025 16:08:45 +0100 Subject: [PATCH 02/66] Add iterable and reverse_iterable concepts --- include/flux/core.hpp | 1 + include/flux/core/iterable_concepts.hpp | 388 ++++++++++++++++++++++++ include/flux/core/utils.hpp | 53 ++++ test/CMakeLists.txt | 1 + test/test_iterable_concepts.cpp | 188 ++++++++++++ 5 files changed, 631 insertions(+) create mode 100644 include/flux/core/iterable_concepts.hpp create mode 100644 test/test_iterable_concepts.cpp diff --git a/include/flux/core.hpp b/include/flux/core.hpp index 67064f8f..a48b2767 100644 --- a/include/flux/core.hpp +++ b/include/flux/core.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/include/flux/core/iterable_concepts.hpp b/include/flux/core/iterable_concepts.hpp new file mode 100644 index 00000000..8e3d3380 --- /dev/null +++ b/include/flux/core/iterable_concepts.hpp @@ -0,0 +1,388 @@ +// Copyright (c) 2025 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef FLUX_CORE_ITERABLE_CONCEPTS_HPP_INCLUDED +#define FLUX_CORE_ITERABLE_CONCEPTS_HPP_INCLUDED + +#include +#include + +namespace flux { + +/* + * MARK: Iteration context + */ + +FLUX_EXPORT +enum class iteration_result : bool { incomplete = false, complete = true }; + +FLUX_EXPORT inline constexpr bool loop_break = false; +FLUX_EXPORT inline constexpr bool loop_continue = true; + +FLUX_EXPORT template +using context_element_t = typename std::remove_cvref_t::element_type; + +FLUX_EXPORT template +concept iteration_context + = requires { typename Ctx::element_type; } && detail::can_reference + && requires(Ctx& ctx, bool (*pred)(context_element_t)) { + { ctx.run_while(flux::copy(pred)) } -> std::same_as; + }; + +FLUX_EXPORT +struct run_while_t { + template + requires callable_mut)> + constexpr auto operator()(Ctx& ctx, Pred&& pred) const -> iteration_result + { + return ctx.run_while(FLUX_FWD(pred)); + } +}; + +FLUX_EXPORT inline constexpr run_while_t run_while {}; + +FLUX_EXPORT +struct step_t { + template + requires callable_once)> + constexpr auto operator()(Ctx& ctx, Fn&& fn) + { + using E = context_element_t; + using R = callable_result_t; + + if constexpr (std::is_void_v) { + bool called = false; + run_while(ctx, [&](auto&& elem) { + std::move(fn)(FLUX_FWD(elem)); + called = true; + return loop_break; + }); + return called; + } else { + // If element_type is an rvalue reference, call with a value instead + using T = std::conditional_t, std::remove_cvref_t, E>; + + flux::optional opt = nullopt; + run_while(ctx, [&](auto&& elem) { + opt.emplace(std::move(fn)(FLUX_FWD(elem))); + return loop_break; + }); + return opt; + } + } +}; + +FLUX_EXPORT inline constexpr step_t step {}; + +/* + * MARK: Iterable + */ + +FLUX_EXPORT template +struct iterable_traits { + using _flux_is_primary_template = std::true_type; +}; + +namespace detail { + +template +concept has_member_iterable_traits = requires { typename T::flux_iterable_traits; } + && std::is_class_v; + +} // namespace detail + +template + requires detail::has_member_iterable_traits +struct iterable_traits : T::flux_iterable_traits { }; + +namespace detail { + +template +using iter_traits_t = iterable_traits>; + +template +concept has_iter_traits = !requires { typename iter_traits_t::_flux_is_primary_template; }; + +template +concept has_valid_iter_traits = has_iter_traits && requires(T& t) { + { iter_traits_t::iterate(t) } -> iteration_context; +}; + +template +concept has_member_iterate = requires(T& t) { + { t.iterate() } -> iteration_context; +}; + +template +concept can_iterate = has_valid_iter_traits || has_member_iterate + || std::ranges::input_range || sequence; + +template +struct range_iteration_context : immovable { +private: + std::ranges::iterator_t iter_; + std::ranges::sentinel_t sent_; + bool increment_next_ = false; + +public: + using element_type = std::ranges::range_reference_t; + + constexpr explicit range_iteration_context(R& rng) + : iter_(std::ranges::begin(rng)), + sent_(std::ranges::end(rng)) + { + } + + constexpr auto run_while(auto&& pred) -> iteration_result + { + if (increment_next_ && iter_ != sent_) { + ++iter_; + increment_next_ = false; + } + + while (iter_ != sent_) { + if (!pred(*iter_)) { + increment_next_ = true; + return iteration_result::incomplete; + } + ++iter_; + } + + return iteration_result::complete; + } +}; + +template +struct range_iteration_context : immovable { +private: + std::ranges::iterator_t iter_; + std::ranges::sentinel_t sent_; + +public: + using element_type = std::ranges::range_reference_t; + + constexpr explicit range_iteration_context(R& rng) + : iter_(std::ranges::begin(rng)), + sent_(std::ranges::end(rng)) + { + } + + constexpr auto run_while(auto&& pred) -> iteration_result + { + while (iter_ != sent_) { + if (!pred(*iter_++)) { + return iteration_result::incomplete; + } + } + + return iteration_result::complete; + } +}; + +template +struct sequence_iteration_context : immovable { +private: + Seq* ptr_; + cursor_t cur_; + bool inc_next_ = false; + +public: + using element_type = element_t; + + constexpr explicit sequence_iteration_context(Seq& seq) + : ptr_(std::addressof(seq)), + cur_(first(seq)) + { + } + + constexpr auto run_while(auto&& pred) -> iteration_result + { + if (inc_next_ && !is_last(*ptr_, cur_)) { + inc(*ptr_, cur_); + inc_next_ = false; + } + + while (!is_last(*ptr_, cur_)) { + if (!pred(read_at_unchecked(*ptr_, cur_))) { + inc_next_ = true; + return iteration_result::incomplete; + } + inc(*ptr_, cur_); + } + + return iteration_result::complete; + } +}; + +template +struct sequence_iteration_context { +private: + Seq* ptr_; + cursor_t cur_; + +public: + using element_type = element_t; + + constexpr explicit sequence_iteration_context(Seq& seq) + : ptr_(std::addressof(seq)), + cur_(first(seq)) + { + } + + constexpr auto run_while(auto&& pred) -> iteration_result + { + while (!is_last(*ptr_, cur_)) { + auto _ = detail::defer_t([&] { inc(*ptr_, cur_); }); + + if (!pred(read_at_unchecked(*ptr_, cur_))) { + return iteration_result::incomplete; + } + } + + return iteration_result::complete; + } +}; + +} // namespace detail + +FLUX_EXPORT +struct iterate_t { + template + requires detail::can_iterate + constexpr auto operator()(It& it) const -> iteration_context auto + { + if constexpr (detail::has_valid_iter_traits) { + return iterable_traits::iterate(it); + } else if constexpr (detail::has_member_iterate) { + return it.iterate(); + } else if constexpr (sequence) { + return detail::sequence_iteration_context(it); + } else if constexpr (std::ranges::input_range) { + return detail::range_iteration_context(it); + } + } +}; + +FLUX_EXPORT inline constexpr iterate_t iterate {}; + +FLUX_EXPORT template +concept iterable = requires(It& it) { + { iterate(it) } -> iteration_context; +}; + +FLUX_EXPORT template +using iteration_context_t = decltype(iterate(std::declval())); + +/* + * MARK: Reverse iterable + */ + +namespace detail { + +template +concept has_reverse_iter_traits = requires(T& t) { + { iter_traits_t::reverse_iterate(t) } -> iteration_context; +}; + +template +concept has_member_reverse_iterate = has_member_iterate && requires(T& t) { + { t.reverse_iterate() } -> iteration_context; +}; + +template +concept can_reverse_iterate = has_reverse_iter_traits || has_member_reverse_iterate + || (bidirectional_sequence && bounded_sequence) + || (std::ranges::bidirectional_range && std::ranges::common_range); + +template + requires std::ranges::common_range +struct range_reverse_iteration_context : immovable { +private: + std::ranges::iterator_t iter_; + std::ranges::iterator_t start_; + +public: + using element_type = std::ranges::range_reference_t; + + constexpr explicit range_reverse_iteration_context(R& rng) + : iter_(std::ranges::end(rng)), + start_(std::ranges::begin(rng)) + { + } + + constexpr auto run_while(auto&& pred) -> iteration_result + { + while (iter_ != start_) { + if (!pred(*--iter_)) { + return iteration_result::incomplete; + } + } + + return iteration_result::complete; + } +}; + +template + requires bounded_sequence +struct sequence_reverse_iteration_context : immovable { +private: + Seq* ptr_; + cursor_t cur_; + cursor_t start_; + +public: + using element_type = element_t; + + constexpr explicit sequence_reverse_iteration_context(Seq& seq) + : ptr_(std::addressof(seq)), + cur_(last(seq)), + start_(first(seq)) + { + } + + constexpr auto run_while(auto&& pred) -> iteration_result + { + while (cur_ != start_) { + dec(*ptr_, cur_); + if (!pred(read_at_unchecked(*ptr_, cur_))) { + return iteration_result::incomplete; + } + } + + return iteration_result::complete; + } +}; +} // namespace detail + +FLUX_EXPORT struct reverse_iterate_t { + template + requires detail::can_reverse_iterate + constexpr auto operator()(It& it) const -> iteration_context auto + { + if constexpr (detail::has_reverse_iter_traits) { + return iterable_traits::reverse_iterate(it); + } else if constexpr (detail::has_member_reverse_iterate) { + return it.reverse_iterate(); + } else if constexpr (bidirectional_sequence && bounded_sequence) { + return detail::sequence_reverse_iteration_context(it); + } else if constexpr (std::ranges::bidirectional_range + && std::ranges::common_range) { + return detail::range_reverse_iteration_context(it); + } + } +}; + +FLUX_EXPORT inline constexpr reverse_iterate_t reverse_iterate {}; + +FLUX_EXPORT template +concept reverse_iterable = iterable && requires(It& it) { + { reverse_iterate(it) } -> iteration_context; +}; + +FLUX_EXPORT template +using reverse_iteration_context_t = decltype(reverse_iterate(std::declval())); + +} // namespace flux + +#endif // FLUX_CORE_ITERABLE_CONCEPTS_HPP_INCLUDED \ No newline at end of file diff --git a/include/flux/core/utils.hpp b/include/flux/core/utils.hpp index 44a12811..43677afb 100644 --- a/include/flux/core/utils.hpp +++ b/include/flux/core/utils.hpp @@ -43,6 +43,15 @@ struct copy_fn { FLUX_EXPORT inline constexpr auto copy = detail::copy_fn{}; +struct immovable { + immovable() = default; + ~immovable() = default; + immovable(immovable const&) = delete; + immovable(immovable&&) = delete; + immovable& operator=(immovable const&) = delete; + immovable& operator=(immovable&&) = delete; +}; + namespace detail { template @@ -66,6 +75,50 @@ concept ordering_invocable = detail::ordering_invocable_ && detail::ordering_invocable_; +FLUX_EXPORT template +using callable_result_t = decltype(std::declval()(std::declval()...)); + +namespace detail { + +template +concept can_call_ = requires { typename callable_result_t; }; + +template +concept can_call_r + = can_call_ && std::convertible_to, R>; + +template +inline constexpr bool callable_ = false; + +template +inline constexpr bool callable_ + = std::is_void_v ? can_call_ : can_call_r; + +} // namespace detail + +FLUX_EXPORT template +concept callable_once = detail::callable_; + +FLUX_EXPORT template +concept callable_mut = detail::callable_ && callable_once; + +FLUX_EXPORT template +concept callable = detail::callable_ && callable_mut; + +namespace detail { + +template Fn> +struct [[nodiscard("Discarded defer_t will execute its associated function immediately")]] defer_t { + FLUX_NO_UNIQUE_ADDRESS Fn fn; + + ~defer_t() { fn(); } +}; + +// template +// defer_t(F) -> defer_t; + +} // namespace detail + } // namespace flux #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 13757184..8712d2da 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,6 +19,7 @@ add_executable(test-flux num/test_checked_ops.cpp num/test_default_ops.cpp + test_iterable_concepts.cpp test_concepts.cpp test_optional.cpp test_predicates.cpp diff --git a/test/test_iterable_concepts.cpp b/test/test_iterable_concepts.cpp new file mode 100644 index 00000000..c3a21d74 --- /dev/null +++ b/test/test_iterable_concepts.cpp @@ -0,0 +1,188 @@ +// Copyright (c) 2025 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#ifdef USE_MODULES +import flux; +#else +# include +#endif + +/* + * Iteration context concept tests + */ + +namespace { + +static_assert(not flux::iteration_context); +static_assert(not flux::iteration_context); +static_assert(not flux::iteration_context); +static_assert(not flux::iteration_context); + +struct empty { }; +static_assert(not flux::iteration_context); + +struct incomplete; +static_assert(not flux::iteration_context); + +struct no_element_type { + auto run_while(auto&&) -> flux::iteration_result; +}; +static_assert(not flux::iteration_context); + +struct void_element_type { + using element_type = void; + auto run_while(auto&&) -> flux::iteration_result; +}; +static_assert(not flux::iteration_context); + +struct no_run_while { + using element_type = int; +}; +static_assert(not flux::iteration_context); + +struct run_while_returns_void { + using element_type = int; + auto run_while(auto&&) -> void; +}; +static_assert(not flux::iteration_context); + +struct run_while_returns_int { + using element_type = int; + auto run_while(auto&&) -> int; +}; +static_assert(not flux::iteration_context); + +struct run_while_takes_void { + using element_type = int; + auto run_while() -> flux::iteration_result; +}; +static_assert(not flux::iteration_context); + +struct minimal_iteration_context { + using element_type = int; + auto run_while(auto&&) -> flux::iteration_result; +}; +static_assert(flux::iteration_context); + +} // namespace + +/* + * Iterable concept tests + */ + +namespace { + +struct has_empty_iter_traits_specialisation { }; +struct has_incorrect_iter_traits_specialisation { }; +struct has_valid_iter_traits_specialisation { }; +} // namespace + +template <> +struct flux::iterable_traits { }; + +template <> +struct flux::iterable_traits { + auto iterate() -> void; +}; + +template <> +struct flux::iterable_traits { + static auto iterate(has_valid_iter_traits_specialisation&) -> minimal_iteration_context + { + return {}; + } +}; + +namespace { + +// Things that are not iterable + +static_assert(not flux::iterable); +static_assert(not flux::iterable); +static_assert(not flux::iterable); +static_assert(not flux::iterable); +static_assert(not flux::iterable); + +static_assert(not flux::iterable); +static_assert(not flux::iterable); + +static_assert(not flux::iterable); +static_assert(not flux::iterable); + +struct member_iterate_returns_int { + auto iterate() -> int; +}; +static_assert(not flux::iterable); + +// Things that *are* iterable + +static_assert(flux::iterable); + +struct minimal_iterable { + auto iterate() -> minimal_iteration_context { return {}; } +}; +static_assert(flux::iterable); + +static_assert(flux::iterable); +static_assert(flux::iterable); +static_assert(flux::iterable>); + +static_assert(flux::iterable); + +} // namespace + +/* + * Reverse iterable concept tests + */ + +namespace { + +struct has_reverse_iter_traits { }; + +} // namespace + +template <> +struct flux::iterable_traits { + static auto iterate(has_reverse_iter_traits&) -> minimal_iteration_context { return {}; } + + static auto reverse_iterate(has_reverse_iter_traits&) -> minimal_iteration_context + { + return {}; + } +}; + +namespace { + +// Things that are not reverse iterable +static_assert(not flux::reverse_iterable); +static_assert(not flux::reverse_iterable); +static_assert(not flux::reverse_iterable); +static_assert(not flux::reverse_iterable); +static_assert(not flux::reverse_iterable); + +static_assert(not flux::reverse_iterable); +static_assert(not flux::reverse_iterable); + +static_assert(not flux::reverse_iterable); +static_assert(not flux::reverse_iterable); + +// Things that *are* reverse iterable +static_assert(flux::reverse_iterable); + +struct has_member_reverse_iterate { + auto iterate() -> minimal_iteration_context { return {}; } + auto reverse_iterate() -> minimal_iteration_context { return {}; } +}; + +static_assert(flux::reverse_iterable); + +static_assert(flux::reverse_iterable); +static_assert(flux::reverse_iterable); +static_assert(flux::reverse_iterable>); + +static_assert(flux::reverse_iterable); + +} // namespace \ No newline at end of file From 4c98b7759809e95b03316756e105eb037e22efdb Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Tue, 13 May 2025 16:57:13 +0100 Subject: [PATCH 03/66] Temporarily rename for_each_while on sequences ...to `seq_for_each_while()` --- example/top10/08_three_consecutive_odds.cpp | 2 +- include/flux/adaptor/cartesian_base.hpp | 23 +++++++++------------ include/flux/adaptor/chain.hpp | 4 ++-- include/flux/adaptor/cycle.hpp | 4 ++-- include/flux/adaptor/drop_while.hpp | 2 +- include/flux/adaptor/filter.hpp | 2 +- include/flux/adaptor/flatten.hpp | 4 ++-- include/flux/adaptor/map.hpp | 2 +- include/flux/adaptor/scan.hpp | 2 +- include/flux/adaptor/scan_first.hpp | 2 +- include/flux/adaptor/stride.hpp | 19 +++++++++-------- include/flux/adaptor/take.hpp | 2 +- include/flux/adaptor/take_while.hpp | 2 +- include/flux/algorithm/all_any_none.hpp | 18 ++++++++-------- include/flux/algorithm/contains.hpp | 6 +++--- include/flux/algorithm/count.hpp | 6 +++--- include/flux/algorithm/find.hpp | 22 ++++++++------------ include/flux/algorithm/fold.hpp | 2 +- include/flux/algorithm/for_each.hpp | 2 +- include/flux/algorithm/zip_algorithms.hpp | 2 +- include/flux/core/inline_sequence_base.hpp | 2 +- include/flux/core/ref.hpp | 4 ++-- include/flux/core/sequence_access.hpp | 4 ++-- test/test_cycle.cpp | 2 +- test/test_repeat.cpp | 8 +++---- 25 files changed, 70 insertions(+), 78 deletions(-) diff --git a/example/top10/08_three_consecutive_odds.cpp b/example/top10/08_three_consecutive_odds.cpp index 05d46d9e..dbaaf0f9 100644 --- a/example/top10/08_three_consecutive_odds.cpp +++ b/example/top10/08_three_consecutive_odds.cpp @@ -17,7 +17,7 @@ namespace version1 { auto const tco = [](std::initializer_list nums) { int odd_count = 0; - auto idx = flux::for_each_while(nums, [&](int i) { + auto idx = flux::seq_for_each_while(nums, [&](int i) { if (i % 2 != 0) { ++odd_count; } else { diff --git a/include/flux/adaptor/cartesian_base.hpp b/include/flux/adaptor/cartesian_base.hpp index 2ee0b469..21dea805 100644 --- a/include/flux/adaptor/cartesian_base.hpp +++ b/include/flux/adaptor/cartesian_base.hpp @@ -186,20 +186,17 @@ struct cartesian_traits_base_impl : default_sequence_traits { { // We need to iterate right to left. if constexpr (I == Arity - 1) { - std::get(cur) = flux::for_each_while(get_base(self), - [&](auto&& elem) { - keep_going = std::invoke(func, - element_t(FLUX_FWD(partial_elements)..., FLUX_FWD(elem))); - return keep_going; - }); + std::get(cur) = flux::seq_for_each_while(get_base(self), [&](auto&& elem) { + keep_going = std::invoke( + func, element_t(FLUX_FWD(partial_elements)..., FLUX_FWD(elem))); + return keep_going; + }); } else { - std::get(cur) = flux::for_each_while(get_base(self), - [&](auto&& elem) { - for_each_while_impl( - self, keep_going, cur, - func, FLUX_FWD(partial_elements)..., FLUX_FWD(elem)); - return keep_going; - }); + std::get(cur) = flux::seq_for_each_while(get_base(self), [&](auto&& elem) { + for_each_while_impl(self, keep_going, cur, func, + FLUX_FWD(partial_elements)..., FLUX_FWD(elem)); + return keep_going; + }); } } diff --git a/include/flux/adaptor/chain.hpp b/include/flux/adaptor/chain.hpp index 754428d7..bb9b545c 100644 --- a/include/flux/adaptor/chain.hpp +++ b/include/flux/adaptor/chain.hpp @@ -168,7 +168,7 @@ struct sequence_traits> : default_sequence_trait { if constexpr (N < End) { auto& base = std::get(self.bases_); - auto base_cur = flux::for_each_while(base, pred); + auto base_cur = flux::seq_for_each_while(base, pred); if (!flux::is_last(base, base_cur)) { return cursor_type(std::in_place_index, std::move(base_cur)); } else { @@ -176,7 +176,7 @@ struct sequence_traits> : default_sequence_trait } } else { return cursor_type(std::in_place_index, - flux::for_each_while(std::get(self.bases_), pred)); + flux::seq_for_each_while(std::get(self.bases_), pred)); } } diff --git a/include/flux/adaptor/cycle.hpp b/include/flux/adaptor/cycle.hpp index fd64b9e1..bd9482a3 100644 --- a/include/flux/adaptor/cycle.hpp +++ b/include/flux/adaptor/cycle.hpp @@ -128,7 +128,7 @@ struct cycle_adaptor : inline_sequence_base> { if constexpr (IsInfinite) { std::size_t n = 0; while (true) { - auto cur = flux::for_each_while(self.base_, constify_pred); + auto cur = flux::seq_for_each_while(self.base_, constify_pred); if (!flux::is_last(self.base_, cur)) { return cursor_type{std::move(cur), n}; } @@ -136,7 +136,7 @@ struct cycle_adaptor : inline_sequence_base> { } } else { for (std::size_t n = 0; n < self.data_.count; ++n) { - auto cur = flux::for_each_while(self.base_, constify_pred); + auto cur = flux::seq_for_each_while(self.base_, constify_pred); if (!flux::is_last(self.base_, cur)) { return cursor_type{std::move(cur), n}; } diff --git a/include/flux/adaptor/drop_while.hpp b/include/flux/adaptor/drop_while.hpp index ecd9c49b..ba064deb 100644 --- a/include/flux/adaptor/drop_while.hpp +++ b/include/flux/adaptor/drop_while.hpp @@ -36,7 +36,7 @@ struct drop_while_adaptor : inline_sequence_base> static constexpr auto first(auto& self) { - return flux::for_each_while(self.base_, std::ref(self.pred_)); + return flux::seq_for_each_while(self.base_, std::ref(self.pred_)); } static constexpr auto data(auto& self) diff --git a/include/flux/adaptor/filter.hpp b/include/flux/adaptor/filter.hpp index ba5ff208..a3e8e46b 100644 --- a/include/flux/adaptor/filter.hpp +++ b/include/flux/adaptor/filter.hpp @@ -103,7 +103,7 @@ class filter_adaptor : public inline_sequence_base> static constexpr auto for_each_while(auto& self, auto&& func) -> cursor_type { - return cursor_type{flux::for_each_while(self.base_, [&](auto&& elem) { + return cursor_type {flux::seq_for_each_while(self.base_, [&](auto&& elem) { if (std::invoke(self.pred_, elem)) { return std::invoke(func, FLUX_FWD(elem)); } else { diff --git a/include/flux/adaptor/flatten.hpp b/include/flux/adaptor/flatten.hpp index bd30e5a6..71eb2586 100644 --- a/include/flux/adaptor/flatten.hpp +++ b/include/flux/adaptor/flatten.hpp @@ -187,8 +187,8 @@ struct flatten_adaptor : inline_sequence_base> { static constexpr auto for_each_while(Self& self, auto&& pred) -> cursor_type { auto inner_cur = cursor_t{}; - auto outer_cur = flux::for_each_while(self.base_, [&](auto&& inner_seq) { - inner_cur = flux::for_each_while(inner_seq, pred); + auto outer_cur = flux::seq_for_each_while(self.base_, [&](auto&& inner_seq) { + inner_cur = flux::seq_for_each_while(inner_seq, pred); return flux::is_last(inner_seq, inner_cur); }); return cursor_type{.outer_cur = std::move(outer_cur), diff --git a/include/flux/adaptor/map.hpp b/include/flux/adaptor/map.hpp index 010ba45f..22a2668c 100644 --- a/include/flux/adaptor/map.hpp +++ b/include/flux/adaptor/map.hpp @@ -57,7 +57,7 @@ struct map_adaptor : inline_sequence_base> static constexpr auto for_each_while(auto& self, auto&& pred) { - return flux::for_each_while(self.base_, [&](auto&& elem) { + return flux::seq_for_each_while(self.base_, [&](auto&& elem) { return std::invoke(pred, std::invoke(self.func_, FLUX_FWD(elem))); }); } diff --git a/include/flux/adaptor/scan.hpp b/include/flux/adaptor/scan.hpp index 46b81040..9aa32d5f 100644 --- a/include/flux/adaptor/scan.hpp +++ b/include/flux/adaptor/scan.hpp @@ -141,7 +141,7 @@ struct scan_adaptor : inline_sequence_base> { static constexpr auto for_each_while(self_t& self, auto&& pred) -> cursor_type requires (Mode != scan_mode::exclusive) { - return cursor_type(flux::for_each_while(self.base_, [&](auto&& elem) { + return cursor_type(flux::seq_for_each_while(self.base_, [&](auto&& elem) { self.accum_ = std::invoke(self.func_, std::move(self.accum_), FLUX_FWD(elem)); return std::invoke(pred, std::as_const(self.accum_)); })); diff --git a/include/flux/adaptor/scan_first.hpp b/include/flux/adaptor/scan_first.hpp index 6ae8a74a..128178b3 100644 --- a/include/flux/adaptor/scan_first.hpp +++ b/include/flux/adaptor/scan_first.hpp @@ -97,7 +97,7 @@ struct scan_first_adaptor : inline_sequence_base cursor_type { - return cursor_type(flux::for_each_while(self.base_, [&](auto&& elem) { + return cursor_type(flux::seq_for_each_while(self.base_, [&](auto&& elem) { if (self.accum_.has_value()) { self.accum_.emplace( std::invoke(self.func_, diff --git a/include/flux/adaptor/stride.hpp b/include/flux/adaptor/stride.hpp index 611dad32..24d3df1a 100644 --- a/include/flux/adaptor/stride.hpp +++ b/include/flux/adaptor/stride.hpp @@ -100,7 +100,7 @@ struct stride_adaptor : inline_sequence_base> { requires sequence { int_t n = self.stride_; - return flux::for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { + return flux::seq_for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { if (++n < s) { return true; } else { @@ -237,14 +237,15 @@ struct stride_adaptor : inline_sequence_base> { requires sequence { int_t n = self.stride_; - auto c = flux::for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { - if (++n < s) { - return true; - } else { - n = 0; - return std::invoke(pred, FLUX_FWD(elem)); - } - }); + auto c + = flux::seq_for_each_while(self.base_, [&n, &pred, s = self.stride_](auto&& elem) { + if (++n < s) { + return true; + } else { + n = 0; + return std::invoke(pred, FLUX_FWD(elem)); + } + }); return cursor_type{std::move(c), (n + 1) % self.stride_}; } }; diff --git a/include/flux/adaptor/take.hpp b/include/flux/adaptor/take.hpp index d6c88f43..5dec9da3 100644 --- a/include/flux/adaptor/take.hpp +++ b/include/flux/adaptor/take.hpp @@ -134,7 +134,7 @@ struct take_adaptor : inline_sequence_base> static constexpr auto for_each_while(auto& self, auto&& pred) -> cursor_type { int_t len = self.count_; - auto cur = flux::for_each_while(self.base_, [&](auto&& elem) { + auto cur = flux::seq_for_each_while(self.base_, [&](auto&& elem) { return (len-- > 0) && std::invoke(pred, FLUX_FWD(elem)); }); diff --git a/include/flux/adaptor/take_while.hpp b/include/flux/adaptor/take_while.hpp index 464968d0..fb3df4a0 100644 --- a/include/flux/adaptor/take_while.hpp +++ b/include/flux/adaptor/take_while.hpp @@ -73,7 +73,7 @@ struct sequence_traits> static constexpr auto for_each_while(auto& self, auto&& func) { - return flux::for_each_while(self.base_, [&](auto&& elem) { + return flux::seq_for_each_while(self.base_, [&](auto&& elem) { if (!std::invoke(self.pred_, elem)) { return false; } else { diff --git a/include/flux/algorithm/all_any_none.hpp b/include/flux/algorithm/all_any_none.hpp index 6f0c8b44..26ca4583 100644 --- a/include/flux/algorithm/all_any_none.hpp +++ b/include/flux/algorithm/all_any_none.hpp @@ -17,9 +17,9 @@ struct fn { requires std::predicate> constexpr bool operator()(Seq&& seq, Pred pred) const { - return is_last(seq, for_each_while(seq, [&](auto&& elem) { - return std::invoke(pred, FLUX_FWD(elem)); - })); + return is_last(seq, seq_for_each_while(seq, [&](auto&& elem) { + return std::invoke(pred, FLUX_FWD(elem)); + })); } }; @@ -34,9 +34,9 @@ struct fn { requires std::predicate> constexpr bool operator()(Seq&& seq, Pred pred) const { - return is_last(seq, for_each_while(seq, [&](auto&& elem) { - return !std::invoke(pred, FLUX_FWD(elem)); - })); + return is_last(seq, seq_for_each_while(seq, [&](auto&& elem) { + return !std::invoke(pred, FLUX_FWD(elem)); + })); } }; @@ -51,9 +51,9 @@ struct fn { requires std::predicate> constexpr bool operator()(Seq&& seq, Pred pred) const { - return !is_last(seq, for_each_while(seq, [&](auto&& elem) { - return !std::invoke(pred, FLUX_FWD(elem)); - })); + return !is_last(seq, seq_for_each_while(seq, [&](auto&& elem) { + return !std::invoke(pred, FLUX_FWD(elem)); + })); } }; diff --git a/include/flux/algorithm/contains.hpp b/include/flux/algorithm/contains.hpp index 95ec9ff9..fccbd85c 100644 --- a/include/flux/algorithm/contains.hpp +++ b/include/flux/algorithm/contains.hpp @@ -18,9 +18,9 @@ struct contains_fn { constexpr auto operator()(Seq&& seq, Value const& value) const -> bool { - return !flux::is_last(seq, flux::for_each_while(seq, [&](auto&& elem) { - return FLUX_FWD(elem) != value; - })); + return !flux::is_last(seq, flux::seq_for_each_while(seq, [&](auto&& elem) { + return FLUX_FWD(elem) != value; + })); } }; diff --git a/include/flux/algorithm/count.hpp b/include/flux/algorithm/count.hpp index 9be202bd..7df6e1d8 100644 --- a/include/flux/algorithm/count.hpp +++ b/include/flux/algorithm/count.hpp @@ -21,7 +21,7 @@ struct count_fn { return flux::size(seq); } else { int_t counter = 0; - flux::for_each_while(seq, [&](auto&&) { + flux::seq_for_each_while(seq, [&](auto&&) { ++counter; return true; }); @@ -37,7 +37,7 @@ struct count_eq_fn { constexpr auto operator()(Seq&& seq, Value const& value) const -> int_t { int_t counter = 0; - flux::for_each_while(seq, [&](auto&& elem) { + flux::seq_for_each_while(seq, [&](auto&& elem) { if (value == FLUX_FWD(elem)) { ++counter; } @@ -54,7 +54,7 @@ struct count_if_fn { constexpr auto operator()(Seq&& seq, Pred pred) const -> int_t { int_t counter = 0; - flux::for_each_while(seq, [&](auto&& elem) { + flux::seq_for_each_while(seq, [&](auto&& elem) { if (std::invoke(pred, FLUX_FWD(elem))) { ++counter; } diff --git a/include/flux/algorithm/find.hpp b/include/flux/algorithm/find.hpp index 06e0cfd9..6bf3f093 100644 --- a/include/flux/algorithm/find.hpp +++ b/include/flux/algorithm/find.hpp @@ -20,9 +20,7 @@ struct find_fn { template static constexpr auto impl(Seq&& seq, Value const& value) -> cursor_t { - return for_each_while(seq, [&](auto&& elem) { - return FLUX_FWD(elem) != value; - }); + return seq_for_each_while(seq, [&](auto&& elem) { return FLUX_FWD(elem) != value; }); } public: @@ -30,10 +28,10 @@ struct find_fn { requires std::equality_comparable_with, Value const&> constexpr auto operator()(Seq&& seq, Value const& value) const -> cursor_t { - constexpr auto can_memchr = - contiguous_sequence && sized_sequence && - std::same_as> && - flux::detail::any_of, char, signed char, unsigned char, char8_t, std::byte>; + constexpr auto can_memchr = contiguous_sequence && sized_sequence + && std::same_as> + && flux::detail::any_of, char, signed char, unsigned char, char8_t, + std::byte>; if constexpr (can_memchr) { if (std::is_constant_evaluated()) { @@ -65,9 +63,8 @@ struct find_if_fn { constexpr auto operator()(Seq&& seq, Pred pred) const -> cursor_t { - return for_each_while(seq, [&](auto&& elem) { - return !std::invoke(pred, FLUX_FWD(elem)); - }); + return seq_for_each_while(seq, + [&](auto&& elem) { return !std::invoke(pred, FLUX_FWD(elem)); }); } }; @@ -77,9 +74,8 @@ struct find_if_not_fn { constexpr auto operator()(Seq&& seq, Pred pred) const -> cursor_t { - return for_each_while(seq, [&](auto&& elem) { - return std::invoke(pred, FLUX_FWD(elem)); - }); + return seq_for_each_while(seq, + [&](auto&& elem) { return std::invoke(pred, FLUX_FWD(elem)); }); } }; diff --git a/include/flux/algorithm/fold.hpp b/include/flux/algorithm/fold.hpp index 8e01e75b..e60f2b42 100644 --- a/include/flux/algorithm/fold.hpp +++ b/include/flux/algorithm/fold.hpp @@ -22,7 +22,7 @@ struct fold_op { constexpr auto operator()(Seq&& seq, Func func, Init init = Init{}) const -> R { R init_ = R(std::move(init)); - flux::for_each_while(seq, [&func, &init_](auto&& elem) { + flux::seq_for_each_while(seq, [&func, &init_](auto&& elem) { init_ = std::invoke(func, std::move(init_), FLUX_FWD(elem)); return true; }); diff --git a/include/flux/algorithm/for_each.hpp b/include/flux/algorithm/for_each.hpp index 43a59917..0d4f853c 100644 --- a/include/flux/algorithm/for_each.hpp +++ b/include/flux/algorithm/for_each.hpp @@ -19,7 +19,7 @@ struct for_each_fn { !infinite_sequence) constexpr auto operator()(Seq&& seq, Func func) const -> Func { - (void) flux::for_each_while(FLUX_FWD(seq), [&](auto&& elem) { + (void)flux::seq_for_each_while(FLUX_FWD(seq), [&](auto&& elem) { std::invoke(func, FLUX_FWD(elem)); return true; }); diff --git a/include/flux/algorithm/zip_algorithms.hpp b/include/flux/algorithm/zip_algorithms.hpp index a53bacdc..32c92a5b 100644 --- a/include/flux/algorithm/zip_algorithms.hpp +++ b/include/flux/algorithm/zip_algorithms.hpp @@ -21,7 +21,7 @@ struct zip_for_each_while_fn { if constexpr (sizeof...(Seqs) == 0) { return std::tuple<>{}; } else if constexpr (sizeof...(Seqs) == 1) { - return std::tuple...>(flux::for_each_while(seqs..., std::ref(pred))); + return std::tuple...>(flux::seq_for_each_while(seqs..., std::ref(pred))); } else { return [&pred, &...seqs = seqs, ...curs = flux::first(seqs)]() mutable { while (!(flux::is_last(seqs, curs) || ...)) { diff --git a/include/flux/core/inline_sequence_base.hpp b/include/flux/core/inline_sequence_base.hpp index 3cbbeb5a..06863e2a 100644 --- a/include/flux/core/inline_sequence_base.hpp +++ b/include/flux/core/inline_sequence_base.hpp @@ -129,7 +129,7 @@ struct inline_sequence_base { detail::boolean_testable>> constexpr auto for_each_while(Pred pred) { - return flux::for_each_while(derived(), std::ref(pred)); + return flux::seq_for_each_while(derived(), std::ref(pred)); } /// Returns true if the sequence contains no elements diff --git a/include/flux/core/ref.hpp b/include/flux/core/ref.hpp index 39c5ff2d..74c92b24 100644 --- a/include/flux/core/ref.hpp +++ b/include/flux/core/ref.hpp @@ -105,9 +105,9 @@ struct passthrough_traits_base : default_sequence_traits { template static constexpr auto for_each_while(Self& self, auto&& pred) - -> decltype(flux::for_each_while(self.base(), FLUX_FWD(pred))) + -> decltype(flux::seq_for_each_while(self.base(), FLUX_FWD(pred))) { - return flux::for_each_while(self.base(), FLUX_FWD(pred)); + return flux::seq_for_each_while(self.base(), FLUX_FWD(pred)); } }; diff --git a/include/flux/core/sequence_access.hpp b/include/flux/core/sequence_access.hpp index 0e3a0d66..422e4306 100644 --- a/include/flux/core/sequence_access.hpp +++ b/include/flux/core/sequence_access.hpp @@ -160,7 +160,7 @@ struct move_at_unchecked_fn { } }; -struct for_each_while_fn { +struct seq_for_each_while_fn { template requires std::invocable> && boolean_testable>> @@ -185,7 +185,7 @@ FLUX_EXPORT inline constexpr auto data = detail::data_fn{}; FLUX_EXPORT inline constexpr auto last = detail::last_fn{}; FLUX_EXPORT inline constexpr auto size = detail::size_fn{}; FLUX_EXPORT inline constexpr auto usize = detail::usize_fn{}; -FLUX_EXPORT inline constexpr auto for_each_while = detail::for_each_while_fn{}; +FLUX_EXPORT inline constexpr auto seq_for_each_while = detail::seq_for_each_while_fn {}; namespace detail { diff --git a/test/test_cycle.cpp b/test/test_cycle.cpp index 41f16c2f..1689180a 100644 --- a/test/test_cycle.cpp +++ b/test/test_cycle.cpp @@ -52,7 +52,7 @@ constexpr bool test_cycle() { // Make sure internal iteration works as expected int counter = 101; - cur = flux::for_each_while(seq, [&counter](auto&&) { return counter-- > 0; }); + cur = flux::seq_for_each_while(seq, [&counter](auto&&) { return counter-- > 0; }); STATIC_CHECK(seq.distance(cur, seq.first()) == -101); } diff --git a/test/test_repeat.cpp b/test/test_repeat.cpp index a87070b8..bc56743f 100644 --- a/test/test_repeat.cpp +++ b/test/test_repeat.cpp @@ -49,8 +49,7 @@ constexpr bool test_repeat() // Check that internal iteration works as expected { auto counter = 0; - auto inner_cur = - flux::for_each_while(seq, [&](int) { return counter++ < 5; }); + auto inner_cur = flux::seq_for_each_while(seq, [&](int) { return counter++ < 5; }); STATIC_CHECK(inner_cur == 5); } } @@ -164,15 +163,14 @@ constexpr bool test_repeat_bounded() // Check that internal iteration works as expected { - auto cur = flux::for_each_while(seq, flux::pred::true_); + auto cur = flux::seq_for_each_while(seq, flux::pred::true_); STATIC_CHECK(cur == seq.last()); } // ...and again with early termination { auto counter = 0; - auto cur = - flux::for_each_while(seq, [&](int) { return counter++ < 3; }); + auto cur = flux::seq_for_each_while(seq, [&](int) { return counter++ < 3; }); STATIC_CHECK(cur == 3); } } From e5102e023e724f6df34670b7006ab124976fb9c7 Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Thu, 15 May 2025 16:49:23 +0100 Subject: [PATCH 04/66] Modify many algorithms to work on iterables Not quite all of them yet though --- .clang-format | 1 + include/flux/algorithm/all_any_none.hpp | 65 ++++----- include/flux/algorithm/compare.hpp | 139 ++++++++++--------- include/flux/algorithm/contains.hpp | 24 ++-- include/flux/algorithm/count.hpp | 62 ++++----- include/flux/algorithm/ends_with.hpp | 84 +++-------- include/flux/algorithm/equal.hpp | 114 ++++++++------- include/flux/algorithm/fill.hpp | 64 +++++---- include/flux/algorithm/find_element.hpp | 40 ++++++ include/flux/algorithm/fold.hpp | 126 +++++++++-------- include/flux/algorithm/for_each.hpp | 25 ++-- include/flux/algorithm/for_each_while.hpp | 26 ++++ include/flux/algorithm/minmax.hpp | 78 ++++++----- include/flux/algorithm/output_to.hpp | 58 ++++---- include/flux/algorithm/starts_with.hpp | 37 ++--- include/flux/algorithm/swap_elements.hpp | 35 ++--- include/flux/algorithm/write_to.hpp | 21 ++- include/flux/algorithm/zip_algorithms.hpp | 48 +++++-- include/flux/core/iterable_concepts.hpp | 129 ++++++++++++++++- include/flux/core/operation_requirements.hpp | 17 ++- include/flux/core/utils.hpp | 2 +- test/test_iterable_concepts.cpp | 58 +++++++- test/test_utils.hpp | 28 ++-- 23 files changed, 745 insertions(+), 536 deletions(-) create mode 100644 include/flux/algorithm/find_element.hpp create mode 100644 include/flux/algorithm/for_each_while.hpp diff --git a/.clang-format b/.clang-format index e0c2e3e7..7c884e39 100644 --- a/.clang-format +++ b/.clang-format @@ -18,5 +18,6 @@ IndentPPDirectives: AfterHash InsertBraces: true NamespaceIndentation: None PackConstructorInitializers: CurrentLine +SpaceBeforeCpp11BracedList: false SortIncludes: Never TabWidth: 4 diff --git a/include/flux/algorithm/all_any_none.hpp b/include/flux/algorithm/all_any_none.hpp index 26ca4583..edb17574 100644 --- a/include/flux/algorithm/all_any_none.hpp +++ b/include/flux/algorithm/all_any_none.hpp @@ -6,60 +6,51 @@ #ifndef FLUX_ALGORITHM_ALL_ANY_NONE_HPP_INCLUDED #define FLUX_ALGORITHM_ALL_ANY_NONE_HPP_INCLUDED -#include +#include namespace flux { -namespace all_detail { - -struct fn { - template - requires std::predicate> - constexpr bool operator()(Seq&& seq, Pred pred) const +FLUX_EXPORT +struct all_t { + template + requires std::predicate> + [[nodiscard]] + constexpr auto operator()(It&& it, Pred const pred) const -> bool { - return is_last(seq, seq_for_each_while(seq, [&](auto&& elem) { - return std::invoke(pred, FLUX_FWD(elem)); - })); + return for_each_while(it, [&](auto&& elem) { return std::invoke(pred, FLUX_FWD(elem)); }) + == iteration_result::complete; } }; -} // namespace all_detail - -FLUX_EXPORT inline constexpr auto all = all_detail::fn{}; - -namespace none_detail { +FLUX_EXPORT inline constexpr all_t all {}; -struct fn { - template - requires std::predicate> - constexpr bool operator()(Seq&& seq, Pred pred) const +FLUX_EXPORT +struct none_t { + template + requires std::predicate> + [[nodiscard]] + constexpr auto operator()(It&& it, Pred const pred) const -> bool { - return is_last(seq, seq_for_each_while(seq, [&](auto&& elem) { - return !std::invoke(pred, FLUX_FWD(elem)); - })); + return for_each_while(it, [&](auto&& elem) { return !std::invoke(pred, FLUX_FWD(elem)); }) + == iteration_result::complete; } }; -} // namespace none_detail +FLUX_EXPORT inline constexpr none_t none {}; -FLUX_EXPORT inline constexpr auto none = none_detail::fn{}; - -namespace any_detail { - -struct fn { - template - requires std::predicate> - constexpr bool operator()(Seq&& seq, Pred pred) const +FLUX_EXPORT +struct any_t { + template + requires std::predicate> + [[nodiscard]] + constexpr auto operator()(It&& it, Pred const pred) const -> bool { - return !is_last(seq, seq_for_each_while(seq, [&](auto&& elem) { - return !std::invoke(pred, FLUX_FWD(elem)); - })); + return for_each_while(it, [&](auto&& elem) { return !std::invoke(pred, FLUX_FWD(elem)); }) + == iteration_result::incomplete; } }; -} // namespace any_detail - -FLUX_EXPORT inline constexpr auto any = any_detail::fn{}; +FLUX_EXPORT inline constexpr any_t any {}; template template diff --git a/include/flux/algorithm/compare.hpp b/include/flux/algorithm/compare.hpp index 82a1b17e..639da076 100644 --- a/include/flux/algorithm/compare.hpp +++ b/include/flux/algorithm/compare.hpp @@ -14,90 +14,97 @@ namespace flux { -namespace detail { - -struct compare_fn { +FLUX_EXPORT +struct compare_t { private: - template - static constexpr auto impl(Seq1& seq1, Seq2& seq2, Cmp& cmp) - -> std::decay_t< - std::invoke_result_t, element_t>> + template + requires ordering_invocable, iterable_element_t> + static constexpr auto impl(It1& it1, It2& it2, Cmp& cmp) -> std::decay_t< + std::invoke_result_t, iterable_element_t>> { - auto cur1 = flux::first(seq1); - auto cur2 = flux::first(seq2); + iteration_context auto ctx1 = iterate(it1); + iteration_context auto ctx2 = iterate(it2); + + while (true) { + auto opt1 = next_element(ctx1); + auto opt2 = next_element(ctx2); - while (!flux::is_last(seq1, cur1) && !flux::is_last(seq2, cur2)) { - if (auto r = std::invoke(cmp, flux::read_at(seq1, cur1), flux::read_at(seq2, cur2)); - r != 0) { - return r; + if (opt1.has_value() && opt2.has_value()) { + auto r = std::invoke(cmp, opt1.value(), opt2.value()); + if (r != 0) { + return r; + } + } else if (opt1.has_value()) { + return std::strong_ordering::greater; + } else if (opt2.has_value()) { + return std::strong_ordering::less; + } else { + return std::strong_ordering::equal; } - flux::inc(seq1, cur1); - flux::inc(seq2, cur2); } + } - return !flux::is_last(seq1, cur1) ? std::strong_ordering::greater : - !flux::is_last(seq2, cur2) ? std::strong_ordering::less : - std::strong_ordering::equal; + template + static constexpr auto memcmp_impl(Seq1& seq1, Seq2& seq2) -> std::strong_ordering + { + auto const seq1_size = flux::usize(seq1); + auto const seq2_size = flux::usize(seq2); + auto min_size = (cmp::min)(seq1_size, seq2_size); + + int cmp_result = 0; + if (min_size > 0) { + auto data1 = flux::data(seq1); + FLUX_ASSERT(data1 != nullptr); + auto data2 = flux::data(seq2); + FLUX_ASSERT(data2 != nullptr); + + cmp_result = std::memcmp(data1, data2, min_size); + } + + if (cmp_result == 0) { + if (seq1_size == seq2_size) { + return std::strong_ordering::equal; + } else if (seq1_size < seq2_size) { + return std::strong_ordering::less; + } else /* seq1_size > seq2_size */ { + return std::strong_ordering::greater; + } + } else if (cmp_result > 0) { + return std::strong_ordering::greater; + } else /* cmp_result < 0 */ { + return std::strong_ordering::less; + } + } + + template + static consteval auto can_memcmp() -> bool + { + return std::same_as && contiguous_sequence + && contiguous_sequence && sized_sequence && sized_sequence + && std::same_as, iterable_value_t> + && std::unsigned_integral> + && ((sizeof(iterable_value_t) == 1) || (std::endian::native == std::endian::big)); } public: - template - requires ordering_invocable, element_t> - constexpr auto operator()(Seq1 &&seq1, Seq2 &&seq2, Cmp cmp = {}) const - -> std::decay_t< - std::invoke_result_t, element_t>> + template + requires ordering_invocable, iterable_element_t> + constexpr auto operator()(It1&& it1, It2&& it2, Cmp cmp = {}) const -> std::decay_t< + std::invoke_result_t, iterable_element_t>> { - constexpr bool can_memcmp = - std::same_as && - contiguous_sequence && - contiguous_sequence && - sized_sequence && - sized_sequence && - std::same_as, value_t> && - std::unsigned_integral> && - ((sizeof(value_t) == 1) || (std::endian::native == std::endian::big)); - - if constexpr (can_memcmp) { + if constexpr (can_memcmp()) { if (std::is_constant_evaluated()) { - return impl(seq1, seq2, cmp); // LCOV_EXCL_LINE + return impl(it1, it2, cmp); // LCOV_EXCL_LINE } else { - auto const seq1_size = flux::usize(seq1); - auto const seq2_size = flux::usize(seq2); - auto min_size = (cmp::min)(seq1_size, seq2_size); - - int cmp_result = 0; - if(min_size > 0) { - auto data1 = flux::data(seq1); - FLUX_ASSERT(data1 != nullptr); - auto data2 = flux::data(seq2); - FLUX_ASSERT(data2 != nullptr); - - cmp_result = std::memcmp(data1, data2, min_size); - } - - if (cmp_result == 0) { - if (seq1_size == seq2_size) { - return std::strong_ordering::equal; - } else if (seq1_size < seq2_size) { - return std::strong_ordering::less; - } else /* seq1_size > seq2_size */ { - return std::strong_ordering::greater; - } - } else if (cmp_result > 0) { - return std::strong_ordering::greater; - } else /* cmp_result < 0 */ { - return std::strong_ordering::less; - } + return memcmp_impl(it1, it2); } } else { - return impl(seq1, seq2, cmp); + return impl(it1, it2, cmp); } } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto compare = detail::compare_fn{}; +FLUX_EXPORT inline constexpr auto compare = compare_t{}; } // namespace flux diff --git a/include/flux/algorithm/contains.hpp b/include/flux/algorithm/contains.hpp index fccbd85c..5096a3f5 100644 --- a/include/flux/algorithm/contains.hpp +++ b/include/flux/algorithm/contains.hpp @@ -6,28 +6,22 @@ #ifndef FLUX_ALGORITHM_CONTAINS_HPP_INCLUDED #define FLUX_ALGORITHM_CONTAINS_HPP_INCLUDED -#include +#include namespace flux { -namespace detail { - -struct contains_fn { - template - requires std::equality_comparable_with, Value const&> - constexpr auto operator()(Seq&& seq, Value const& value) const - -> bool +FLUX_EXPORT +struct contains_t { + template + requires std::equality_comparable_with, Value const&> + [[nodiscard]] + constexpr auto operator()(It&& it, Value const& value) const -> bool { - return !flux::is_last(seq, flux::seq_for_each_while(seq, [&](auto&& elem) { - return FLUX_FWD(elem) != value; - })); + return any(it, [&](auto&& elem) { return FLUX_FWD(elem) == value; }); } }; - -} // namespace detail - -FLUX_EXPORT inline constexpr auto contains = detail::contains_fn{}; +FLUX_EXPORT inline constexpr contains_t contains {}; template template diff --git a/include/flux/algorithm/count.hpp b/include/flux/algorithm/count.hpp index 7df6e1d8..e4831028 100644 --- a/include/flux/algorithm/count.hpp +++ b/include/flux/algorithm/count.hpp @@ -6,69 +6,59 @@ #ifndef FLUX_ALGORITHM_COUNT_HPP_INCLUDED #define FLUX_ALGORITHM_COUNT_HPP_INCLUDED -#include +#include namespace flux { -namespace detail { - -struct count_fn { - template +FLUX_EXPORT +struct count_t { + template [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> int_t + constexpr auto operator()(It&& it) const -> int_t { - if constexpr (sized_sequence) { - return flux::size(seq); + if constexpr (sized_iterable) { + return flux::iterable_size(it); } else { int_t counter = 0; - flux::seq_for_each_while(seq, [&](auto&&) { - ++counter; - return true; - }); + for_each(it, [&](auto&&) { counter = num::add(counter, int_t{1}); }); return counter; } } }; -struct count_eq_fn { - template - requires std::equality_comparable_with, Value const&> +FLUX_EXPORT inline constexpr count_t count{}; + +FLUX_EXPORT +struct count_if_t { + template + requires std::predicate> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Value const& value) const -> int_t + constexpr auto operator()(It&& it, Pred pred) const -> int_t { int_t counter = 0; - flux::seq_for_each_while(seq, [&](auto&& elem) { - if (value == FLUX_FWD(elem)) { + for_each(it, [&](auto&& elem) { + if (std::invoke(pred, FLUX_FWD(elem))) { ++counter; } - return true; }); return counter; } }; -struct count_if_fn { - template - requires std::predicate> +FLUX_EXPORT inline constexpr count_if_t count_if{}; + +FLUX_EXPORT +struct count_eq_t { + template + requires std::equality_comparable_with, Value const&> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Pred pred) const -> int_t + constexpr auto operator()(It&& it, Value const& value) const -> int_t { - int_t counter = 0; - flux::seq_for_each_while(seq, [&](auto&& elem) { - if (std::invoke(pred, FLUX_FWD(elem))) { - ++counter; - } - return true; - }); - return counter; + return count_if(it, [&](auto&& elem) { return value == FLUX_FWD(elem); }); } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto count = detail::count_fn{}; -FLUX_EXPORT inline constexpr auto count_eq = detail::count_eq_fn{}; -FLUX_EXPORT inline constexpr auto count_if = detail::count_if_fn{}; +FLUX_EXPORT inline constexpr count_eq_t count_eq{}; template constexpr auto inline_sequence_base::count() diff --git a/include/flux/algorithm/ends_with.hpp b/include/flux/algorithm/ends_with.hpp index b3c87690..12c6d853 100644 --- a/include/flux/algorithm/ends_with.hpp +++ b/include/flux/algorithm/ends_with.hpp @@ -6,85 +6,41 @@ #ifndef FLUX_ALGORITHM_ENDS_WITH_HPP_INCLUDED #define FLUX_ALGORITHM_ENDS_WITH_HPP_INCLUDED +#include +#include #include #include +#include namespace flux { -namespace detail { - -struct ends_with_fn { -private: - template - static constexpr auto bidir_impl(H& h, N& n, auto& cmp) -> bool +FLUX_EXPORT +struct ends_with_t { + template + requires std::predicate, iterable_element_t> + && (multipass_sequence || sized_iterable) + && (multipass_sequence || sized_iterable) + [[nodiscard]] + constexpr auto operator()(Haystack&& haystack, Needle&& needle, Cmp cmp = Cmp{}) const -> bool { - if constexpr (sized_sequence && sized_sequence) { - if (flux::size(h) < flux::size(n)) { - return false; - } - } - - auto cur1 = flux::last(h); - auto cur2 = flux::last(n); - - auto const f1 = flux::first(h); - auto const f2 = flux::first(n); - - if (cur2 == f2) { - return true; - } else if (cur1 == f1) { - return false; - } - - while (true) { - flux::dec(h, cur1); - flux::dec(n, cur2); - - if (!std::invoke(cmp, flux::read_at(h, cur1), flux::read_at(n, cur2))) { - return false; - } - - if (cur2 == f2) { - return true; - } else if (cur1 == f1) { - return false; - } - } - } - -public: - template - requires std::predicate, element_t> && - (multipass_sequence || sized_sequence) && - (multipass_sequence || sized_sequence) - constexpr auto operator()(Haystack&& haystack, Needle&& needle, Cmp cmp = Cmp{}) const - -> bool - { - if constexpr(bidirectional_sequence && - bounded_sequence && - bidirectional_sequence && - bounded_sequence) { - return bidir_impl(haystack, needle, cmp); + if constexpr (reverse_iterable && reverse_iterable) { + return starts_with(reverse(from_fwd_ref(haystack)), reverse(from_fwd_ref(needle)), + std::ref(cmp)); } else { - int_t len1 = flux::count(haystack); - int_t len2 = flux::count(needle); + int_t haystack_size = flux::count(haystack); + int_t needle_size = flux::count(needle); - if (len1 < len2) { + if (haystack_size < needle_size) { return false; } - auto cur1 = flux::first(haystack); - detail::advance(haystack, cur1, len1 - len2); - - return flux::equal(flux::slice(haystack, std::move(cur1), flux::last), - needle, std::move(cmp)); + return equal(drop(from_fwd_ref(haystack), num::sub(haystack_size, needle_size)), needle, + std::ref(cmp)); } } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto ends_with = detail::ends_with_fn{}; +FLUX_EXPORT inline constexpr auto ends_with = ends_with_t{}; template template diff --git a/include/flux/algorithm/equal.hpp b/include/flux/algorithm/equal.hpp index 2ba36888..89370ce5 100644 --- a/include/flux/algorithm/equal.hpp +++ b/include/flux/algorithm/equal.hpp @@ -12,89 +12,97 @@ namespace flux { -namespace detail { - -struct equal_fn { +FLUX_EXPORT +struct equal_t { private: - template - static constexpr auto impl(Seq1& seq1, Seq2& seq2, Cmp cmp) + template + static constexpr auto impl(It1& it1, It2& it2, Cmp& cmp) -> bool { - auto cur1 = flux::first(seq1); - auto cur2 = flux::first(seq2); + iteration_context auto ctx1 = iterate(it1); + iteration_context auto ctx2 = iterate(it2); + + while (true) { + flux::optional opt1 = next_element(ctx1); + flux::optional opt2 = next_element(ctx2); - while (!flux::is_last(seq1, cur1) && !flux::is_last(seq2, cur2)) { - if (!std::invoke(cmp, flux::read_at(seq1, cur1), flux::read_at(seq2, cur2))) { + if (opt1.has_value() && opt2.has_value()) { + if (!std::invoke(cmp, opt1.value(), opt2.value())) { + return false; + } + } else if (opt1.has_value() || opt2.has_value()) { return false; + } else { + return true; } - flux::inc(seq1, cur1); - flux::inc(seq2, cur2); } + } + + template + static constexpr auto memcmp_impl(Seq1& seq1, Seq2& seq2) -> bool + { + auto size = flux::usize(seq1); + if (size == 0) { + return true; + } + + auto data1 = flux::data(seq1); + auto data2 = flux::data(seq2); + FLUX_ASSERT(data1 != nullptr); + FLUX_ASSERT(data2 != nullptr); - return flux::is_last(seq1, cur1) == flux::is_last(seq2, cur2); + auto result = std::memcmp(data1, data2, size * sizeof(iterable_value_t)); + return result == 0; + } + + template + static consteval auto can_memcmp() -> bool + { + return std::same_as && contiguous_sequence + && contiguous_sequence && sized_sequence && sized_sequence + && std::same_as, iterable_value_t> + && (std::integral> || std::is_pointer_v>) + && std::has_unique_object_representations_v>; } public: - template - requires std::predicate, element_t> - constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const - -> bool + template + requires std::predicate, iterable_element_t> + constexpr auto operator()(It1&& it1, It2&& it2, Cmp cmp = {}) const -> bool { - if constexpr (sized_sequence && sized_sequence) { - if (flux::size(seq1) != flux::size(seq2)) { + if constexpr (sized_iterable && sized_iterable) { + if (flux::iterable_size(it1) != flux::iterable_size(it2)) { return false; } } - constexpr bool can_memcmp = - std::same_as && - contiguous_sequence && contiguous_sequence && - sized_sequence && sized_sequence && - std::same_as, value_t> && - (std::integral> || std::is_pointer_v>) && - std::has_unique_object_representations_v>; - - if constexpr (can_memcmp) { + if constexpr (can_memcmp()) { if (std::is_constant_evaluated()) { - return impl(seq1, seq2, cmp); // LCOV_EXCL_LINE + return impl(it1, it2, cmp); // LCOV_EXCL_LINE } else { - auto size = flux::usize(seq1); - if(size == 0) { - return true; - } - - auto data1 = flux::data(seq1); - auto data2 = flux::data(seq2); - FLUX_ASSERT(data1 != nullptr); - FLUX_ASSERT(data2 != nullptr); - - auto result = std::memcmp(data1, data2, size * sizeof(value_t)); - return result == 0; + return memcmp_impl(it1, it2); } } else { - return impl(seq1, seq2, cmp); + return impl(it1, it2, cmp); } } - template - requires (sequence> && - sequence> && - !std::equality_comparable_with, element_t> && - std::is_invocable_v) - constexpr auto operator()(Seq1&& seq1, Seq2&& seq2) const -> bool + template + requires iterable> && iterable> + && (!std::equality_comparable_with, iterable_element_t> + && std::is_invocable_v) + constexpr auto operator()(It1&& it1, It2&& it2) const -> bool { - if constexpr (sized_sequence && sized_sequence) { - if (flux::size(seq1) != flux::size(seq2)) { + if constexpr (sized_iterable && sized_iterable) { + if (flux::iterable_size(it1) != flux::iterable_size(it2)) { return false; } } - return (*this)(seq1, seq2, *this); + return (*this)(it1, it2, *this); } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto equal = detail::equal_fn{}; +FLUX_EXPORT inline constexpr equal_t equal{}; } // namespace flux diff --git a/include/flux/algorithm/fill.hpp b/include/flux/algorithm/fill.hpp index 71c57066..94c3c6c5 100644 --- a/include/flux/algorithm/fill.hpp +++ b/include/flux/algorithm/fill.hpp @@ -11,51 +11,49 @@ namespace flux { -namespace detail { - -struct fill_fn { +FLUX_EXPORT +struct fill_t { private: + template + static constexpr auto impl(It&& it, Value const& value) -> void + { + flux::for_each(it, [&value](auto&& elem) { FLUX_FWD(elem) = value; }); + } + template - static constexpr auto impl(Seq& seq, Value const& value) + static constexpr auto memset_impl(Seq& seq, Value const& value) -> void { - flux::for_each(seq, [&value](auto&& elem) { FLUX_FWD(elem) = value; }); + if (std::is_constant_evaluated()) { + impl(seq, value); // LCOV_EXCL_LINE + } else { + auto size = flux::usize(seq); + if (size == 0) { + return; + } + + FLUX_ASSERT(flux::data(seq) != nullptr); + + std::memset(flux::data(seq), value, size * sizeof(value_t)); + } } public: - template Seq> - constexpr void operator()(Seq&& seq, Value const& value) const + template + requires std::assignable_from, Value const&> + constexpr auto operator()(It&& it, Value const& value) const -> void { - constexpr bool can_memset = - contiguous_sequence && - sized_sequence && - std::same_as> && - // only allow memset on single byte types - sizeof(value_t) == 1 && - std::is_trivially_copyable_v>; - - if constexpr (can_memset) { - if (std::is_constant_evaluated()) { - impl(seq, value); // LCOV_EXCL_LINE - } else { - auto size = flux::usize(seq); - if(size == 0) { - return; - } - - FLUX_ASSERT(flux::data(seq) != nullptr); - - std::memset(flux::data(seq), value, - size * sizeof(value_t)); - } + if constexpr (contiguous_sequence && sized_sequence + && std::same_as> + // only allow memset on single byte types + && sizeof(value_t) == 1 && std::is_trivially_copyable_v>) { + memset_impl(it, value); } else { - impl(seq, value); + impl(it, value); } } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto fill = detail::fill_fn{}; +FLUX_EXPORT inline constexpr fill_t fill {}; template template diff --git a/include/flux/algorithm/find_element.hpp b/include/flux/algorithm/find_element.hpp new file mode 100644 index 00000000..c9359d5f --- /dev/null +++ b/include/flux/algorithm/find_element.hpp @@ -0,0 +1,40 @@ +// Copyright (c) 2025 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef FLUX_ALGORITHM_FIND_ELEMENT_HPP_INCLUDED +#define FLUX_ALGORITHM_FIND_ELEMENT_HPP_INCLUDED + +#include + +namespace flux { + +FLUX_EXPORT +struct find_element_if_t { + template + requires std::predicate> + constexpr auto operator()(It&& it, Pred pred) const + { + iterable auto filtered = filter(std::ref(it), std::ref(pred)); + iteration_context auto ctx = iterate(filtered); + return next_element(ctx); + } +}; + +FLUX_EXPORT inline constexpr find_element_if_t find_element_if{}; + +FLUX_EXPORT +struct find_element_t { + template + requires std::equality_comparable_with, Value const&> + constexpr auto operator()(It&& it, Value const& value) const + { + return find_element_if(it, [&value](auto&& elem) { return FLUX_FWD(elem) == value; }); + } +}; + +FLUX_EXPORT inline constexpr find_element_t find_element{}; + +} // namespace flux + +#endif // FLUX_ALGORITHM_FIND_ELEMENT_HPP_INCLUDED diff --git a/include/flux/algorithm/fold.hpp b/include/flux/algorithm/fold.hpp index e60f2b42..88f9fb8b 100644 --- a/include/flux/algorithm/fold.hpp +++ b/include/flux/algorithm/fold.hpp @@ -6,23 +6,22 @@ #ifndef FLUX_ALGORITHM_FOLD_HPP_INCLUDED #define FLUX_ALGORITHM_FOLD_HPP_INCLUDED -#include +#include namespace flux { -namespace detail { - -struct fold_op { - template , - typename R = fold_result_t> - requires std::invocable> && - std::invocable> && - std::convertible_to && - std::assignable_from>> - constexpr auto operator()(Seq&& seq, Func func, Init init = Init{}) const -> R +FLUX_EXPORT +struct fold_t { + template , + typename R = fold_result_t> + requires std::invocable> + && std::invocable> && std::convertible_to + && std::assignable_from>> + [[nodiscard]] + constexpr auto operator()(It&& it, Func func, Init init = Init{}) const -> R { R init_ = R(std::move(init)); - flux::seq_for_each_while(seq, [&func, &init_](auto&& elem) { + for_each_while(it, [&func, &init_](auto&& elem) { init_ = std::invoke(func, std::move(init_), FLUX_FWD(elem)); return true; }); @@ -30,31 +29,39 @@ struct fold_op { } }; -struct fold_first_op { - template > - requires std::invocable> && - std::assignable_from&, std::invoke_result_t>> +FLUX_EXPORT inline constexpr auto fold = fold_t{}; + +FLUX_EXPORT +struct fold_first_t { + template > + requires std::invocable> + && std::assignable_from&, + std::invoke_result_t>> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Func func) const -> flux::optional + constexpr auto operator()(It&& it, Func func) const -> flux::optional { - auto cur = flux::first(seq); + iteration_context auto ctx = iterate(it); - if (flux::is_last(seq, cur)) { - return std::nullopt; - } - - V init(flux::read_at(seq, cur)); - flux::inc(seq, cur); + auto opt = next_element(ctx); - while (!flux::is_last(seq, cur)) { - init = std::invoke(func, std::move(init), flux::read_at(seq, cur)); - flux::inc(seq, cur); + if (!opt.has_value()) { + return flux::nullopt; } + V init = std::move(opt).value(); + run_while(ctx, [&](auto&& elem) { + init = std::invoke(func, std::move(init), FLUX_FWD(elem)); + return loop_continue; + }); + return flux::optional(std::in_place, std::move(init)); } }; +FLUX_EXPORT inline constexpr fold_first_t fold_first{}; + +namespace detail { + // Workaround libc++18 invoke() bug: https://github.com/llvm/llvm-project/issues/106428 consteval bool libcpp_fold_invoke_workaround_required() { @@ -65,66 +72,67 @@ consteval bool libcpp_fold_invoke_workaround_required() #endif } -struct sum_op { - template - requires std::default_initializable> && - std::invocable> +} // namespace detail + +FLUX_EXPORT +struct sum_t { + template + requires std::invocable> && requires { iterable_value_t(0); } [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> value_t + constexpr auto operator()(It&& it) const -> iterable_value_t { - if constexpr (num::integral>) { - if constexpr (libcpp_fold_invoke_workaround_required()) { + if constexpr (num::integral>) { + if constexpr (detail::libcpp_fold_invoke_workaround_required()) { auto add = [](T lhs, T rhs) -> T { return num::add(lhs, rhs); }; - return fold_op{}(FLUX_FWD(seq), add, value_t(0)); + return fold(FLUX_FWD(it), add, iterable_value_t(0)); } else { - return fold_op{}(FLUX_FWD(seq), num::add, value_t(0)); + return fold(FLUX_FWD(it), num::add, iterable_value_t(0)); } } else { - return fold_op{}(FLUX_FWD(seq), std::plus<>{}, value_t(0)); + return fold(FLUX_FWD(it), std::plus<>{}, iterable_value_t(0)); } } }; -struct product_op { - template - requires std::invocable> && - requires { value_t(1); } +FLUX_EXPORT inline constexpr auto sum = sum_t{}; + +FLUX_EXPORT +struct product_t { + template + requires std::invocable> + && requires { iterable_value_t(1); } [[nodiscard]] - constexpr auto operator()(Seq&& seq) const -> value_t + constexpr auto operator()(It&& it) const -> iterable_value_t { - if constexpr (num::integral>) { - if constexpr (libcpp_fold_invoke_workaround_required()) { + if constexpr (num::integral>) { + if constexpr (detail::libcpp_fold_invoke_workaround_required()) { auto mul = [](T lhs, T rhs) -> T { return num::mul(lhs, rhs); }; - return fold_op{}(FLUX_FWD(seq), mul, value_t(1)); + return fold(FLUX_FWD(it), mul, iterable_value_t(1)); } else { - return fold_op{}(FLUX_FWD(seq), num::mul, value_t(1)); + return fold(FLUX_FWD(it), num::mul, iterable_value_t(1)); } } else { - return fold_op{}(FLUX_FWD(seq), std::multiplies<>{}, value_t(1)); + return fold(FLUX_FWD(it), std::multiplies<>{}, iterable_value_t(1)); } } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto fold = detail::fold_op{}; -FLUX_EXPORT inline constexpr auto fold_first = detail::fold_first_op{}; -FLUX_EXPORT inline constexpr auto sum = detail::sum_op{}; -FLUX_EXPORT inline constexpr auto product = detail::product_op{}; +FLUX_EXPORT inline constexpr auto product = product_t{}; template template requires foldable [[nodiscard]] -constexpr auto inline_sequence_base::fold(Func func, Init init) -> fold_result_t +constexpr auto inline_sequence_base::fold(Func func, Init init) + -> fold_result_t { return flux::fold(derived(), std::move(func), std::move(init)); } template template - requires std::invocable, element_t> && - std::assignable_from&, std::invoke_result_t, element_t>> + requires std::invocable, element_t> + && std::assignable_from&, std::invoke_result_t, element_t>> constexpr auto inline_sequence_base::fold_first(Func func) { return flux::fold_first(derived(), std::move(func)); @@ -132,16 +140,14 @@ constexpr auto inline_sequence_base::fold_first(Func func) template constexpr auto inline_sequence_base::sum() - requires foldable, value_t> && - std::default_initializable> + requires foldable, value_t> && std::default_initializable> { return flux::sum(derived()); } template constexpr auto inline_sequence_base::product() - requires foldable, value_t> && - requires { value_t(1); } + requires foldable, value_t> && requires { value_t(1); } { return flux::product(derived()); } diff --git a/include/flux/algorithm/for_each.hpp b/include/flux/algorithm/for_each.hpp index 0d4f853c..5b3292d8 100644 --- a/include/flux/algorithm/for_each.hpp +++ b/include/flux/algorithm/for_each.hpp @@ -6,30 +6,25 @@ #ifndef FLUX_ALGORITHM_FOR_EACH_HPP_INCLUDED #define FLUX_ALGORITHM_FOR_EACH_HPP_INCLUDED -#include +#include namespace flux { -namespace detail { - -struct for_each_fn { - - template - requires (std::invocable> && - !infinite_sequence) - constexpr auto operator()(Seq&& seq, Func func) const -> Func +FLUX_EXPORT +struct for_each_t { + template + requires std::invocable> + constexpr auto operator()(It&& it, Func func) const -> Func { - (void)flux::seq_for_each_while(FLUX_FWD(seq), [&](auto&& elem) { - std::invoke(func, FLUX_FWD(elem)); - return true; + for_each_while(it, [&](auto&& elem) { + static_cast(std::invoke(func, FLUX_FWD(elem))); + return loop_continue; }); return func; } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto for_each = detail::for_each_fn{}; +FLUX_EXPORT inline constexpr for_each_t for_each {}; template template diff --git a/include/flux/algorithm/for_each_while.hpp b/include/flux/algorithm/for_each_while.hpp new file mode 100644 index 00000000..b8d5ba94 --- /dev/null +++ b/include/flux/algorithm/for_each_while.hpp @@ -0,0 +1,26 @@ +// Copyright (c) 2025 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef FLUX_ALGORITHM_FOR_EACH_WHILE_HPP_INCLUDED +#define FLUX_ALGORITHM_FOR_EACH_WHILE_HPP_INCLUDED + +#include + +namespace flux { + +FLUX_EXPORT +struct for_each_while_t { + template )> Pred> + constexpr auto operator()(It&& it, Pred&& pred) const -> iteration_result + { + iteration_context auto ctx = iterate(it); + return ctx.run_while(FLUX_FWD(pred)); + } +}; + +FLUX_EXPORT inline constexpr for_each_while_t for_each_while {}; + +} // namespace flux + +#endif // FLUX_ALGORITHM_FOR_EACH_WHILE_HPP_INCLUDED \ No newline at end of file diff --git a/include/flux/algorithm/minmax.hpp b/include/flux/algorithm/minmax.hpp index 52018419..0f0829f7 100644 --- a/include/flux/algorithm/minmax.hpp +++ b/include/flux/algorithm/minmax.hpp @@ -19,17 +19,16 @@ struct minmax_result { T max; }; -namespace detail { - -struct min_op { - template Cmp = std::compare_three_way> +FLUX_EXPORT +struct min_t { + template Cmp = std::compare_three_way> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Cmp cmp = Cmp{}) const - -> flux::optional> + constexpr auto operator()(It&& it, Cmp cmp = Cmp{}) const + -> flux::optional> { - return flux::fold_first(FLUX_FWD(seq), [&](auto min, auto&& elem) -> value_t { + return fold_first(FLUX_FWD(it), [&](auto min, auto&& elem) -> iterable_value_t { if (std::invoke(cmp, elem, min) < 0) { - return value_t(FLUX_FWD(elem)); + return iterable_value_t(FLUX_FWD(elem)); } else { return min; } @@ -37,15 +36,18 @@ struct min_op { } }; -struct max_op { - template Cmp = std::compare_three_way> +FLUX_EXPORT inline constexpr min_t min{}; + +FLUX_EXPORT +struct max_t { + template Cmp = std::compare_three_way> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Cmp cmp = Cmp{}) const - -> flux::optional> + constexpr auto operator()(It&& it, Cmp cmp = Cmp{}) const + -> flux::optional> { - return flux::fold_first(FLUX_FWD(seq), [&](auto max, auto&& elem) -> value_t { + return fold_first(FLUX_FWD(it), [&](auto max, auto&& elem) -> iterable_value_t { if (!(std::invoke(cmp, elem, max) < 0)) { - return value_t(FLUX_FWD(elem)); + return iterable_value_t(FLUX_FWD(elem)); } else { return max; } @@ -53,42 +55,42 @@ struct max_op { } }; -struct minmax_op { - template Cmp = std::compare_three_way> +FLUX_EXPORT inline constexpr max_t max{}; + +FLUX_EXPORT +struct minmax_t { + template Cmp = std::compare_three_way> [[nodiscard]] - constexpr auto operator()(Seq&& seq, Cmp cmp = Cmp{}) const - -> flux::optional>> + constexpr auto operator()(It&& it, Cmp cmp = Cmp{}) const + -> flux::optional>> { - using R = minmax_result>; + using R = minmax_result>; - auto cur = flux::first(seq); - if (flux::is_last(seq, cur)) { - return std::nullopt; + iteration_context auto ctx = iterate(it); + + auto opt = next_element(ctx); + if (!opt.has_value()) { + return flux::nullopt; } - R init = R{value_t(flux::read_at(seq, cur)), - value_t(flux::read_at(seq, cur))}; + auto min = iterable_value_t(opt.value()); + auto max = iterable_value_t(std::move(opt).value()); - auto fold_fn = [&](R mm, auto&& elem) -> R { - if (std::invoke(cmp, elem, mm.min) < 0) { - mm.min = value_t(elem); + run_while(ctx, [&](auto&& elem) { + if (std::invoke(cmp, elem, min) < 0) { + min = iterable_value_t(elem); } - if (!(std::invoke(cmp, elem, mm.max) < 0)) { - mm.max = value_t(FLUX_FWD(elem)); + if (!(std::invoke(cmp, elem, max) < 0)) { + max = iterable_value_t(FLUX_FWD(elem)); } - return mm; - }; + return loop_continue; + }); - return flux::optional(std::in_place, - flux::fold(flux::slice(seq, std::move(cur), flux::last), fold_fn, std::move(init))); + return flux::optional(R(std::move(min), std::move(max))); } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto min = detail::min_op{}; -FLUX_EXPORT inline constexpr auto max = detail::max_op{}; -FLUX_EXPORT inline constexpr auto minmax = detail::minmax_op{}; +FLUX_EXPORT inline constexpr minmax_t minmax{}; template template diff --git a/include/flux/algorithm/output_to.hpp b/include/flux/algorithm/output_to.hpp index 3815df7f..9bd03586 100644 --- a/include/flux/algorithm/output_to.hpp +++ b/include/flux/algorithm/output_to.hpp @@ -13,43 +13,49 @@ namespace flux { -namespace detail { - -struct output_to_fn { +FLUX_EXPORT +struct output_to_t { private: template - static constexpr auto impl(Seq& seq, Iter& iter) -> Iter + static constexpr auto impl(Seq& seq, Iter iter) -> Iter { - flux::for_each(seq, [&iter](auto&& elem) { + for_each(seq, [&iter](auto&& elem) { *iter = FLUX_FWD(elem); ++iter; }); return iter; } + template + static consteval auto can_memcpy() -> bool + { + return contiguous_sequence && sized_sequence && std::contiguous_iterator + && std::is_trivially_copyable_v>; + } + + template + static constexpr auto memcpy_impl(Seq& seq, Iter iter) -> Iter + { + auto size = flux::usize(seq); + if (size == 0) { + return iter; + } + FLUX_ASSERT(flux::data(seq) != nullptr); + std::memmove(std::to_address(iter), flux::data(seq), size * sizeof(value_t)); + return iter + num::cast>(flux::size(seq)); + } + public: - template - requires std::indirectly_writable> + template + requires std::weakly_incrementable + && std::indirectly_writable> constexpr auto operator()(Seq&& seq, Iter iter) const -> Iter { - constexpr bool can_memcpy = - contiguous_sequence && - sized_sequence && - std::contiguous_iterator && - std::is_trivially_copyable_v>; - - if constexpr (can_memcpy) { + if constexpr (can_memcpy()) { if (std::is_constant_evaluated()) { return impl(seq, iter); // LCOV_EXCL_LINE } else { - auto size = flux::usize(seq); - if (size == 0) { - return iter; - } - FLUX_ASSERT(flux::data(seq) != nullptr); - std::memmove(std::to_address(iter), flux::data(seq), - size * sizeof(value_t)); - return iter + num::checked_cast>(flux::size(seq)); + return memcpy_impl(seq, iter); } } else { return impl(seq, iter); @@ -57,19 +63,15 @@ struct output_to_fn { } }; -} - -FLUX_EXPORT inline constexpr auto output_to = detail::output_to_fn{}; +FLUX_EXPORT inline constexpr output_to_t output_to{}; template template - requires std::weakly_incrementable && - std::indirectly_writable> + requires std::weakly_incrementable && std::indirectly_writable> constexpr auto inline_sequence_base::output_to(Iter iter) -> Iter { return flux::output_to(derived(), std::move(iter)); } - } #endif // FLUX_ALGORITHM_OUTPUT_TO_HPP_INCLUDED diff --git a/include/flux/algorithm/starts_with.hpp b/include/flux/algorithm/starts_with.hpp index 5ebf88bd..36f44b14 100644 --- a/include/flux/algorithm/starts_with.hpp +++ b/include/flux/algorithm/starts_with.hpp @@ -10,37 +10,40 @@ namespace flux { -namespace detail { - -struct starts_with_fn { - template +FLUX_EXPORT +struct starts_with_t { + template requires std::predicate, element_t> + [[nodiscard]] constexpr auto operator()(Haystack&& haystack, Needle&& needle, Cmp cmp = Cmp{}) const -> bool { - if constexpr (sized_sequence && sized_sequence) { - if (flux::size(haystack) < flux::size(needle)) { + if constexpr (sized_iterable && sized_iterable) { + if (flux::iterable_size(haystack) < flux::iterable_size(needle)) { return false; } } - auto h = flux::first(haystack); - auto n = flux::first(needle); + iteration_context auto haystack_ctx = iterate(haystack); + iteration_context auto needle_ctx = iterate(needle); + + while (true) { + auto haystack_elem = next_element(haystack_ctx); + auto needle_elem = next_element(needle_ctx); - while (!flux::is_last(haystack, h) && !flux::is_last(needle, n)) { - if (!std::invoke(cmp, flux::read_at(haystack, h), flux::read_at(needle, n))) { + if (haystack_elem.has_value() && needle_elem.has_value()) { + if (!std::invoke(cmp, haystack_elem.value(), needle_elem.value())) { + return false; + } + } else if (needle_elem.has_value()) { return false; + } else { + return true; } - flux::inc(haystack, h); - flux::inc(needle, n); } - - return flux::is_last(needle, n); } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto starts_with = detail::starts_with_fn{}; +FLUX_EXPORT inline constexpr starts_with_t starts_with{}; template template diff --git a/include/flux/algorithm/swap_elements.hpp b/include/flux/algorithm/swap_elements.hpp index a0ad44a3..7d1e1b70 100644 --- a/include/flux/algorithm/swap_elements.hpp +++ b/include/flux/algorithm/swap_elements.hpp @@ -10,28 +10,29 @@ namespace flux { -namespace detail { - -struct swap_elements_fn { - template - requires element_swappable_with - constexpr void operator()(Seq1&& seq1, Seq2&& seq2) const +FLUX_EXPORT +struct swap_elements_t { + template + requires std::swappable_with, iterable_element_t> + constexpr auto operator()(It1&& it1, It2&& it2) const -> void { - auto cur1 = flux::first(seq1); - auto cur2 = flux::first(seq2); - - while (!flux::is_last(seq1, cur1) && !flux::is_last(seq2, cur2)) { - flux::swap_with(seq1, cur1, seq2, cur2); - flux::inc(seq1, cur1); - flux::inc(seq2, cur2); + iteration_context auto ctx1 = iterate(it1); + iteration_context auto ctx2 = iterate(it2); + + while (true) { + auto opt1 = next_element(ctx1); + auto opt2 = next_element(ctx2); + + if (opt1.has_value() && opt2.has_value()) { + std::ranges::swap(*std::move(opt1), *std::move(opt2)); + } else { + break; + } } } }; -} - -FLUX_EXPORT inline constexpr auto swap_elements = detail::swap_elements_fn{}; - +FLUX_EXPORT inline constexpr swap_elements_t swap_elements{}; } #endif // FLUX_ALGORITHM_SWAP_ELEMENTS_HPP_INCLUDED diff --git a/include/flux/algorithm/write_to.hpp b/include/flux/algorithm/write_to.hpp index 0886ffbe..2a532fdd 100644 --- a/include/flux/algorithm/write_to.hpp +++ b/include/flux/algorithm/write_to.hpp @@ -12,27 +12,24 @@ namespace flux { -namespace detail { - -struct write_to_fn { - - template +FLUX_EXPORT +struct write_to_t { + template requires std::derived_from - auto operator()(Seq&& seq, OStream& os) const - -> std::ostream& + auto operator()(It&& it, OStream& os) const -> OStream& { bool first = true; os << '['; - flux::for_each(FLUX_FWD(seq), [&os, &first](auto&& elem) { + for_each(it, [&os, &first](auto&& elem) { if (first) { first = false; } else { os << ", "; } - if constexpr (sequence>) { - write_to_fn{}(FLUX_FWD(elem), os); + if constexpr (iterable>) { + write_to_t{}(FLUX_FWD(elem), os); } else { os << FLUX_FWD(elem); } @@ -43,9 +40,7 @@ struct write_to_fn { } }; -} // namespace detail - -FLUX_EXPORT inline constexpr auto write_to = detail::write_to_fn{}; +FLUX_EXPORT inline constexpr write_to_t write_to{}; template auto inline_sequence_base::write_to(std::ostream& os) -> std::ostream& diff --git a/include/flux/algorithm/zip_algorithms.hpp b/include/flux/algorithm/zip_algorithms.hpp index 32c92a5b..f1522b4e 100644 --- a/include/flux/algorithm/zip_algorithms.hpp +++ b/include/flux/algorithm/zip_algorithms.hpp @@ -5,7 +5,8 @@ #ifndef FLUX_ALGORITHM_ZIP_ALGORITHMS_HPP_INCLUDED #define FLUX_ALGORITHM_ZIP_ALGORITHMS_HPP_INCLUDED -#include +#include +#include namespace flux { @@ -13,24 +14,27 @@ namespace detail { struct zip_for_each_while_fn { template - requires std::invocable...> && - boolean_testable...>> - constexpr auto operator()(Pred pred, Seqs&&... seqs) const - -> std::tuple...> + requires std::invocable...> + && boolean_testable...>> + constexpr auto operator()(Pred pred, Seqs&&... seqs) const -> iteration_result { if constexpr (sizeof...(Seqs) == 0) { - return std::tuple<>{}; + return {}; } else if constexpr (sizeof...(Seqs) == 1) { - return std::tuple...>(flux::seq_for_each_while(seqs..., std::ref(pred))); + return flux::for_each_while(seqs..., std::ref(pred)); } else { - return [&pred, &...seqs = seqs, ...curs = flux::first(seqs)]() mutable { - while (!(flux::is_last(seqs, curs) || ...)) { - if (!std::invoke(pred, flux::read_at_unchecked(seqs, curs)...)) { - break; + return [&pred, ... ctxs = iterate(seqs)]() mutable { + return [&pred, &ctxs..., ... opts = step(ctxs, std::identity {})]() mutable { + while (true) { + if (!(opts.has_value() && ...)) { + return iteration_result::complete; + } + if (!std::invoke(pred, opts.value_unchecked()...)) { + return iteration_result::incomplete; + } + ((opts = step(ctxs, std::identity {})), ...); } - (flux::inc(seqs, curs), ...); - } - return std::tuple...>(std::move(curs)...); + }(); }(); } } @@ -61,7 +65,21 @@ struct zip_find_if_fn { constexpr auto operator()(Pred pred, Seqs&&... seqs) const -> std::tuple...> { - return zip_for_each_while(std::not_fn(pred), seqs...); + if constexpr (sizeof...(Seqs) == 0) { + return {}; + } else if constexpr (sizeof...(Seqs) == 1) { + return std::tuple...>(flux::find_if(seqs..., std::ref(pred))); + } else { + return [&pred, &... seqs = seqs, ... curs = first(seqs)]() mutable { + while ((!is_last(seqs, curs) && ...)) { + if (std::invoke(pred, read_at_unchecked(seqs, curs)...)) { + break; + } + ((inc(seqs, curs)), ...); + } + return std::tuple...>(std::move(curs)...); + }(); + } } }; diff --git a/include/flux/core/iterable_concepts.hpp b/include/flux/core/iterable_concepts.hpp index 8e3d3380..3b4be154 100644 --- a/include/flux/core/iterable_concepts.hpp +++ b/include/flux/core/iterable_concepts.hpp @@ -6,6 +6,7 @@ #define FLUX_CORE_ITERABLE_CONCEPTS_HPP_INCLUDED #include +#include #include namespace flux { @@ -46,7 +47,7 @@ FLUX_EXPORT struct step_t { template requires callable_once)> - constexpr auto operator()(Ctx& ctx, Fn&& fn) + constexpr auto operator()(Ctx& ctx, Fn fn) const { using E = context_element_t; using R = callable_result_t; @@ -73,7 +74,21 @@ struct step_t { } }; -FLUX_EXPORT inline constexpr step_t step {}; +FLUX_EXPORT inline constexpr step_t step{}; + +FLUX_EXPORT +struct next_element_t { + template + constexpr auto operator()(Ctx& ctx) const + -> std::conditional_t>, + flux::optional>>, + flux::optional>> + { + return step(ctx, std::identity{}); + } +}; + +FLUX_EXPORT inline constexpr next_element_t next_element{}; /* * MARK: Iterable @@ -115,8 +130,8 @@ concept has_member_iterate = requires(T& t) { }; template -concept can_iterate = has_valid_iter_traits || has_member_iterate - || std::ranges::input_range || sequence; +concept can_iterate = has_valid_iter_traits || has_member_iterate || sequence + || std::ranges::input_range; template struct range_iteration_context : immovable { @@ -253,7 +268,7 @@ struct iterate_t { constexpr auto operator()(It& it) const -> iteration_context auto { if constexpr (detail::has_valid_iter_traits) { - return iterable_traits::iterate(it); + return detail::iter_traits_t::iterate(it); } else if constexpr (detail::has_member_iterate) { return it.iterate(); } else if constexpr (sequence) { @@ -266,13 +281,113 @@ struct iterate_t { FLUX_EXPORT inline constexpr iterate_t iterate {}; +FLUX_EXPORT template +using iteration_context_t = decltype(iterate(std::declval())); + +FLUX_EXPORT template +using iterable_element_t = context_element_t>; + +namespace detail { + +// Try to work out the value type of the iterable +// * If iterable_context_t::value_type exists, use that +// * otherwise, if iterable_traits::value_type exists, use that +// * otherwise, if T::value_type exists, use that, +// * otherwise, fall back to std::remove_cvref_t> + +template +concept has_context_value_type = requires { typename iteration_context_t::value_type; }; + +template +concept has_traits_value_type = requires { typename iter_traits_t::value_type; }; + +template +concept has_member_value_type = requires { typename T::value_type; }; + +template +struct iterable_value_type { + using type = std::remove_cvref_t>; +}; + +template + requires has_context_value_type +struct iterable_value_type { + using type = typename iteration_context_t::value_type; +}; + +template + requires has_traits_value_type && (!has_context_value_type) +struct iterable_value_type { + using type = typename iter_traits_t::value_type; +}; + +template + requires has_member_value_type && (!has_context_value_type && !has_traits_value_type) +struct iterable_value_type { + using type = std::remove_cvref_t>; +}; + +} // namespace detail + +FLUX_EXPORT template +using iterable_value_t = typename detail::iterable_value_type::type; + +FLUX_EXPORT template +using iterable_common_element_t = std::common_reference_t, value_t&>; + FLUX_EXPORT template concept iterable = requires(It& it) { { iterate(it) } -> iteration_context; +} && requires { + typename iterable_value_t; +} && std::is_object_v> && std::common_reference_with&&, iterable_value_t&>; + +/* + * MARK: Sized iterable + */ + +namespace detail { + +template +concept has_iterable_traits_size = requires(T& t) { + { iter_traits_t::size(t) } -> num::integral; }; -FLUX_EXPORT template -using iteration_context_t = decltype(iterate(std::declval())); +template +concept has_member_size = has_member_iterate && requires(T& t) { + { t.size() } -> num::integral; +}; + +template +concept is_sized_iterable = has_iterable_traits_size || has_member_size || sized_sequence + || std::ranges::sized_range; + +} // namespace detail + +struct iterable_size_fn_t { + template + requires detail::is_sized_iterable + [[nodiscard]] + constexpr auto operator()(It&& it) const -> int_t + { + if constexpr (detail::has_iterable_traits_size) { + return num::cast(detail::iter_traits_t::size(it)); + } else if constexpr (detail::has_member_size) { + return num::cast(it.size()); + } else if constexpr (sized_sequence) { + return sequence_traits>::size(it); + } else if constexpr (std::ranges::sized_range) { + return num::cast(std::ranges::ssize(it)); + } + } +}; + +FLUX_EXPORT inline constexpr iterable_size_fn_t iterable_size{}; + +FLUX_EXPORT template +concept sized_iterable = iterable && requires(It&& it) { + { iterable_size(it) } -> std::same_as; +}; /* * MARK: Reverse iterable diff --git a/include/flux/core/operation_requirements.hpp b/include/flux/core/operation_requirements.hpp index f5f4c9b3..5d8f6274 100644 --- a/include/flux/core/operation_requirements.hpp +++ b/include/flux/core/operation_requirements.hpp @@ -7,6 +7,7 @@ #define FLUX_CORE_OPERATION_REQUIREMENTS_HPP_INCLUDED #include +#include namespace flux { @@ -52,15 +53,13 @@ concept foldable = detail::foldable_; FLUX_EXPORT -template -concept weak_ordering_for = - sequence && - sequence && - ordering_invocable, element_t, std::weak_ordering> && - ordering_invocable&, element_t, std::weak_ordering> && - ordering_invocable, value_t&, std::weak_ordering> && - ordering_invocable&, value_t&, std::weak_ordering> && - ordering_invocable, common_element_t, std::weak_ordering>; +template +concept weak_ordering_for = iterable && iterable + && ordering_invocable, iterable_element_t, std::weak_ordering> + && ordering_invocable&, iterable_value_t&, std::weak_ordering> + && ordering_invocable&, iterable_value_t&, std::weak_ordering> + && ordering_invocable, iterable_common_element_t, + std::weak_ordering>; } // namespace flux diff --git a/include/flux/core/utils.hpp b/include/flux/core/utils.hpp index 43677afb..0889a38a 100644 --- a/include/flux/core/utils.hpp +++ b/include/flux/core/utils.hpp @@ -111,7 +111,7 @@ template Fn> struct [[nodiscard("Discarded defer_t will execute its associated function immediately")]] defer_t { FLUX_NO_UNIQUE_ADDRESS Fn fn; - ~defer_t() { fn(); } + constexpr ~defer_t() { fn(); } }; // template diff --git a/test/test_iterable_concepts.cpp b/test/test_iterable_concepts.cpp index c3a21d74..04392b49 100644 --- a/test/test_iterable_concepts.cpp +++ b/test/test_iterable_concepts.cpp @@ -185,4 +185,60 @@ static_assert(flux::reverse_iterable>); static_assert(flux::reverse_iterable); -} // namespace \ No newline at end of file +} // namespace + +/* + * sized_iterable concept tests + */ + +namespace { + +// Things that are not sized iterable + +static_assert(not flux::sized_iterable); +static_assert(not flux::sized_iterable); +static_assert(not flux::sized_iterable); +static_assert(not flux::sized_iterable); +static_assert(not flux::sized_iterable); +static_assert(not flux::sized_iterable); +static_assert(not flux::sized_iterable); +static_assert(not flux::sized_iterable); +static_assert(not flux::sized_iterable); +static_assert(not flux::sized_iterable); + +struct has_invalid_member_size { + auto iterate() -> minimal_iteration_context { return {}; } + auto size() -> bool { return false; } +}; + +static_assert(not flux::sized_iterable); + +struct has_sized_iter_traits_specialisation { }; + +} // namespace + +template <> +struct flux::iterable_traits { + static auto iterate(has_sized_iter_traits_specialisation&) -> minimal_iteration_context + { + return {}; + } + + static auto size(has_sized_iter_traits_specialisation&) -> int_t { return 0; } +}; + +namespace { + +// Things that *are* sized iterable +static_assert(flux::sized_iterable); +static_assert(flux::sized_iterable); +static_assert(flux::sized_iterable); +static_assert(flux::sized_iterable>); + +struct has_member_size { + auto iterate() -> minimal_iteration_context { return {}; } + auto size() -> int { return 0; } +}; +static_assert(flux::sized_iterable); + +} // namespace diff --git a/test/test_utils.hpp b/test/test_utils.hpp index eb0e8958..0b29c96c 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -24,21 +24,27 @@ inline namespace test_utils { inline constexpr struct { private: - static constexpr bool impl(flux::sequence auto&& seq1, flux::sequence auto&& seq2) + static constexpr bool impl(flux::iterable auto&& it1, flux::iterable auto&& it2) { using namespace flux; - auto cur1 = first(seq1); - auto cur2 = first(seq2); - - while (!is_last(seq1, cur1) && !is_last(seq2, cur2)) { - if (read_at(seq1, cur1) != read_at(seq2, cur2)) { return false; } - - inc(seq1, cur1); - inc(seq2, cur2); + auto ctx1 = iterate(it1); + auto ctx2 = iterate(it2); + + while (true) { + auto opt1 = next_element(ctx1); + auto opt2 = next_element(ctx2); + + if (opt1.has_value() && opt2.has_value()) { + if (*opt1 != *opt2) { + return false; + } + } else if (opt1.has_value() || opt2.has_value()) { + return false; + } else { + return true; + } } - - return is_last(seq1, cur1) == is_last(seq2, cur2); } public: From d42b5404fca3d29823e5c81a47c2105c122e3897 Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Thu, 15 May 2025 17:46:08 +0100 Subject: [PATCH 05/66] Add find_element tests --- include/flux/algorithm.hpp | 1 + include/flux/algorithm/find_element.hpp | 2 + test/CMakeLists.txt | 1 + test/test_find_element.cpp | 99 +++++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 test/test_find_element.cpp diff --git a/include/flux/algorithm.hpp b/include/flux/algorithm.hpp index 676d3830..9a507eb2 100644 --- a/include/flux/algorithm.hpp +++ b/include/flux/algorithm.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/include/flux/algorithm/find_element.hpp b/include/flux/algorithm/find_element.hpp index c9359d5f..331e7d86 100644 --- a/include/flux/algorithm/find_element.hpp +++ b/include/flux/algorithm/find_element.hpp @@ -13,6 +13,7 @@ FLUX_EXPORT struct find_element_if_t { template requires std::predicate> + [[nodiscard]] constexpr auto operator()(It&& it, Pred pred) const { iterable auto filtered = filter(std::ref(it), std::ref(pred)); @@ -27,6 +28,7 @@ FLUX_EXPORT struct find_element_t { template requires std::equality_comparable_with, Value const&> + [[nodiscard]] constexpr auto operator()(It&& it, Value const& value) const { return find_element_if(it, [&value](auto&& elem) { return FLUX_FWD(elem) == value; }); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8712d2da..3d1a6853 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -52,6 +52,7 @@ add_executable(test-flux test_filter.cpp test_filter_map.cpp test_find.cpp + test_find_element.cpp test_find_if.cpp test_find_if_not.cpp test_find_min_max.cpp diff --git a/test/test_find_element.cpp b/test/test_find_element.cpp new file mode 100644 index 00000000..0a4241e1 --- /dev/null +++ b/test/test_find_element.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2025 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include "test_utils.hpp" + +namespace { + +constexpr bool test_find_element_if() +{ + { + std::array arr{1, 2, 3, 4, 5}; + + auto elem = flux::find_element_if(arr, flux::pred::eq(3)); + + static_assert(std::same_as>); + + STATIC_CHECK(elem.has_value()); + STATIC_CHECK(*elem == 3); + STATIC_CHECK(&*elem == arr.data() + 2); + + *elem = 99; + STATIC_CHECK(check_equal(arr, {1, 2, 99, 4, 5})); + } + + { + std::array const arr{1, 2, 3, 4, 5}; + + auto elem = flux::find_element_if(arr, [](auto&&) { return false; }); + + static_assert(std::same_as>); + + STATIC_CHECK(not elem.has_value()); + } + + return true; +} +static_assert(test_find_element_if()); + +constexpr bool test_find_element() +{ + { + std::array arr{1, 2, 3, 4, 5}; + + auto elem = flux::find_element(arr, 3); + + static_assert(std::same_as>); + + STATIC_CHECK(elem.has_value()); + STATIC_CHECK(*elem == 3); + STATIC_CHECK(&*elem == arr.data() + 2); + + *elem = 99; + STATIC_CHECK(check_equal(arr, {1, 2, 99, 4, 5})); + } + + { + std::array const arr{1, 2, 3, 4, 5}; + + auto elem = flux::find_element(arr, 9999); + + static_assert(std::same_as>); + + STATIC_CHECK(not elem.has_value()); + } + + { + std::array arr{1, 2, 3, 4, 5}; + + auto copies = flux::map(arr, flux::copy); + + auto elem = flux::find_element(copies, 3); + + static_assert(std::same_as>); + + STATIC_CHECK(elem.has_value()); + STATIC_CHECK(*elem == 3); + + *elem = 999; + + STATIC_CHECK(check_equal(arr, {1, 2, 3, 4, 5})); + } + + return true; +} +static_assert(test_find_element()); + +} // namespace + +TEST_CASE("find_element") +{ + bool r = test_find_element_if(); + REQUIRE(r); + + r = test_find_element(); + REQUIRE(r); +} \ No newline at end of file From da005daafeff244d6c0cfc5f819b4550d74664bb Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Fri, 16 May 2025 17:49:20 +0100 Subject: [PATCH 06/66] Update ranges::to to use iterables ...and while we're at it, use the new std::from_range constructors in C++23 if we can, and the revised formulation for appending elements --- include/flux/adaptor/cartesian_base.hpp | 2 +- include/flux/algorithm/to.hpp | 175 +++++++++++++++--------- include/flux/core.hpp | 1 + include/flux/core/as_range.hpp | 90 ++++++++++++ include/flux/core/iterable_concepts.hpp | 1 + test/test_to.cpp | 8 +- 6 files changed, 205 insertions(+), 72 deletions(-) create mode 100644 include/flux/core/as_range.hpp diff --git a/include/flux/adaptor/cartesian_base.hpp b/include/flux/adaptor/cartesian_base.hpp index 21dea805..3791fa14 100644 --- a/include/flux/adaptor/cartesian_base.hpp +++ b/include/flux/adaptor/cartesian_base.hpp @@ -19,7 +19,7 @@ inline constexpr auto checked_pow = { T res{1}; for(U i{0}; i < exponent; i++) { - res = num::mul(res, base, loc); + res = num::checked_mul(res, base, loc); } return res; }; diff --git a/include/flux/algorithm/to.hpp b/include/flux/algorithm/to.hpp index 6f3d06c9..874f9830 100644 --- a/include/flux/algorithm/to.hpp +++ b/include/flux/algorithm/to.hpp @@ -10,51 +10,61 @@ #include #include +#if defined(__cpp_lib_ranges_to_container) && (__cpp_lib_ranges_to_container >= 202202L) +# define FLUX_HAVE_FROM_RANGE_CONSTRUCTORS +#endif + namespace flux { FLUX_EXPORT -struct from_sequence_t { - explicit from_sequence_t() = default; +struct from_iterable_t { + explicit from_iterable_t() = default; }; -FLUX_EXPORT inline constexpr auto from_sequence = from_sequence_t{}; +FLUX_EXPORT inline constexpr auto from_iterable = from_iterable_t{}; namespace detail { -template -concept direct_sequence_constructible = - std::constructible_from; +template +concept direct_iterable_constructible = std::constructible_from; + +template +concept from_iterable_constructible = std::constructible_from; -template -concept from_sequence_constructible = - std::constructible_from; +#ifdef FLUX_HAVE_FROM_RANGE_CONSTRUCTORS +template +concept from_range_constructible + = std::constructible_from())), + Args...>; +#else +template +concept from_range_constructible = false; +#endif template using container_value_t = typename C::value_type; // Let's just assume it exists -template -using common_iterator_t = - std::ranges::iterator_t; +template +using common_iterator_t + = std::ranges::iterator_t())))>; - -template -concept cpp17_range_constructible = - std::constructible_from, common_iterator_t, Args...>; +template +concept cpp17_range_constructible + = std::constructible_from, common_iterator_t, Args...>; template -concept container_insertable = - requires (C& c, Elem&& elem) { - requires (requires { c.push_back(FLUX_FWD(elem)); } || - requires { c.insert(c.end(), FLUX_FWD(elem)); }); - }; +concept container_appendable = requires(C& c, Elem&& elem) { + requires( + requires { c.emplace_back(FLUX_FWD(elem)); } || requires { c.push_back(FLUX_FWD(elem)); } + || requires { c.emplace(c.end(), FLUX_FWD(elem)); } + || requires { c.insert(c.end(), FLUX_FWD(elem)); }); +}; -template -concept container_convertible = - direct_sequence_constructible || - from_sequence_constructible || - cpp17_range_constructible || - ( std::constructible_from && - container_insertable>); +template +concept container_convertible + = direct_iterable_constructible || from_iterable_constructible + || from_range_constructible || cpp17_range_constructible + || (std::constructible_from && container_appendable>); template concept reservable_container = @@ -65,43 +75,69 @@ concept reservable_container = { c.capacity() } -> std::same_as>; }; -template -constexpr auto make_inserter(C& c) +template +constexpr auto container_appender(Container& c) { - if constexpr (requires { c.push_back(FLUX_DECLVAL(Elem)); }) { - return std::back_inserter(c); - } else { - return std::inserter(c, c.end()); - } + return [&c](auto&& elem) { + if constexpr (requires { c.emplace_back(FLUX_FWD(elem)); }) { + c.emplace_back(FLUX_FWD(elem)); + } else if constexpr (requires { c.push_back(FLUX_FWD(elem)); }) { + c.push_back(FLUX_FWD(elem)); + // } else if constexpr (requires { c.emplace(c.end(), FLUX_FWD(elem)); }) { + // c.emplace(c.end(), FLUX_FWD(elem)); + } else { + c.insert(c.end(), FLUX_FWD(elem)); + } + }; } template