So basically what I want to do is to have a pure virtual method returning an iterator to an arbitrary collection of a concrete type, e.g in pseudo code:
virtual Iterator<T> getIterator() const = 0;
The user of this class actually don't care what implementation the child class uses. It could be a set, vector, list, array etc.
I'm aware of the std::iterator class but I cant find a way to specify it correctly in order to work with a simple vector.
virtual std::iterator<std::random_access_iterator_tag,T> getIterator() const = 0;
myVector.begin() // compilation error in implementation
defining std::iterator with const T as type parameter hasn't worked too. I also tried leaving T and instead defining the pointer and reference types as const T* and const T&.
By taking a look at the std::vector implementation, I found out that std::vector::const_iterator actually derives from _Iterator012 deriving from _Iterator_base.
It really bugs me that there isn't any way to work with arbitrary collections in std.
Implementing my classes as templates like in <algorithm> is not an option for me due two reasons:
No control over the actual value type
I simply don't want to make my classes templates complicating my design a lot and making things less flexible.
The used type parameter T was just for demonstration, actually this is a concrete type.
Here's a basic and very rudimentary skeleton approach using type erasure. You'll have to fill in a lot of missing details, though!
#include <memory>
template <typename T>
class TEIterator
{
struct TEImplBase
{
virtual ~TEImplBase() { }
virtual std::unique_ptr<TEImplBase> clone() const = 0;
virtual void increment() = 0;
virtual T & getValue() = 0;
T * getPointer() { return std::addressof(getValue()); }
};
template <typename Iter>
struct TEImpl
{
Iter iter;
TEImpl(Iter i) : iter(i) { }
virtual T & getValue()
{ return *iter; }
virtual std::unique_ptr<TEImplBase> clone() const
{ return std::unique_ptr<TEImplBase>(new TEImpl<Iter>(*this)); }
virtual void increment()
{ ++iter; }
};
std::unique_ptr<TEImplBase> impl;
public:
template <typename T>
TEClass(T && x)
: impl(new TEImpl<typename std::decay<T>::type>(std::forward<T>(x)))
{
}
TEClass(TEClass && rhs) = default;
TEClass(TEClass const & rhs) : impl(rhs.impl.clone()) { }
TEIterator & operator++()
{
impl->increment();
return *this;
}
T & operator*() { return impl->getValue(); }
T * operator->() { return impl->getPointer(); }
};
Usage:
std::vector<int> v;
std::deque<int> dq;
TEIterator<int> a = v.begin(), b = dq.end();
If you want to use a virtual method, you cannot use an arbitrary return value. What you can do, is define a base class, which is a wrapper around iterators, and subclass from that wrapper class.
But even then, you must restrict yourself to the smallest common denominator, since there are several iterator classes in the C++ standard library.
So, AFAICS, such a method with arbitrary iterators isn't really feasible without using templates.
Related
What I want to achieve is probably easily explained: Consider I have an abstract class that I know will contain multiple objects of known type. However the actual container holding these objects will be implemented in sub-classes.
In my abstract base class I now want to provide an interface to iterate over these objects. Given that I don't know (or rather don't want to fix) the type of container, I thought that iterators would probably be my best bet.
A conceptual declaration of this class might look like this:
class MyClass {
public:
// Other interface methods, e.g. size()
virtual Iterable<MyObject> objects() = 0;
};
The intention here is that I'll be able to iterate over the nested objects of my class like this:
MyClass *class = new ImplementationOfClass();
for (const MyObject &obj : class->objects()) {
// Do stuff with obj
}
The issue I am facing however is that I can't seem to figure out how Iterable<MyObject> should be defined. The key property of this object is that at the time of defining this class I can only specify that the returned value will be iterable (using STL-style iterators) and will yield objects of type MyObject when the used iterator is dereferenced.
Normally I would use an abstract class on its own for this but it seems that this is very tricky (impossible?) since iterators are always passed by value and thus to my knowledge no Polymorphism is possible.
Questions dealing with how to pass arbitrary iterator types as arguments into a function always come up with the "use templates" answer. However I think in my case I can't use templates for that. This assumption might be wrong though, so feel free to correct me.
Essentially the barrier I always run into is that at some point I have to write down the iterator type explicitly which in my case I can't. I thought about using a template for that but this would then inhibit proper Polymorphism (I think?) because the user of that abstract interface seems to have the burden of explicitly initializing the correct template. The whole point of all of this however is that the caller does not have to care about the underlying structure.
TL;DR: Is there a way to create an interface class that only promises to be iterable and that dereferencing an iterator will yield an object of type T?
With the help of #FrançoisAndrieux and a hint from https://stackoverflow.com/a/4247445/3907364, I was able to come up with an approach to my problem.
Essentially the idea is to create an iterator-wrapper that stores a function to obtain an object of the given type if given an index. That index is then what is iterated on.
The nice thing about this is that the iterator interface is fixed by specifying the type of object that dereferencing it should return. The polymorphism comes into play by making the member function objects() virtual so that each sub-class can construct the iterator itself, providing a custom function pointer. Thus as long as there is a way to map an index to the respective element in the container (whichever is used), this trick is usable.
Note that you can either directly use pointers to e.g.std::vector<T>::at or create a custom function that will return the respective element.
Here's the implementation for the iterator (The implementation could probably be improved upon but it seems to get the job done):
template< typename T > struct iterator_impl {
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T *;
using reference = T &;
using access_function_t = std::function< T &(std::size_t) >;
// regular Ctor
iterator_impl(std::size_t start, access_function_t &func, const void *id)
: m_index(start), m_func(func), m_id(id) {}
// function-move Ctor
iterator_impl(std::size_t start, access_function_t &&func, const void *id)
: m_index(start), m_func(func), m_id(id) {}
// copy Ctor
iterator_impl(const iterator_impl &) = default;
// move ctor
iterator_impl(iterator_impl &&other) {
std::swap(m_index, other.m_index);
m_func = std::move(other.m_func);
std::swap(m_id, other.m_id);
}
// copy-assignment
iterator_impl &operator=(const iterator_impl &other) = default;
// prefix-increment
iterator_impl &operator++() {
++m_index;
return *this;
}
// postfix-increment
iterator_impl operator++(int) {
iterator_impl old = *this;
++(*this);
return old;
}
bool operator==(const iterator_impl &other) { return m_index == other.m_index && m_id == other.m_id; }
bool operator!=(const iterator_impl &other) { return !(*this == other); }
T &operator*() { return m_func(m_index); }
T *operator->() { return &m_func(m_index); };
protected:
std::size_t m_index = 0;
access_function_t m_func;
const void *m_id = nullptr;
};
Note that I had to introduce the m_id member variable as a means to properly compare iterators (std::function can't be compared using ==). it is meant to be e.g. the address of the container the elements are contained in. Its sole purpose is to make sure that 2 iterators that happen to have the same index but are iterating over completely different sets are not considered equal.
And based on that here's an implementation of an Iterable<T>:
template< typename T > struct Iterable {
using iterator = iterator_impl< T >;
using const_iterator = iterator_impl< const std::remove_const_t< T > >;
Iterable(std::size_t start, std::size_t end, typename iterator_impl< T >::access_function_t &func, const void *id)
: m_begin(start, func, id), m_end(end, func, id) {}
iterator begin() { return m_begin; }
iterator end() { return m_end; }
const_iterator begin() const { return m_begin; }
const_iterator end() const { return m_end; }
const_iterator cbegin() const { return m_begin; }
const_iterator cend() const { return m_end; }
protected:
iterator m_begin;
iterator m_end;
};
Lets say I have an interface. Interface has begin and end functions because derived classes have to implement for-range functionality. The user will only use interface and will not know about the implementation of derived classes.
I cannot use the same iterator for all derived classes (more specifically, operator++() is different) so I have to make an abstract base iterator class.
class BaseIterator
{
//...
public:
virtual Type operator*()
{
//Implementation
}
virtual bool operator!=(const BaseIterator&)
{
//Implementation
}
virtual BaseIterator& operator++() = 0;
}
//Interface
struct Interface
{
//other pure virtual functions
virtual BaseIterator& begin() = 0;
virtual BaseIterator& end() = 0;
}
In concrete classes who inherit the interface I am using, lets say one of them is class A, each has its own iterator which inherits from BaseIterator, and uses it to implement begin and end functions.
class A : public Interface
{
//...
class AIterator : public BaseIterator
{
AIterator& operator++()
{
//...
}
}
public:
AIterator& begin()
{
//...
}
AIterator& end()
{
//...
}
}
Similarly for other derived classes. The problem occurs when I try to use the for range loop with polymorphic types. For example (*)
Interface* c = Interface::makeA(); //assume for simplicity that there is static function in "Interface"
for(auto el : *c)
{
//do something with el
}
I get an error that I cannot instantiate the abstract class because of the pure virtual function operator++(). The reason I think this happens is in the implementation of for-range loop which goes something equivalently to the following:
auto && __range = range_expression ;
for (auto __begin = __range.begin(), __end = __range.end(); __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement;
}
I believe the problem lies in the "auto__begin == __range.begin()". begin returns reference to the BaseIterator, which because of the auto type deduction gets removed which in the end makes __begin type of BaseIterator and that is abstract class and cannot be instantiated.
I know that this kind of behaviour can be implemented in Java.
Am I missing something here? If not, how would you implement this but keeping the functionality in (*)?
Please post a full example showing the problem in the future. I guess that the error you are facing is something like (https://wandbox.org/permlink/PKop4WMbpuygHoes)
prog.cc:73:19: error: cannot allocate an object of abstract type 'BaseIterator'
73 | for(auto&& i: a) {
| ^
prog.cc:3:7: note: because the following virtual functions are pure within 'BaseIterator':
3 | class BaseIterator
| ^~~~~~~~~~~~
prog.cc:19:27: note: 'virtual BaseIterator& BaseIterator::operator++()'
19 | virtual BaseIterator& operator++() = 0;
| ^~~~~~~~
prog.cc:73:19: error: cannot declare variable '__for_begin ' to be of abstract type 'BaseIterator'
73 | for(auto&& i: a) {
| ^
prog.cc:73:19: error: cannot allocate an object of abstract type 'BaseIterator'
prog.cc:73:19: error: cannot declare variable '__for_end ' to be of abstract type 'BaseIterator'
The problem is obvious because the for loop tries to assign the return value of begin() to a local variables which it can't.
C++ isn't Java. The ranged-based for loop either calls begin/end member functions or calls free functions, so there is no need for an interface to get the iterator. Your class only has to implement these functions and the range-based for will work. Besides that, having operator!= in the base class makes it possible to compare two iterators of different sub-types. To check this you need to use dynamic_castto check the types. You also need to down-cast the argument in the implementation which is obviously not a good design.
Inheritance is not so prominent for these kinds of problems in C++ and generic code using templates is preferred, e.g. in the STL. All the algorithms will work with a compatible iterator without inheritance because the iterator type is a template parameter.
Here is an example using a simple Range class as container to iterator over:
#include
struct Range;
struct Iterator {
Range const* r;
int current;
Iterator(Range const* x, int c) : r(x), current(c) {}
Iterator& operator++() {
++current;
return *this;
}
int const& operator*() const {
return current;
}
};
bool operator==(Iterator const& x, Iterator const& y) {
return x.current == y.current;
}
bool operator!=(Iterator const& x, Iterator const& y) {
return !(x == y);
}
struct Range {
int min;
int max;
Iterator begin() const {
return Iterator(this, min);
}
Iterator end() const {
return Iterator(this, max+1);
}
};
int main() {
Range r{-5, 5};
for(auto&& i: r) {
std::cout << i << std::endl;
}
}
This is a simplified and incomplete example only to show how to make it work with the range-based for loop. If you want to implement iterators that are conforming to the iterators in the standard library you need to add a bunch of member functions and typedefs. There should be plenty of information of how to write these iterators.
PS: If you decide to use the Java-like approach with interfaces don't forget to give your interfaces a (public or protected) virtual destructor and declare copy constructors as delete to prevent object slicing.
Consider this kind of problem. I have a Base class and three classes derived from Base. For instance: DerivedA, DerivedB and DerivedC. Each derived class has its unique container. Hence DerivedA has std::vector<int>, DerivedB has std::set<int> and DerivedC has std::map<int, std::string>. And I want an interface in Base to access the container of derived class on which it is currently pointed to.
Base* d1 = new DerivedA;
for(std::vector<int>::iterator iter = d1->begin(); iter != d1->end(); ++iter)
{
//processing
}
I tried to wrap each container to separate class and keep a pointer of their base
in the Base class.
class CollA;
template<class T>
class traits;
template<>
class traits<CollA>
{
public:
typedef vector<int> container;
};
template<class T>
class Coll
{
public:
typedef typename traits<T>::container container;
typename container::iterator begin() const
{
}
};
class CollA : public Coll<CollA>
{
typedef traits<CollA>::container container;
public:
container::iterator begin()
{
return V.begin();
}
private:
vector<int> V;
};
class Base
{
public:
Base()
{
}
// what to do here? I must keep a pointer to Coll; But Coll itself is a template
};
Suggest me something. I am kind of lost in this horrible design.
In order to do what you want, you need to define a common type of iterator that can be returned from the different begin() and end() overrides in the derived classes.
Before that, of course, you need to decide what exactly you want that iterator to do, as Yakk explained in his comment. For starters, you need to decide what value_type will result from indirecting through such an iterator. The only common type that I can think of given your three different containers is const int, as keys in std::maps are const and std::set iterators are const iterators (since the elements are keys themselves). So, when iterating using the common iterator type, you'll only be able to observe the ints in there.
Now, the iterator implementation will need to call different code (at runtime) depending on the derived class from which it originated. This is a typical use case for type erasure. When done properly, this would allow you to wrap any kind of iterator, as long as it supports the interface you need. In your case however, you may not need to go that far, since I suppose you know the full set of containers you need to support, so the set of iterator types is well known and bounded as well.
This means you can use a boost::variant to store the wrapped iterator. This should be more efficient than a full type erasure solution, since it avoids some internal virtual function calls and possibly some heap allocations (unless the type erasure solution can use some kind of small object optimization, which is fairly possible for iterators, but is even more complicated to implement).
Here's a skeleton implementation of such an iterator, together with the class hierarchy using it and some simple test code. Note that I've only implemented the basic iterator functionality that's needed to make your loop work.
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <iterator>
#include "boost/variant.hpp"
//Helper function object types to implement each operator on the variant iterator.
struct indirection_visitor : boost::static_visitor<const int&>
{
const int& operator()(std::vector<int>::iterator i) const { return *i; }
const int& operator()(std::set<int>::iterator i) const { return *i; }
const int& operator()(std::map<int, std::string>::iterator i) const { return i->first; }
};
struct prefix_increment_visitor : boost::static_visitor<>
{
template<typename I> void operator()(I& i) const { ++i; }
};
//The iterator itself.
//It should probably hide the internal variant, in which case the non-member operators
//should be declared as friends.
struct var_iterator : std::iterator<std::bidirectional_iterator_tag, const int>
{
var_iterator() { }
template<typename I> var_iterator(I i) : it(i) { }
boost::variant<std::vector<int>::iterator, std::set<int>::iterator, std::map<int, std::string>::iterator> it;
const int& operator*() { return boost::apply_visitor(indirection_visitor(), it); }
var_iterator& operator++()
{
boost::apply_visitor(prefix_increment_visitor(), it);
return *this;
}
};
inline bool operator==(var_iterator i1, var_iterator i2) { return i1.it == i2.it; }
inline bool operator!=(var_iterator i1, var_iterator i2) { return !(i1 == i2); }
//Here's the class hierarchy.
//We use CRTP only to avoid copying and pasting the begin() and end() overrides for each derived class.
struct Base
{
virtual var_iterator begin() = 0;
virtual var_iterator end() = 0;
};
template<typename D> struct Base_container : Base
{
var_iterator begin() override { return static_cast<D*>(this)->container.begin(); }
var_iterator end() override { return static_cast<D*>(this)->container.end(); }
};
struct DerivedA : Base_container<DerivedA>
{
std::vector<int> container;
};
struct DerivedB : Base_container<DerivedB>
{
std::set<int> container;
};
struct DerivedC : Base_container<DerivedC>
{
std::map<int, std::string> container;
};
//Quick test.
void f(Base* bp)
{
for(auto iter = bp->begin(); iter != bp->end(); ++iter)
{
std::cout << *iter << ' ';
}
std::cout << '\n';
//We have enough to make range-based for work too.
for(auto i : *bp)
std::cout << i << ' ';
std::cout << '\n';
}
int main()
{
DerivedA da;
da.container = {1, 2, 3};
f(&da);
DerivedB db;
db.container = {4, 5, 6};
f(&db);
DerivedC dc;
dc.container = std::map<int, std::string>{{7, "seven"}, {8, "eight"}, {9, "nine"}};
f(&dc);
}
Implementation notes:
As mentioned above, this is not a complete bidirectional iterator; I chose that tag as the most powerful common iterator among your container types.
I compiled and (superficially) tested the code in Clang 3.6.0 and GCC 5.1.0 in C++11 mode, and in Visual C++ 2013, using boost 1.58.0.
The code works in C++14 mode as well in the compilers above (and also in Visual C++ 2015 CTP6), but needs a small change because of a bug in boost 1.58 (I'll have to report that), otherwise you'll get an ambiguity error. You need to remove the base class of indirection_visitor and let the return type of this visitor be determined automatically. This only works in C++14, as it uses decltype(auto) internally, and it's this new code that causes the ambiguity. Earlier versions of boost don't have this problem, but don't have autodetection of return types either.
In C++14 mode and boost 1.58, you can use generic lambdas to implement simple visitors like prefix_increment_visitor, which makes the code more straightforward.
I removed the comparison visitors from my first version of the code, as boost::variant already provides a default equality operator and it's enough for this case (the example is long enough as it is).
You can add const in the required places to get true const iterator behaviour if needed (qualify begin() and end(), use static_cast<const D*> in CRTP, declare the variant to contain const_iterators, adjust the visitor).
You can, of course, implement some sort of poor-man's variant and avoid using boost, but boost::variant makes everything much easier, cleaner and safer.
I'd like to design a class Foo that stores various data of different types and returns iterators over them. It's supposed to be generic, so the user of Foo does not know how the data is stored (Foo could be using std::set or std::vector or whatever).
I'm tempted to write an interface like this:
class Foo {
class FooImpl;
FooImpl* impl_;
public:
const Iterator<std::string>& GetStrings() const;
const Iterator<int>& GetInts() const;
};
where Iterator is something like this (like iterators in .NET):
template<class T>
class Iterator {
public:
const T& Value() const = 0;
bool Done() const = 0;
void Next() = 0;
};
But I know that kind of iterator is not standard in C++, and it's better to use iterators the way the STL does, so you can use the STL algorithms on them.
How can I do that? (Do I need iterator_traits by any chance?)
Do you understand why the STL chose to put iterator implementation details in the header file? JIT frameworks are able to inline across compilation units, but C++ can only inline within a compilation unit. Advancing through a sequence is much faster when inlined, the cost of a function call dominates actually traversing the data structure.
If you really want to hide the implementation details, go ahead. You could make an STL-compatible iterator that implements operator++ and operator!= and operator-> in terms of protected virtual functions, the Next, Done, and Value you've mentioned would be decent names. Just expect to pay for the encapsulation with lower performance.
A c++ class with iterators has to provide at least two functions if they have to work with the std library
iterator begin() //returns an iterator at starting pos
iterator end() //returns an iterator one past end or just invald
The iterator has to overload the increment operators, equals and *
iterator operator++()
iterator operator==()//make sure that an invalid iterator equals end()
T& operator*()
You can use the iterator class to wrap around the iterator of the internal storage to ensure that the user is limited to these methods.
template <typename T> iter
{
iter(T::iterator& intern)
T::value_type& operator*(){return *intern}
iter operator++(){return iter(++intern);}
bool operator==(iter const& other)const{return intern == other.intern;}
}
Where T is the type of your container.(The class is incomplete and I may have mixed something up)
It almost looks like you're trying to create container-independent code, which is not (in general) a good idea, unless you are writing an algorithm which can operate solely with iterators. (See Scott Myers Effective STL Item 2: Beware the illusion of container-independent code)
The problem is that most of the standard containers do not provide overlapping functionality. If you're writing code for a particular container, assume you're writing code for that container. Don't bother trying to make it container-independent.
Use a typedef to return an boost::iterator_range. For example (never mind the names),
class Container
{
typedef std::vector<int> Collection;
public:
typedef boost::iterator_range<Collection::iterator> CollectionRange;
typedef Collection::iterator CollectionIterator;
Range range() const {
return make_iterator_range(collection_.begin(), collection_.end());
}
private:
Collection collection_;
};
The user code will be
Container c;
// ...
FOREACH(int i, c.range()) { //... }
Container::Range r = c.range();
for(Container::iterator j = r.begin(); j!= r.end(); j++) { // ... }
This is not generic, but the same idea can be used with templates.
To fulfill the requirement that the particular container (vector, set, ...) is not mentioned in the header file, and the user will be able to iterate over all strings, is to use the visitor pattern. The downside, of course, is that the user won't be able to use the STL algorithms on the strings.
// foo.h
class StringVisitor {
public:
void accept(const std::string& str) {
std::cout << str << std::endl;
}
};
class Foo {
class Impl;
Impl* impl_;
public:
Foo();
~Foo();
void VisitStrings(StringVisitor v) const;
};
// foo.cc
class Foo::Impl {
typedef std::vector<std::string> StringContainer;
StringContainer str_;
public:
Impl() {
str_.push_back("a");
str_.push_back("b");
}
void VisitStrings(StringVisitor v) const {
for(StringContainer::const_iterator it = str_.begin();
it != str_.end(); ++it){
v.accept(*it);
}
}
};
Foo::Foo() : impl_(new Impl()) {}
Foo::~Foo() {delete impl_;}
void Foo::VisitStrings(StringVisitor v) const {
impl_->VisitStrings(v);
}
// main.cc
int main() {
Foo foo;
foo.VisitStrings(StringVisitor());
return 0;
}
Having toyed with this I suspect it isn't remotely possible, but I thought I'd ask the experts. I have the following C++ code:
class IInterface
{
virtual void SomeMethod() = 0;
};
class Object
{
IInterface* GetInterface() { ... }
};
class Container
{
private:
struct Item
{
Object* pObject;
[... other members ...]
};
std::list<Item> m_items;
};
I want to add these methods to Container:
MagicIterator<IInterface*> Begin();
MagicIterator<IInterface*> End();
In order that callers can write:
Container c = [...]
for (MagicIterator<IInterface*> i = c.Begin(); i != c.End(); i++)
{
IInterface* pItf = *i;
[...]
}
So essentially I want to provide a class which appears to be iterating over some collection (which the caller of Begin() and End() is not allowed to see) of IInterface pointers, but which is actually iterating over a collection of pointers to other objects (private to the Container class) which can be converted into IInterface pointers.
A few key points:
MagicIterator is to be defined outside Container.
Container::Item must remain private.
MagicIterator has to iterate over IInterface pointers, despite the fact that Container holds a std::list<Container::Item>. Container::Item contains an Object*, and Object can be used to fetch IInterface*.
MagicIterator has to be reusable with several classes which resemble Container, but might internally have different list implementations holding different objects (std::vector<SomeOtherItem>, mylist<YetAnotherItem>) and with IInterface* obtained in a different manner each time.
MagicIterator should not contain container-specific code, though it may delegate to classes which do, provided such delegation is not hard coded to to particular containers inside MagicIterator (so is somehow resolved automatically by the compiler, for example).
The solution must compile under Visual C++ without use of other libraries (such as boost) which would require a license agreement from their authors.
Also, iteration may not allocate any heap memory (so no new() or malloc() at any stage), and no memcpy().
Thanks for your time, even if you're just reading; this one's really been bugging me!
Update: Whilst I've had some very interesting answers, none have met all the above requirements yet. Notably the tricky areas are i) decoupling MagicIterator from Container somehow (default template arguments don't cut it), and ii) avoiding heap allocation; but I'm really after a solution which covers all of the above bullets.
I think you have two separate issues here:
First, create an iterator that will return the IInterface* from your list<Container::Item>. This is easily done with boost::iterator_adaptor:
class cont_iter
: public boost::iterator_adaptor<
cont_iter // Derived
, std::list<Container::Item>::iterator // Base
, IInterface* // Value
, boost::forward_traversal_tag // CategoryOrTraversal
, IInterface* // Reference :)
>
{
public:
cont_iter()
: cont_iter::iterator_adaptor_() {}
explicit cont_iter(const cont_iter::iterator_adaptor_::base_type& p)
: cont_iter::iterator_adaptor_(p) {}
private:
friend class boost::iterator_core_access;
IInterface* dereference() { return this->base()->pObject->GetInterface(); }
};
You would create this type as inner in Container and return in from its begin() and end() methods.
Second, you want the runtime-polymorphic MagicIterator. This is exactly what any_iterator does. the MagicIterator<IInterface*> is just any_iterator<IInterface*, boost::forward_traversal_tag, IInterface*>, and cont_iter can be just assigned to it.
Create an abstract IteratorImplementation class:
template<typename T>
class IteratorImplementation
{
public:
virtual ~IteratorImplementation() = 0;
virtual T &operator*() = 0;
virtual const T &operator*() const = 0;
virtual Iterator<T> &operator++() = 0;
virtual Iterator<T> &operator--() = 0;
};
And an Iterator class to wrap around it:
template<typename T>
class Iterator
{
public:
Iterator(IteratorImplementation<T> * = 0);
~Iterator();
T &operator*();
const T &operator*() const;
Iterator<T> &operator++();
Iterator<T> &operator--();
private:
IteratorImplementation<T> *i;
}
Iterator::Iterator(IteratorImplementation<T> *impl) :
i(impl)
{
}
Iterator::~Iterator()
{
delete i;
}
T &Iterator::operator*()
{
if(!impl)
{
// Throw exception if you please.
return;
}
return (*impl)();
}
// etc.
(You can make IteratorImplementation a class "inside" of Iterator to keep things tidy.)
In your Container class, return an instance of Iterator with a custom subclass of IteratorImplementation in the ctor:
class ObjectContainer
{
public:
void insert(Object *o);
// ...
Iterator<Object *> begin();
Iterator<Object *> end();
private:
class CustomIteratorImplementation :
public IteratorImplementation<Object *>
{
public:
// Re-implement stuff here.
}
};
Iterator<Object *> ObjectContainer::begin()
{
CustomIteratorImplementation *impl = new CustomIteratorImplementation(); // Wish we had C++0x's "var" here. ;P
return Iterator<Object *>(impl);
}
Doesn't sound too complicated. You can define the iterator outside. You can also use typedefs. Something like this would fit i think. Note that it would be way cleaner if that MagicIterator would be not a free template, but a member of Item, typedefed in Container maybe. As it's now, there is a cyclic reference in it, which make it necassary to write some ugly workaround code.
namespace detail {
template<typename T, typename U>
struct constify;
template<typename T, typename U>
struct constify<T*, U*> {
typedef T * type;
};
template<typename T, typename U>
struct constify<T*, U const*> {
typedef T const * type;
};
}
template<typename DstType,
typename Container,
typename InputIterator>
struct MagicIterator;
class Container
{
private:
struct Item
{
Object* pObject;
};
std::list<Item> m_items;
public:
// required by every Container for the iterator
typedef std::list<Item> iterator;
typedef std::list<Item> const_iterator;
// convenience declarations
typedef MagicIterator< IInterface*, Container, iterator >
item_iterator;
typedef MagicIterator< IInterface*, Container, const_iterator >
const_item_iterator;
item_iterator Begin();
item_iterator End();
};
template<typename DstType,
typename Container = Container,
typename InputIterator = typename Container::iterator>
struct MagicIterator :
// pick either const T or T, depending on whether it's a const_iterator.
std::iterator<std::input_iterator_tag,
typename detail::constify<
DstType,
typename InputIterator::value_type*>::type> {
typedef std::iterator<std::input_iterator_tag,
typename detail::constify<
DstType,
typename InputIterator::value_type*>::type> base;
MagicIterator():wrapped() { }
explicit MagicIterator(InputIterator const& it):wrapped(it) { }
MagicIterator(MagicIterator const& that):wrapped(that.wrapped) { }
typename base::value_type operator*() {
return (*wrapped).pObject->GetInterface();
}
MagicIterator& operator++() {
++wrapped;
return *this;
}
MagicIterator operator++(int) {
MagicIterator it(*this);
wrapped++;
return it;
}
bool operator==(MagicIterator const& it) const {
return it.wrapped == wrapped;
}
bool operator!=(MagicIterator const& it) const {
return !(*this == it);
}
InputIterator wrapped;
};
// now that the iterator adepter is defined, we can define Begin and End
inline Container::item_iterator Container::Begin() {
return item_iterator(m_items.begin());
}
inline Container::item_iterator Container::End() {
return item_iterator(m_items.end());
}
Now, start using it:
for(MagicIterator<IInterface*> it = c.Begin(); it != c.End(); ++it) {
// ...
}
You can also use a iterator mixin provided by boost, which works like the input version of boost::function_output_iterator. It calls your iterator's operator() which then returns the appropriate value, doing what we do above in our operator* in principle. You find it in random/detail/iterator_mixin.hpp. That would probably result in fewer code. But it also requires to wrack up our neck to ensure the friend-stuff because Item is private and the iterator isn't defined inside Item. Anyway, good luck :)
It really depends on the Container, because the return values of c.Begin() and c.End() are implementation-defined.
If a list of possible Containers is known to MagicIterator, a wrapper class could be used.
template<typename T>
class MagicIterator
{
public:
MagicIterator(std::vector<T>::const_iterator i)
{
vector_const_iterator = i;
}
// Reimplement similarly for more types.
MagicIterator(std::vector<T>::iterator i);
MagicIterator(std::list<T>::const_iterator i);
MagicIterator(std::list<T>::iterator i);
// Reimplement operators here...
private:
std::vector<T>::const_iterator vector_const_iterator;
std::vector<T>::iterator vector_iterator;
std::list<T>::const_iterator list_const_iterator;
std::list<T>::iterator list_iterator;
};
The easy way would be to use a template which accepts Container's type:
// C++0x
template<typename T>
class Iterator :
public T::iterator
{
using T::iterator::iterator;
};
for(Iterator<Container> i = c.begin(); i != c.end(); ++i)
{
// ...
}
More information here.
I see no reason why you can't implement this exactly as you've laid it out... am I missing something?
To clarify, you'll need to put some kind of accessor methods on your Container class. They can be private and you can declare MagicIterator as a friend, if you feel that's the best way to encapsulate it, but I'd expose them directly. These accessor methods would use a normal STL iterator inside Container and perform the conversion to IInterface. Thus the iterating would actually be done with the Container's accessor methods and MagicIterator would just be a kind of proxy object to make it easier. To make it reentrant, you could have the MagicIterator pass in some kind of ID to look up the STL iterator inside Container, or you could actually have it pass in the STL iterator as a void *.
I've now found a solution which is fitter for my original purpose. I still don't like it though :)
The solution involves MagicIterator being templated on IInterface* and being constructed with both a void* to an iterator, the byte size of said iterator, and a table of pointers to functions which perform standard iteration functions on said void* such as increment, decrement, dereference, etc. MagicIterator assumes that it is safe to memcpy the given iterator into an internal buffer, and implements its own members by passing its own buffer as a void* to the supplied functions as if it were the original iterator.
Container then has to implement static iteration functions which cast back a supplied void* to a std::list::iterator. Container::begin() and Container::end() simply construct a std::list::iterator, pass a pointer to it into a MagicIterator along with a table of its iteration functions, and return the MagicIterator.
It's somewhat disgusting, and breaks my original rule regarding "no memcpy()", and makes assumptions about the internals of the iterators in question. But it avoids heap allocation, keeps Collection's internals (including Item) private, renders MagicIterator entirely independent of the collection in question and of IInterface*, and in theory allows MagicIterators to work with any collection (provided its iterators can be safely memcopy()'d).
A visitor may be a simpler (and therefore easier to maintain) solution.