Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retain bidirectionality with Ranges-v3 view::join

I've run into some difficulty with the view::join function object of the (amazing) Ranges-v3 library. My client code is dependent upon the presence of a back method (and would greatly appreciate random access iterators) for an aggregate view of a collection of ranges.

After reviewing the relevant documentation it seems that a back method is compatible with instantations of the join_view class template, but I've been unable to instantiate it as such.

#include <iostream>
#include <vector>
#include <range/v3/all.hpp>

struct Foo{
    std::vector<int> i = {1,2,3,4};
    const std::vector<int>& data() const { return this->i; }
};

int main(){
    std::vector< Foo > foos = { Foo(), Foo(), Foo() };

    auto data = []( auto&& foo ){ return foo.data() | ranges::view::all; };
    auto flat = foos | ranges::view::transform(data) | ranges::view::join;
    std::cout << flat.back() << std::endl; // compiler error
}

The relevant bits of the compiler error message are:

main.cpp:17:28: error: no matching function for call to 'ranges::v3::join_view<ranges::v3::transform_view<ranges::v3::range<__gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> >, __gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> > >, main()::<lambda(auto:1&&)> >, void>::back()'

 std::cout << flat.back() << std::endl; // compiler error

/usr/local/include/range/v3/range_interface.hpp:116:34: note: candidate: template<class D, int _concept_requires_115, typename std::enable_if<((_concept_requires_115 == 43) || ((std::is_same<D, ranges::v3::join_view<ranges::v3::transform_view<ranges::v3::range<__gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> >, __gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> > >, main()::<lambda(auto:1&&)> >, void> >() && ranges::v3::concepts::models<ranges::v3::concepts::BoundedView, T>()) && ranges::v3::concepts::models<ranges::v3::concepts::BidirectionalView, T>())), int>::type <anonymous> > ranges::v3::range_reference_t<D> ranges::v3::range_interface<Derived, Inf>::back() [with D = D; int _concept_requires_115 = _concept_requires_115; typename std::enable_if<((_concept_requires_115 == 43) || ((std::is_same<D, Derived>() && ranges::v3::concepts::models<ranges::v3::concepts::BoundedView, D>()) && ranges::v3::concepts::models<ranges::v3::concepts::BidirectionalView, D>())), int>::type <anonymous> = <enumerator>; Derived = ranges::v3::join_view<ranges::v3::transform_view<ranges::v3::range<__gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> >, __gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> > >, main()::<lambda(auto:1&&)> >, void>; bool Inf = false]

         range_reference_t<D> back()

/usr/local/include/range/v3/range_interface.hpp:115:17: error: no type named 'type' in 'struct std::enable_if<false, int>'

                 CONCEPT_REQUIRES_(Same<D, Derived>() && BoundedView<D>() && BidirectionalView<D>())>

The first requirement seems to be enforcing proper use of the CRTP, which is satisfied. So the join_view violates either the BoundedView or BidirectionalView concepts (or both). I was able to quickly eliminate the former as a possiblity.

auto flat = foos 
  | ranges::view::transform(data) 
  | ranges::view::join 
  | ranges::view::bounded;
std::cout << flat.back() << std::endl; // compiler error

In this case, flat satisfies the BoundedView concept, but the error message remains unchanged.

To verify the BidirectionalView, I attempted examined the iterators of the join_view, but encountered (what is suspect) is a bug.

auto it = flat.begin();
std::cout << *it << std::endl; // correct
++it; std::cout << *it << std::endl; // correct
--it; std::cout << *it << std::endl; // doesn't actually decrement
auto other = --it;
std::cout << *it << ' ' << *other << std::endl; // also doesn't decrement

I've written up a live version for easy inspection.

Has anyone had any luck instantiating a bidirectional join_view? Any suggestion of how I might achieve similar behavior without copying the underlying data?

like image 814
apmccartney Avatar asked Mar 03 '26 22:03

apmccartney


1 Answers

Range-v3's join view satisfies InputRange, but not Forward or anything stronger. It has to do with how the join is accomplished. While iterating over an inner range, the range needs to be stored somewhere. That somewhere is in a member of the join_view object. In other words, join_view is mutated while you iterate over it. For that reason, it cannot model any range category stronger than Input.

like image 154
Eric Niebler Avatar answered Mar 05 '26 17:03

Eric Niebler