Use reverse_iterator with type erasure

327 Views Asked by At

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.

2

There are 2 best solutions below

7
sehe On BEST ANSWER

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 << " ";
    }

}
3
Yakk - Adam Nevraumont On

Simply reverse the type erased iterators.

This exposes .base(), which means erasing is almost as easy as erasing the type erased forward ones.

As an aside, your design has performance costs for (in my experience) marginal benefit. The iterafor invalidadion rules of the underlying container still apply, so the user of your class has to know what the underlying container is anyhow! (Or, know so much they might as well). Swappimng out the container will not provide sufficiently similar behaviour, so your container is locked in despite your reasonably costly attempt to hide it.