dev
dev

Reputation: 19

C++ Filteration using lazy iterator

template<typename Iterator>
    struct Range {
        using LazyIterator = Iterator; // required for accessing the used Iterator type from other locations
        Iterator m_begin;
        Iterator m_end;
        auto begin() { return m_begin; }
        auto end() { return m_end; }
    };

    template<typename Iterator>
    Range(Iterator, Iterator) -> Range<Iterator>;    
 template<typename Iterator, typename Callable>
        struct FilteringIterator : Iterator {
            Callable callable;
            using OriginalIterator = Iterator;
            using t = typename OriginalIterator::value_type;
            FilteringIterator(const Iterator begin, Callable callable):Iterator(begin),callable(callable){}
            Iterator &get_orig_iter() { return ((Iterator &)*this); }
            auto operator*() { return callable(*get_orig_iter()); }
        };
auto filter = [](auto action) {
    return [=]( auto &container) {
        using Container = std::decay_t<decltype(container)>;
        using Iterator = typename Container::iterator;
        using actiontype = decltype(action);
        using filter_iterator = FilteringIterator<Iterator, actiontype>;
        return Range{filter_iterator{container.begin(),action}, filter_iterator{container.end(),action}};
    };
};

I need a lazy iterator that will traverse over a range and filter it lazily. For example

auto v = std::vector<double>{};
    auto odd_gen = views::odds();

    for(int i=0; i<5; ++i)
        v.push_back(odd_gen() * 2.5);
    // v contains {2.5, 7.5, 12.5, 17.5, 22.5} here

    new_line();
    for(auto a : v | filter(greater_than(15))) // filter is applied lazily as the range is traversed
        std::cout << a  << std::endl;
// print { 17.5, 22.5} here

but it prints

{0,0,0,0,17.5,22.5}

I want it to only print 17.5, 22.5

how can I achieve that?

Upvotes: 1

Views: 306

Answers (1)

G. Sliepen
G. Sliepen

Reputation: 7973

Your code never skips elements, it just transforms them. You need to make sure that when the operator*() is invoked, it starts by advancing the iterator while callable returns false, and only then return the value pointed to by the Iterator.

There are corner cases though you have to take care of. If the input ends with elements that don't match the filter, then you run into a problem. You also need to skip over all filtered out elements when operator++() is called. And also think about the case where the filter would not match any elements of the input; in that case you must ensure that begin() is equal to end().

Upvotes: 1

Related Questions