I'm programming to interfaces and I want to interate over its contents in a generalized way. So, in general, my interfaces have prototypes like that:
class Interface
{
public:
class Iterator;
virtual Interface::Iterator* begin() = 0;
virtual Interface::Iterator* end() = 0;
class Iterator
{
public:
virtual const Iterator* operator++() = 0;
virtual bool operator!=(const Iterator& i) = 0;
};
};
and bellow, is an example of a simple specialization:
class Derived : public Interface
{
public:
Derived() : array {2, 0, 1, 5, 4, 3} {};
Iterator* begin() { return new Derived::IterDerived(0);};
Iterator* end() { return new Derived::IterDerived(6);};
int array[6];
public:
class IterDerived : public Interface::Iterator
{
public:
IterDerived(int i) {pos = i;};
IterDerived(IterDerived&& i) {pos = i.pos;};
const Interface::Iterator* operator++() override { ++pos; return this;};
bool operator!=(const Interface::Iterator& i) { return pos != dynamic_cast<const Derived::IterDerived&>(i).pos;};
int position() { return pos;};
private:
int pos;
};
};
So far, so good. Now, I want to write a piece of code that allow me to iterate over its contents using the new for range, as specified in new standard (c++11). Note that in real interface I will provide getter methods so, using dynamic_cast will be unnecessary. i.e:
int main()
{
Interface *a = new Derived();
for(auto i : a)
std::cout << dynamic_cast<Derived*>(a)->array[dynamic_cast<Derived::IterDerived*>(i.get())->position()] << " ";
std::cout << std::endl;
}
But this code does not compile. In order to compile, I need to use macro in the form of:
#define FOR_ITERATOR(iter, object) \
for(std::unique_ptr<Interface::Iterator> iter((object)->begin()), \
iter##end((object)->end()); \
*iter != *(iter##end); ++(*iter))
at the place of the for range. Any chances to use the new range for statements, or do I have to use this kind of macro?
When you say:
Interface *a = new Derived();
for(auto i : a)
a is a pointer, and it doesn't really make any sense to iterate over a pointer. You probably want:
for (auto i : *a)
that is, iterate over the object that a points at.
That probably still won't work, as your Interface::begin and Interface::end methods return an Interface::Iterator * instead of an Interface::Iterator, which is nonsense.
You appear to want to be able to use a subclass of Interface::Iterator to iterate over your derived class, but that won't work, as the auto needs to be able to figure out a static type for the i object (and associated iterator) it builds.
If you want to do something like this, you need to make your iterator object encapsulate a pointer to a class that can be subclassed by Derived while keeping the iterator class itself fixed. Something like:
class Interface
{
public:
class Iterator;
virtual Interface::Iterator begin() = 0;
virtual Interface::Iterator end() = 0;
protected:
class IteratorImpl {
public:
virtual IteratorImpl &operator++() = 0;
virtual bool operator!=(const Iterator& i) = 0;
virtual IteratorImpl *clone() = 0;
};
};
public:
class Iterator {
protected:
class IteratorImpl *impl;
public:
Iterator(IteratorImpl *a) : impl(a) {}
Iterator(iterator &a) : impl(a.impl->clone()) {}
Iterator &operator++() { ++*rep; return this; }
:
You then derive from this as
class Derived : Interface {
class IteratorImpl : public Interface::IteratorImpl {
:
};
public:
Iterator begin() { return Iterator(new IteratorImpl( ...
Of course, you need to make sure to add all the needed destructors, assignment operators, dereference operators etc.
This probably also needs to be templated over the type that is supposed to be contained in whatever it is that you are actually iterating over.
::Iterator is an abstract object and it needs to be. So, I cant return an concrete object.
Then you are now in conflict between virtual (runtime) polymorphism and generic (compile-time) polymorphism. Range-based for, and pretty much every algorithm in existence, uses concrete objects for iterators. They will be copied around with abandon by algorithms.
To make this work, you will need to have your iterators hide the virtual machinery within themselves. This can be as simple as having a basic Iterator class that stores a pointer to the "real" iterator class, which it forwards all calls to.
You would do something like this:
class IteratorBase
{
public:
virtual const IteratorBase* operator++() = 0;
virtual bool operator!=(const IteratorBase& i) = 0;
};
class Iterator
{
public:
explicit Iterator(IteratorBase *it) : m_it(it) {}
const Iterator &operator++() {return Iterator(++(*m_it));}
bool operator !=(const Iterator &i) {return *m_it != *i.m_it;}
private:
IteratorBase *m_it;
};
You derive your iterators from IteratorBase.
Generic programming-style interfaces tend to avoid pointers in general. They work more with references if not actual objects. So if you want your classes to work well with generic programming, you're going to have to follow allow with that convention. And, generally speaking, it is very odd to want to try to do something like have a runtime polymorphic range.
A pointer is not a range; only a reference to a concrete type can be a range. So you can't pass pointers to range-based for, unless you were to specialize std::begin and std::end.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With