'Can I somehow filter any_view<A const&, forward | sized> view?

Even for A being int, a naive approach for filtering fails,

#include <range/v3/view/any_view.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/transform.hpp>
#include <vector>

using namespace ranges;
using namespace ranges::views;

constexpr auto noop = [](int const& i) -> int const& { return (i); };

using View = any_view<int const&, category::forward | category::sized>;

template<std::size_t n>
View fun(View v) {
    if constexpr (n == 0)
        return v | transform(noop); // transforming is fine, the view stays the same size
    else
        return v | filter(noop); // lazy filtering doesn't work, as the view is no more sized
}

int main() {

    std::vector<int> va{1, 2, 3, 4, 5};

    View w{va};
    auto w2 = fun<0>(w); // succeeds
    //auto w3 = fun<1>(w); // fails at compile-time
}

let alone A beging a user defined class.

From the comments you see I understand the reason for the failure: v | filter(noop)'s size can be known only upon traversing the whole v, which is done in linear, not constant time, so it does not meet the requirements of the sized_range concept, hence it can't be used to construct a any_view<int const&, forward | sized>.

transforming, on the other hand, is possible, as it doesn't alter the size of the range.

But still, from within fun, I know that if the enclosing program is well written, the View v parameter has cog int const& references to all valid ints living in main, so I don't see why I can't do an eager filtering and construct a any_view from that.

However, I'm not sure how I can do it (and if).

Here's how my idea starts and breaks:

  • I filter, as that's the plan,
  • then, I take the address of the items, so I get things that can be copied around without loosing track of the original objects I'm working with
  • I convert to a hard vector to trigger the traversal of the filter view, thus obtaining a range of known size, but which contains pointers to original data, not copied of the original data
  • finally... what do I do? I have a std::vector<A const*> and I want to return a any_view<A const&, forward | sized>. How can I do it? indirect doesn't seem to be the answer, as you can see below:
#include <boost/hof/lift.hpp>
#include <iostream>
#include <functional>
#include <memory>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/any_view.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/indirect.hpp>
#include <range/v3/view/transform.hpp>
#include <vector>

using namespace ranges;
using namespace ranges::views;

using A = int; // so I can easily change it to a user defined class later

constexpr auto noop = [](A const& i) -> decltype(auto) { return (i); };

constexpr auto addressof = BOOST_HOF_LIFT(std::addressof); // so I can pass it around

using View = any_view<A const&, category::forward | category::sized>;

View fun(View v) {
    std::vector<A const*> w = v | filter(noop) | transform(addressof) | to_vector;
    return w | ranges::views::indirect;
}

int main() {

    std::vector<A> va(5);

    View w{va};
    auto w3 = fun(w); // succeeds
    std::cout << w3 << std::endl; // segmentation fault, so I'm playing with garbage
}

(any_view is not in C++20, so I'm tagging ; but I suspect the problem will exist in C++ whenever such a view will be added, so I'm also adding .)



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source