Open melton1968 opened 5 years ago
It may be helpful to look at the implementation of range-v3's chunk_view
, which splits a range into a range of subranges of a runtime-specified size.
So I have group_n
(produces a range of ranges with each subrange containing n
elements where n
is given by a range) working for a simple test:
Note 1. v::chr::alpha()
produces lowercase letters
Note 2. v::in_range(3, 7)
produces integers in the open range (3,7)
auto generator = v::chr::alpha() | v::group_n(v::in_range(3, 7)) | v::take(20);
for (auto values : generator)
{
for (auto value : values)
cout << value;
cout << Lendl;
}
produces:
icoheo
yzzzesz
ycmuuhd
kcx
...
A couple of questions:
Is there any interest, once I have this cleaned up and tested, in taking this as a contribution?
I am a little unsure about the following snippets of code from the implementation of outer_iterator (which follows very closely to split_view::outer_iterator with pattern replaced by length). Am I manipulating the iterators correctly in operator++?
using subrange_iter_t = counted_iterator<iterator_t<Base>>;
using value_type = subrange<subrange_iter_t, default_sentinel, subrange_kind::sized>;
constexpr value_type operator*() const
{
auto iter_n = counted_iterator(current(), *parent_->length_.begin());
auto rng = subrange{ iter_n, default_sentinel{} };
return rng;
}
constexpr outer_iterator& operator++() {
auto& cur = current();
const auto end = __stl2::end(parent_->base_);
index_t n = *parent_->length_.begin();
++(parent_->length_.begin());
while (++cur != end and n > 0)
--n;
return *this;
}
After this, I plan on implementing group
which will be analogous to group_n
but taking a predicate indicating where to break the groups rather than a count.
cheers, mark
This is my implementation of group_n
. It doesn't include any optimizations for forward_iterator, but otherwise is functional.
Example usage:
TEST(View, GroupNFixed)
{
strings expected = { "abcd", "efgh", "ijkl", "mnop", "qrst",
"uvwx", "yzab", "cdef", "ghij", "klmn" };
auto actual = v::iota(0)
| v::transform([](auto i) { return char((i % 26) + 'a'); })
| v::group_n(4)
| v::take(10)
| v::subrange_to<string>
| r::to<vector>;
EXPECT_EQ(actual, expected);
};
#pragma once
#include <stl2/type_traits.hpp>
#include <stl2/detail/fwd.hpp>
#include <stl2/detail/algorithm/mismatch.hpp>
#include <stl2/detail/concepts/object.hpp>
#include <stl2/detail/iterator/default_sentinel.hpp>
#include <stl2/detail/range/access.hpp>
#include <stl2/detail/range/concepts.hpp>
#include <stl2/detail/view/view_closure.hpp>
#include <stl2/view/all.hpp>
#include <stl2/view/single.hpp>
#include "core/common.h"
namespace std::experimental::ranges {
namespace detail::group_n {
template<class Range, class Count>
constexpr bool ViewReqs = View<Range>
&& View<Count>
&& Integral<iter_value_t<iterator_t<Count>>>;
template<InputRange Range, InputRange Count>
requires ViewReqs<Range, Count>
struct base
{
private:
using subrange_iter_t = counted_iterator<iterator_t<Range>>;
using value_type = subrange<subrange_iter_t, default_sentinel, subrange_kind::sized>;
sentinel_t<Range> end_range_ {};
iterator_t<Range> current_range_ {};
iterator_t<Range> current_group_ {};
iterator_t<Count> current_count_ {};
index_t actual_count_ {};
protected:
void reset_iterators(Range& range, Count& count)
{
end_range_ = __stl2::end(range);
current_range_ = __stl2::begin(range);
current_group_ = __stl2::begin(range);
current_count_ = __stl2::begin(count);
actual_count_ = 0;
}
auto end_range() const
{ return end_range_; }
auto current_group() const
{ return current_group_; }
value_type group_subrange() const
{
auto iter_n = counted_iterator{ current_group_, int(actual_count_) };
return subrange{ iter_n, default_sentinel{} };
}
void advance_group()
{
current_group_ = current_range_;
index_t max_count = *current_count_;
actual_count_ = 0;
while (current_range_ != end_range_ and max_count > 0)
{
++current_range_;
++actual_count_;
--max_count;
}
}
void advance_count()
{
auto iter = current_count_;
++iter;
}
};
}; // end ns detail::group_n
template<InputRange Range, InputRange Count> requires detail::group_n::ViewReqs<Range, Count>
struct group_n_view : public detail::group_n::base<Range, Count>
{
private:
using Base = detail::group_n::base<Range, Count>;
using Base::reset_iterators;
using group_value_type = typename Base::value_type;
struct group_iterator;
Range range_ {};
Count count_ {};
public:
group_n_view() = default;
constexpr group_n_view(Range range, Count count)
: range_(std::move(range))
, count_(std::move(count))
{}
constexpr group_n_view(Range range, Integral count)
requires Constructible<Count, single_view<int>>
: range_(std::move(range))
, count_(std::move(single_view{count}))
{}
constexpr auto begin()
{
reset_iterators(range_, count_);
return group_iterator{ *this };
}
constexpr auto end() const { return default_sentinel{}; }
};
template<class Range, class Count>
group_n_view(Range&&, Count&&) -> group_n_view<all_view<Range>, all_view<Count>>;
template<class Range, Integral Count>
group_n_view(Range&&, Count) -> group_n_view<all_view<Range>, single_view<Count>>;
template<InputRange Range, InputRange Count>
requires detail::group_n::ViewReqs<Range, Count>
struct group_n_view<Range, Count>::group_iterator
{
private:
using Parent = group_n_view;
Parent *parent_{ nullptr };
public:
using iterator_category = __stl2::input_iterator_tag;
using difference_type = iter_difference_t<iterator_t<Range>>;
using value_type = group_n_view::group_value_type;
group_iterator() = default;
constexpr explicit group_iterator(Parent& parent)
: parent_(std::addressof(parent))
{ parent_->advance_group(); }
constexpr auto operator*() const
{ return parent_->group_subrange(); }
constexpr group_iterator& operator++()
{
parent_->advance_group();
parent_->advance_count();
return *this;
}
constexpr decltype(auto) operator++(int)
{ ++*this; }
friend constexpr bool operator==(const group_iterator& x, default_sentinel)
{ return x.parent_->current_group() == x.parent_->end_range(); }
friend constexpr bool operator==(default_sentinel x, const group_iterator& y)
{ return y == x; }
friend constexpr bool operator!=(const group_iterator& x, default_sentinel y)
{ return !(x == y); }
friend constexpr bool operator!=(default_sentinel x, const group_iterator& y)
{ return !(y == x); }
};
namespace view {
struct __group_n_fn {
template<class E, class F>
constexpr auto operator()(E&& e, F&& f) const
STL2_REQUIRES_RETURN(group_n_view{static_cast<E&&>(e), static_cast<F&&>(f)});
template<CopyConstructible T>
constexpr auto operator()(T&& t) const
{ return detail::view_closure{*this, std::forward<T>(t)}; }
};
inline constexpr __group_n_fn group_n {};
}; // end ns view
}; // end ns stl2
I initially tried to get this functionality by combining off-the-shelf views, but I don't think that is possible.
So, I am trying to write a group_view that is analogous to split_view but takes a length rather than a pattern. After puzzling over the code for a long while, here is how I think I should proceed:
Change all the construction paths to take a Length rather than a Pattern and store the Length in the view . The Length will necessarily be an Integral type. If the length
Change
outer_iterator::operator++
to move forward Length units at a time.Change
outer_iterator::value_type
to be something like subrange. Or, can I use subrange directly here?Is there a better approach?
Any guidance is appreciated.
cheers, mark