Search code examples
c++boostiteratorreverse-iteratorboost-type-erasure

Use reverse_iterator with type erasure


I have a class which contains and manages a series of objects. To avoid leaking how these objects are stored while allowing to iterate through them, I decided to use type erasure using boost::any_iterator.

 using my_erased_type_iterator = boost::range_detail::any_iterator<
    MyClass,
    boost::bidirectional_traversal_tag,
    MyClass&, 
    std::ptrdiff_t>;

I defined a function Begin() and End() in MyClass, which simply returns the container's begin() and end() function as a my_erased_type_iterator. It works exactly as I want to, and nobody outside of MyClass knows I'm using a vector to store the objects nor do they have access to the container aside from the functions I expose in Myclass's interface.

Now, for a number of reasons, I need to reverse-iterate through the objects. I also need to know the next element after a reverse iterator (akin to calling std::next() on a normal iterator, which is already not so trivial for reverse iterators) and I might also need to call functions like erase() on that reverse iterator.

So for my question: is there an elegant way to use type-erasure along with reverse iterators (and the const version of both forward and reverse) ? Should I use forward type-erased iterators and iterate backward instead? It crossed my mind that I might be tackling this problem the wrong way, so I'm open to any suggestions or to clarify my questions if needed.


Solution

  • Note that any_iterator is an implementation detail.

    I'll answer your direct question first, then show the approach with any_range<> as intended by the public API of Boost Range.

    1. make_reverse_iterator

    You can simply use the make_reverse_iterator facility from

    Live On Coliru

    #include <boost/range.hpp>
    #include <boost/range/any_range.hpp>
    
    struct MyClass {
        int i;
    };
    
    using my_erased_type_iterator = boost::range_detail::any_iterator<
        MyClass,
        boost::bidirectional_traversal_tag,
        MyClass&, 
        std::ptrdiff_t>;
    
    #include <iostream>
    #include <vector>
    
    int main() {
        using namespace boost;
        std::vector<MyClass> const v { {1}, {2}, {3}, {4} };
    
        for (auto& mc : make_iterator_range(
                    make_reverse_iterator(v.end()),
                    make_reverse_iterator(v.begin())))
        {
            std::cout << mc.i << " ";
        }
    }
    

    Prints

    4 3 2 1 
    

    2. reversed range adaptor:

    Alternatively, you can go full range-style and use any_range<>:

    Live On Coliru

    int main() {
        std::vector<MyClass> const v { {1}, {2}, {3}, {4} };
    
        boost::any_range_type_generator<decltype(v)>::type x = reverse(v);
    
        for (my_erased_type_const_iterator f = boost::begin(x), l = boost::end(x); f!=l; ++f) {
            std::cout << f->i << " ";
        }
    
    }