I have a virtual container "ISequence" which is used as a template for container implementations on arrays or lists. I have implemented an iterator for both Array and List containers. What I want to do is to create a virtual IIterator class so I can create algorithms that use iterators and accept ISequence is a parameter. My goal is to be free of implementation.
I tried adding virtual IIterator class to ISequence with no methods and that inherits std::iterator but that didn't work
ISequence class:
template <typename T>
class ISequence {
protected:
int length; //length of sequence
public:
virtual int getLength() const = 0; //get length of sequence
virtual bool getIsEmpty() const = 0; //check if empty
public:
virtual T get(int index) const = 0; //get item based on index
virtual T getFirst() const = 0; //get first item
virtual T getLast() const = 0; //get last item
virtual ISequence<T>* getSubSequence(int startIndex, int endIndex) const = 0;
virtual void append(T item) = 0; //add item to the end
virtual void prepend(T item) = 0; //add item to the beginning
virtual void insertAt(int index, T item) = 0; //insert item at a specific point
virtual void remove(T item) = 0; //remove specific item
virtual void replace(int index, T item) = 0; //replace an item
};
Array with an iterator:
template <typename T>
class ArraySequence: public ISequence<T> {
private:
T* data;
public:
ArraySequence();
ArraySequence(ISequence<T>* sequence);
ArraySequence(int n, int leftLimit, int rightLimit);
ArraySequence<T>& operator=(const ArraySequence<T>& sequence);
~ArraySequence();
public:
virtual int getLength() const override;
virtual bool getIsEmpty() const override;
public:
virtual T get(int index) const override;
virtual T getFirst() const override;
virtual T getLast() const override;
virtual ArraySequence<T>* getSubSequence(int startIndex, int endIndex) const override;
virtual void append(T item) override;
virtual void prepend(T item) override;
virtual void insertAt(int index, T item) override;
virtual void remove(T item) override;
virtual void replace(int index, T item) override;
private:
class MyIterator: public std::iterator<std::random_access_iterator_tag, T> {
friend class ArraySequence;
private:
T* pos;
MyIterator(T* pos);
public:
MyIterator(const MyIterator &it);
~MyIterator();
public:
typename MyIterator::reference operator*() const;
typename MyIterator::pointer operator->() const;
typename MyIterator::reference operator[](const typename MyIterator::difference_type& n) const;
typename MyIterator::difference_type operator-(const MyIterator& it) const;
MyIterator operator++(int);
MyIterator& operator++();
MyIterator operator--(int);
MyIterator& operator--();
MyIterator operator+(const typename MyIterator::difference_type& n) const;
MyIterator& operator+=(const typename MyIterator::difference_type& n);
MyIterator operator-(const typename MyIterator::difference_type& n) const;
MyIterator& operator-=(const typename MyIterator::difference_type& n);
bool operator!=(const MyIterator& it) const;
bool operator==(const MyIterator& it) const;
bool operator<(const MyIterator& it) const;
bool operator>(const MyIterator& it) const;
bool operator<=(const MyIterator& it) const;
bool operator>=(const MyIterator& it) const;
};
public:
typedef MyIterator iterator;
typedef MyIterator const const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
};
My code for abstract IIterator (as an addition to ISequence):
protected:
class IIterator: public std::iterator<std::random_access_iterator_tag, T> { //virtual Iterator class
};
public:
typedef IIterator iterator;
typedef IIterator const const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
Then I inherit it by MyIterator in Array.
With what I tried I get the "Invalid operands to binary expression ('ISequence::IIterator' and 'ISequence::IIterator')" error message when I run simple forrange cycle.
The concepts you're implementing are rather foundational in C#. Perhaps there's a good reason none of that exists in C++? Yes!
You are trying to implement .Net library using C++. This is not such a great idea, since the design of that library caters to the limitations of C# and the CLR (or whatever they call it these days). C# can do much less with types at compile time, and it doesn't have a compile-time generic type substitution system like C++ does. By not using what C++ offers, you're reinventing the wheel and writing code that looks quite unidiomatic. And it'll likely be slower than it needs to be, since virtual method calls on iterators are liable to be invoked often, their overheads showing. The compiler will devirtualize some of the calls if it can, but that's not something to depend on.
Generalizing a bit; The direct use of most any idiom from the .Net ecosystem in C++ is wrong by default unless proven otherwise.
In C++ the notion of "interfaces" doesn't depend on virtual methods, but on concepts - i.e. constraints on types. There's no way to achieve this in C#, nor in CLR in general, the design doesn't support it. In C++, as long as the concrete type used fits within the constraints that the caller requires (e.g. is iterable), the compiler will take care of the rest. Idiomatically, you'd pass containers around either using ranges or iterators, and all code that accepts those should be generic: the type of the container or iterator should be a template parameter, and the iterators should not be forced to be of the same type - this was really a library design bug in older versions of C++ library.
So, the beauty of C++ is that you don't have to define any interfaces whatsoever. In C++20 you can constrain the types using concepts, but other than that, it's just simple - much simpler than in C#. The idea of ranges is to have objects that act "like" containers, i.e. you can iterate on them using range-for, but they aren't necessarily containers - they may be just a way to express a range of elements on a container, or even dynamically generated.
If you're thinking of doing this in support of porting code from C# to C++, then simply forget about the need for ISequence etc. Just use generic type parameters, optionally constrain them using concepts if you're using a C++20 compiler, and you're set. It's that easy. The container can be anything. Even the plain "C" array (shudder - don't use those, use std::array instead, always!).
Suppose we had following C# code:
System.IO.TextWriter cout = Console.Out;
Action<System.Collections.IEnumerable> printValues = (values) =>
{
cout.WriteLine("printValues");
foreach (var v in values)
cout.Write($"{v} ");
cout.Write("\n");
};
var list_of_ints = new List<int>{1, 2, 3, 4, 5};
var vector_of_strings = new String[]{"a", "b", "c", "d"};
cout.WriteLine("* Entire containers");
printValues(list_of_ints);
printValues(vector_of_strings);
cout.WriteLine("\n* Subranges of containers");
printValues(list_of_ints.GetRange(1, list_of_ints.Count() - 1));
printValues(vector_of_strings.Take(vector_of_strings.Count() - 1));
Output:
* Entire containers
printValues
1 2 3 4 5
printValues
a b c d
* Subranges of containers
printValues
2 3 4 5
printValues
a b c
It wouldn't look much different in C++, as long as you stick to idiomatic C++ and don't try reimplementing .Net in C++:
#include <forward_list>
#include <iostream>
#include <string>
#include <vector>
// This will accept entire containers, as well as C++20 ranges.
template <class C> void printValues(const C &values) {
std::cout << __FUNCTION__ << " with container\n";
for (auto &v : values)
std::cout << v << " ";
std::cout << "\n";
}
// This is the more legacy way of doing it - to stay compatible with C++98 (shiver).
template <class I1, class I2> void printValues(I1 start, I2 end) {
std::cout << __FUNCTION__ << " with iterators\n";
for (; start != end; ++start)
std::cout << *start << " ";
std::cout << "\n";
}
int main() {
std::forward_list<int> list_of_ints{1,2,3,4,5};
std::vector<std::string> vector_of_strings{"a", "b", "c", "d"};
std::cout << "* Entire containers\n";
printValues(list_of_ints);
printValues(vector_of_strings);
std::cout << "\n* Subranges of containers\n";
printValues(std::next(list_of_ints.cbegin()), list_of_ints.cend());
printValues(vector_of_strings.cbegin(), std::prev(vector_of_strings.cend()));
}
Output:
* Entire containers
printValues with container
1 2 3 4 5
printValues with container
a b c d
* Subranges of containers
printValues with iterators
2 3 4 5
printValues with iterators
a b c
Related
I am recently trying to know how ranged-for work,
and finally got a picture as below:
for(obj& temp_ref:range r)//hide iterator somewhere as the range defined.
For each iteration,there is just a update of current object to doing the job in one circle of loop,and this is decent explain for me.
But when I trying to combine range and iterator into a mixed class,
there`s a problem of which operator++ should it call in my code,
in the sense, it should be the derivative class method be call in range-based-for,
because I create the object as derivative object,my declare is virtual.
the code is provided here,the first part is sprated and fine,
but the second one is works weird for me.
#include <iostream>
namespace first
{
template<class iterator>
class range
{
public:
range(iterator ina,iterator inb):a(ina),b(inb){};
iterator a,b;
iterator& begin(){return a;};
iterator& end(){return b;};
};
template<class T>
class iterator
{
public:
iterator(T* ini):i(ini){};
T* i;
virtual iterator<T>& operator= (const iterator<T>& other){this->i=other.i;return *this;};
virtual T& operator* (){return *i;};
virtual T operator!= (const iterator<T>& other){return this->i==other.i?0:1;};
virtual void operator++ (){i++;};
};
class jump2:public iterator<int>
{
public:
jump2(int* ini):iterator<int>(ini){};
virtual void operator++ (){i+=2;};
};
}
namespace second
{
template<class T>
class iterator
{
public:
iterator(T* inStart,T* inFinal):current(inStart),final(inFinal){};
T* current;
T* final;
iterator<T> begin(){return iterator<T>(this->current,this->final);};
iterator<T> end(){return iterator<T>(this->final,this->final);};
virtual iterator<T>& operator= (const iterator<T>& other){this->current=other.current;this->final=other.final;return *this;};
virtual T& operator* (){return *this->current;};
virtual bool operator!= (iterator<T>& other){return this->current!=other.final;};
virtual void operator++ ()
{
std::cout<<"<call base>";
this->current=this->current+1;
};
};
template<class T>
class jumper:public iterator<T>
{
public:
jumper(T* inStart,T* inFinal):iterator<T>(inStart,inFinal){};
void operator++ ()
{
std::cout<<"<call deri>";
this->current=this->current+2;
};
};
};
int main()
{
int a[6]={1,0,2,0,3,0};
//success
{
using namespace first;
range<jump2> a_range(jump2(a),jump2(a+6));
for(int store:a_range)
std::cout<<store<<std::endl;
}//pause();
//Fail
{
using namespace second;
jumper<int> a_iterator_call_jumper(a,a+6);
for(int& i:a_iterator_call_jumper)
std::cout<<i<<std::endl;
}//pause();
return 0;
};
this output is
1
2
3
1
<call base>0
<call base>2
<call base>0
<call base>3
<call base>0
<call base>
but it should be like
1
2
3
1
<call deri>2
<call deri>3
<call deri>
Is this thing goes wrong because I use it wrong?
Or there`s a bug I am not find yet?
template<class iterator>
class range
{
public:
range(iterator ina,iterator inb):a(ina),b(inb){};
iterator a,b;
iterator& begin(){return a;};
iterator& end(){return b;};
};
no
template<class iterator>
class range
{
public:
range(iterator ina,iterator inb):a(ina),b(inb){};
iterator a,b;
iterator begin() const {return a;};
iterator end() const {return b;};
};
yes.
template<class T>
class iterator
{
public:
iterator(T* ini):i(ini){};
T* i;
virtual iterator<T>& operator= (const iterator<T>& other){this->i=other.i;return *this;};
virtual T& operator* (){return *i;};
virtual T operator!= (const iterator<T>& other){return this->i==other.i?0:1;};
virtual void operator++ (){i++;};
};
no
template<class T>
class iterator
{
public:
iterator(T* ini):i(ini){};
T* i;
iterator( iterator const& ) = default;
iterator& operator= (const iterator<T>& other) & = default;
T& operator*() {return *i;};
bool operator!= (const iterator& other) const {return this->i==other.i?0:1;};
void operator++() {++i;};
};
yes.
Don't use the standard C++ vtable-based polymorphic object model for iterators; they should be regular types, and the vtable-based polymorphic object model is not regular.
As for second, it is not a good plan. An iterator and the range over which you iterate are not the same thing. Smashing them together makes your class incoherent.
C++ iteration is not designed for dynamic dispatch (polymorphism). You can do it, but it kills performance, and it isn't trivial to write. You basically need to hide the virtual dispatch behind a regular type (and you can dispatch using vtables, or you can dispatch using C++'s void pointer based type erasure).
If you are used to other languages, many other languages descended from C++ use mandatory reference semantics with garbage collection for objects of class type. Their virtual inheritance object model works on reference variables, as it does in C++ with reference and pointer types.
C++ is relatively quirky (in this day and age) in that it supports complex objects as value types, and that the language and standard library presumed you are working with value types most of the time. (Note that a pointer is a kind of value type; but its value is the pointer, not the object), and that when you want to use reference/pointer semantics you have to adapt your code.
I have a pure virtual interface to a container which is more or less like this:
class IContainer
{
public:
virtual ~IContainer() = default;
virtual Element& operator[](size_t index) = 0;
virtual const Element& operator[](size_t index) const = 0;
virtual size_t size() const = 0;
};
I would like to make use of range for loops, so I need to define begin() and end(). In order to do so, I need to define the iterator type as well.
It should be not be particularly hard, but nevertheless I would like to know if is there already anything in STL or Boost that can come to help, before I start coding something that already exists.
It might not be a good idea, but adding for(:) loop support is relatively easy here. I'll be minimal.
I'll create an iteroid, a not-iterator that is enough to support for(:) loops. This requires ++, != and unary * support, and nothing else.
template<class C>
struct index_iteroid {
decltype(auto) operator*()const {
return (*container)[i];
}
index_iteroid(index_iteroid const&)=default;
index_iteroid& operator=(index_iteroid const&)=default;
friend bool operator==(index_iteroid const& lhs, index_iteroid const& rhs) {
return std::tie(lhs.i, lhs.container)==std::tie(rhs.i, rhs.container);
}
friend bool operator!=(index_iteroid const& lhs, index_iteroid const& rhs) {
return !(lhs==rhs);
}
void operator++()&{
++i;
}
index_iteroid(C* c, std::size_t in):i(in), container(c) {}
private:
std::size_t i = 0;
C* container = nullptr;
};
now we use it:
class IContainer
{
public:
virtual ~IContainer() = default;
virtual Element& operator[](size_t index) = 0;
virtual const Element& operator[](size_t index) const = 0;
virtual size_t size() const = 0;
index_iteroid<IContainer> begin() { return {this, 0}; }
index_iteroid<IContainer> end() { return {this, size()}; }
index_iteroid<IContainer const> begin() const { return {this, 0}; }
index_iteroid<IContainer const> end() const { return {this, size()}; }
};
and there you have it.
void test( IContainer* cont ) {
if (!cont) return;
for(Element& e : *cont) {
// code
}
}
please excuse any typos.
Now a full iterator takes about 2-3 times as much code as my iteroid does, but nothing tricky, just annoying boilerplate mostly.
The standard doesn't have much to help you. For boost, you could compose a counting iterator with a function caling iterator/generator, and have the function call use []. Boost also has some utilities to make it take less boilerplate to write a full iterator, if you want to upgrade the iteroid to an iterator.
C++ doesn't do "interfaces" like this. The idiomatic way is for the (potential) clients of IContainer to instead be templated over the container type and just call values[index], or be templated over an iterator type and call like *(first + offset).
In C++20 you will be able to write a Container Concept, which behaves somewhat like an interface definition, but you can already express a Concept as documented requirements.
If instead you want a type-erased random access "container", you can use boost::any_range<Element, boost::random_access_traversal_tag>
I want to know how to have a c++ class to be iterable (stl compatible) without exposing the implementation ?
The structure of the project is like :
Stream
class Stream
{
public:
Stream();
[...]
StreamIterator iter()
{
return StreamIterator(this);
}
private:
class impl;
std::unique_ptr<impl> pimpl;
};
StreamFilter
class StreamFilter
{
public:
StreamFilter();
[...]
private:
class impl;
std::shared_ptr<impl> pimpl;
};
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
StreamFilter is a base class for differents filtering strategies.
For simplicity in the sample code, I used raw memory pointers, of course in a real exploitation code I use intelligents pointers : shared, weak ...
I want to allow the StreamIterator become iterable in a STL way, doing :
StreamIterator iter = stream.iter();
iter.addFilter(new FilterByOffset([...with parameters...]));
for (auto item : iter)
{
[...doing something with filtered items...]
}
The basic way is to add some accessors to allow range-based for loop.
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
iterator begin();
iterator end();
const_iterator cbegin() const;
const_iterator cend() const;
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
Where iterator and const_iterator are basically typedef's to the internal container iterators. And this is the problem.
First, I don't want to expose private implementation in the StreamIterator header. And so, iterator and const_iterator are not allowed here.
Second, because of the stream filtering strategy, the iterators returned are not just alias to some internal stl containers. In the internal implementation, I need to call the filters in a functor way to check if the item need to be exclude or not.
The only type allowed in the StreamIterator header is the type of the item object returned.
Is there a way to do that?
Thank you very much!
Additional Information:
Maybe this declaration is a way to allow a private implementation, I need to investigate more :
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
struct iterator
{
Object::Ptr operator*();
iterator& operator++();
bool operator!= (const iterator& it) const;
};
typedef typename StreamIterator::iterator iterator;
iterator begin();
iterator end();
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
First, don't call it StreamIterator; an iterator is a pointer-like object for which 2 of them can specify a range. Your StreamIterator doesn't have this. Nothing good can come of reusing the well defined iterator term here.
Now, your StreamIterator is some kind of range of iterators. So we'll call it a StreamRange.
In order to hide how StreamRange can be iterated over, you have to hide how the iterators it uses work.
And this -- hiding the implementation detalis of a stream iterator -- has a substantial cost to it.
When iterating over a loop, each step in the loop involves ++ and * and an ==. Throwing a pointer indirection and a vtable lookup (or equivalent) on each of those will make your loops much slower.
But here is how to do it.
template<class Value>
struct any_input_iterator {
using difference_type = std::ptrdiff_t;
using value_type=Value;
using pointer = value_type*;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
private:
struct vtable_t {
bool(*equal)( std::any const& lhs, std::any const& rhs ) = 0;
Value(*get)( std::any& ) = 0;
void(*inc)( std::any& ) = 0;
};
vtable_t const* vtable = 0;
std::any state;
template<class U>
static vtable_t make_vtable() {
return {
[](std::any const& lhs, std::any const& rhs)->bool {
return std::any_cast<U const&>(lhs) == std::any_cast<U const&>(rhs);
},
[](std::any& src)->Value {
return *std::any_cast<U&>(src);
},
[](std::any& src) {
++std::any_cast<U&>(src);
}
};
}
template<class U>
static vtable_t const* get_vtable() {
static const auto vtable = make_vtable<U>();
return &vtable;
}
public:
template<class U,
std::enable_if_t<!std::is_same<std::decay_t<U>, any_input_iterator>{}, bool> = true
>
any_input_iterator(U&& u):
vtable(get_vtable<std::decay_t<U>>()),
state(std::forward<U>(u))
{}
any_input_iterator& operator++() { vtable->inc(state); return *this; }
any_input_iterator operator++(int) { auto tmp = *this; ++*this; return tmp; }
reference operator*() { return vtable->get(state); }
friend bool operator==( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
if (lhs.vtable != rhs.vtable) return false;
if (!lhs.vtable) return true;
return lhs.vtable->equal( lhs.state, rhs.state );
}
friend bool operator!=( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
return !(lhs==rhs);
}
struct fake_ptr {
Value t;
Value* operator->()&&{ return std::addressof(t); }
};
fake_ptr operator->()const { return {**this}; }
};
there are probably some typoes, but this is basic type erasure. Boost does a better job at this.
I only supported input iterators. If you want to support forward iterators, you have to change up some typedefs and return references and the like.
any_input_iterator<int> begin();
any_input_iterator<int> end();
that is, however, enough to let someone iterate over your range in question.
It will be slow, but it will work.
I have written a very simple file managing database that basicly looks like this:
class FileDB
{
public:
FileDB(std::string dir) : rootDir(dir) { }
void loadFile(std::string filename, File &file) const;
void saveFile(std::string filename, const File &file) const;
private:
std::string rootDir;
}
Now I would like to iterate through all files contained in the database like using a std::iterator:
void iterateFiles()
{
FileDB filedb("C:\\MyFiles");
for (FileDB::iterator file_it = filedb.begin(); file_it != filedb.end(); ++file_it)
{
File f = *file_it;
// do something with file
}
}
I've read answers to similar questions, some suggesting to derive std::iterator, some to use std::iterator_traits, but I don't really understand how to do that. What can possibly go wrong when trying to implement a custom iterator? And what's a simple yet elegant way to do it?
EDIT:
Please don't consider using boost, my question is of more conceptual nature.
EDIT 2:
The FileDB works like this:
rootDir
foo1
bar1
foo1bar1_1.txt
foo1bar1_2.txt
bar2
foo1bar2_1.txt
foo1bar2_2.txt
foo2
fooN
barM
fooNBarM_x.txt
So basicly, I can find a file by its name.
As my container is not in memory, I don't have pointers to its data. So my idea was to store the file's path in the iterator. This way, I can implement operator== with a string comparison, as the paths should be unique. The iterator returned from fileDB.end() would be an empty string and operator* would call fileDB::loadFile() with its filepath.
My biggest concern is about operator++. Having the filename, I can find out the containing directory and search for the next file, but this is really ineffective. Any ideas on how to do that? Or am I completely wrong with my whole concept?
Writing iterators yourself is hardly ever pretty. The pretiest way to add an iterator to your classes is to reuse an existing one. You should be aware, that pointers for example are just as good as iterators in C++ so there are many ways to provide an iterator without actually having to write your own.
This is basically how C++ works in many ways. It tries to make the language expendable and simple for end users by putting a lot of burden on library writers. I.e. library writers can write all the unpretty stuff, so the end user doesn't have to. Iterators are usally a part of a library.
Having said that, here comes the actual ugly part:
To be able to write your own iterators, here are some things you need to be aware of.
Type traits:
Type traits are a simple mechanism to add aditional information to types in C++ which works even with types which cannot be changed themselves. For example for an iterator it is important to know what it iterates over (i.e. the contained type). The way to get this information for a given iterator depends a lot on the iterator. For iterators which actually are objects, you can add typedefs in the class and use those, but for iterators which are pointers you need to infer it from the pointer type. To make this possible the information is stored in a type trait instead so there is a single place an code can look this information. This is the std::iterator_traits type trait.
std::iterator_traits work on anything, which is derived from the std::iterator template as well as on any kind of pointer, without any tweaking. So often it is best to use std::iterator as a base to avoid having to write your own traits specialisation. In case you cannot do this, it is still possible to provide the necessary traits, but it will be harder.
Tag classes and iterator types:
There are several different types of iterators available in C++ which have different behaviour and can/cannot do a lot of different things. Have a look at http://cplusplus.com/reference/std/iterator/ to see what kind of iterators are available and what they can do. The diagrams are not meant to be in an object oriented way (i.e. an input_iterator is neither a sub nor a base class of a forward_iterator), but rather as an API kind of derivation. I.e. you can use all algorithms which were written for an input iterator also with an forward iterator. The table on the page will tell you which methods you have to provide for each category.
Since these categories are not actually sub classes of each other (they shouldn't be, especially when comming from different types of collections), another mechanism is used to identify the capabilities of each iterator. An empty tag class is also included in the std::iterator_traits describing each iterator, which tells what this iterator can do and what it cannot do. If you do not write your own traits, you need to supply this tag class to the std::iterator template upon instantiation.
Example:
This example is taken from the cplusplus.com section on iterators:
class myiterator : public iterator<input_iterator_tag, int>
{
int* p;
public:
myiterator(int* x) :p(x) {}
myiterator(const myiterator& mit) : p(mit.p) {}
myiterator& operator++() {++p;return *this;}
myiterator operator++(int) {myiterator tmp(*this); operator++(); return tmp;}
bool operator==(const myiterator& rhs) {return p==rhs.p;}
bool operator!=(const myiterator& rhs) {return p!=rhs.p;}
int& operator*() {return *p;}
};
This iterator does not really make sense, since it only wraps a pointer, which could also have been used directly. However it can serve as an explanation. The iterator is derived from std::iterator as an input_iterator by supplying the appropriate tag. Also the template is told, that this iterator is iterating over ints. All other types, which are needed difference_type, reference, poiner etc. are automatically infered by the template. In some cases it may make sense to change some of these types manually (for example a std::shared_ptr has to be used as a pointer sometimes). Also the traits needed for this iterator will exist automatically, since it is already derived from std::iterator and the std::iterator_traits know where to find all necessary information.
class FileDB
{
class iterator;
public:
FileDB(std::string dir) : m_rootDir(dir) { m_files = getFileTreeList(); }
void loadFile(std::string filename, File &file) const;
void saveFile(std::string filename, const File &file) const;
iterator begin()
{
return iterator(m_files.begin(), *this);
}
iterator end()
{
return iterator(m_files.end(), *this);
}
private:
std::list<std::string> getFileTreeList() const;
private:
std::string m_rootDir;
std::list<std::string> m_files;
}
class FileDB::iterator
{
public:
iterator(std::list<std::string>::iterator pos, FileDB& owner) : m_pos(pos), m_owner(owner){}
bool operator==(const iterator& rhs) {return m_pos == rhs.m_pos;}
bool operator!=(const iterator& rhs) {return m_pos != rhs.m_pos;}
//etc
void operator++() {++m_pos;}
File operator*()
{
return openFile(*m_pos); // for example open some file descriptor
}
~iterator()
{
closeFile(*m_pos); // clean up!
}
private:
std::list<std::string>::iterator& m_pos;
FileDB& m_owner;
};
Here is iterator which calculatetes subnodes during traversal.
I wrote it for windows, but I think it is not difficult to castomize it for other platforms.
#include <list>
#include <windows.h>
#include <assert.h>
#include <iostream>
#include <string>
class File{};
class Iterator
{
public:
virtual bool isDone() = 0;
virtual void next() = 0;
virtual std::string getFileName() = 0;
virtual ~Iterator(){};
};
bool operator== (Iterator& lhs, Iterator& rhs);
class EndIterator : public Iterator
{
public:
virtual bool isDone() {return true;}
virtual void next(){};
virtual std::string getFileName() {return "end";};
};
class DirectoryIterator : public Iterator
{
public:
DirectoryIterator(const std::string& path);
virtual bool isDone();
virtual void next();
virtual std::string getFileName();
virtual ~DirectoryIterator();
private:
std::list<Iterator*> getSubelementsList(const std::string& path) const;
void init();
private:
bool m_wasInit;
std::string m_path;
std::list<Iterator*> m_leaves;
std::list<Iterator*>::iterator m_current;
};
class FilesIterator : public Iterator
{
public:
FilesIterator(const std::string& fileName);
virtual bool isDone(){return true;};
virtual void next(){};
virtual std::string getFileName();
virtual ~FilesIterator(){};
private:
std::string m_fileName;
};
class DbItertor
{
public:
DbItertor(Iterator* iterator) : m_ptr(iterator){}
DbItertor(const DbItertor& rhs) {*m_ptr = *rhs.m_ptr;}
std::string operator*()
{
if(m_ptr->isDone())
return "end";
return m_ptr->getFileName();
}
//File operator->(){return FileOpen(m_ptr->getFileName());}
void operator++() {m_ptr->next();}
~DbItertor(){delete m_ptr;}
private:
Iterator* m_ptr;
};
class FileDB
{
public:
FileDB(std::string dir) : m_rootDir(dir){}
DbItertor begin()
{
return DbItertor(new DirectoryIterator(m_rootDir));
}
DbItertor end()
{
return DbItertor(new EndIterator());
}
private:
std::string m_rootDir;
};
FilesIterator::FilesIterator(const std::string& fileName) :
m_fileName(fileName)
{}
std::string FilesIterator::getFileName()
{
return m_fileName;
}
DirectoryIterator::DirectoryIterator(const std::string& path) :
m_wasInit(false),
m_path(path)
{}
void DirectoryIterator::init()
{
m_leaves = getSubelementsList(m_path);
m_current = m_leaves.begin();
m_wasInit = true;
next();
}
DirectoryIterator::~DirectoryIterator()
{
for(std::list<Iterator*>::iterator i = m_leaves.begin(); i != m_leaves.end(); ++i)
delete *i;
}
void DirectoryIterator::next()
{
if(!m_wasInit)
init();
if(isDone())
return;
if((*m_current)->isDone())
++m_current;
else
(*m_current)->next();
}
bool DirectoryIterator::isDone()
{
if(!m_wasInit)
init();
return (m_leaves.size() == 0) || (m_current == --m_leaves.end());
}
std::string DirectoryIterator::getFileName()
{
if(!m_wasInit)
init();
return (*m_current)->getFileName();
}
std::list<Iterator*> DirectoryIterator::getSubelementsList(const std::string& path) const
{
std::list<Iterator*> result;
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;
char sPath[2048] = {0};
sprintf(sPath, "%s\\*.*", path.c_str());
hFind = FindFirstFile(sPath, &fdFile);
assert(hFind != INVALID_HANDLE_VALUE);
do
{
if(strcmp(fdFile.cFileName, ".") != 0 && strcmp(fdFile.cFileName, "..") != 0)
{
std::string fullName = path;
fullName += std::string(fdFile.cFileName);
if(fdFile.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
{
fullName += "\\";
result.push_back(new DirectoryIterator(fullName));
}
else
{
result.push_back(new FilesIterator(fullName));
}
}
}
while(FindNextFile(hFind, &fdFile));
FindClose(hFind);
return result;
}
bool operator== (Iterator& lhs, Iterator& rhs)
{
return lhs.getFileName() == rhs.getFileName();
}
bool operator!= (DbItertor& lhs, DbItertor& rhs)
{
return *lhs != *rhs;
}
int main()
{
FileDB db("C:\\456\\");
for(DbItertor i = db.begin(); i != db.end(); ++i)
{
std::cout << *i << std::endl;
}
return 0;
}
Have a quick question about what would be the best way to implement iterators in the following:
Say I have a templated base class 'List' and two subclasses "ListImpl1" and "ListImpl2". The basic requirement of the base class is to be iterable i.e. I can do:
for(List<T>::iterator it = list->begin(); it != list->end(); it++){
...
}
I also want to allow iterator addition e.g.:
for(List<T>::iterator it = list->begin()+5; it != list->end(); it++){
...
}
So the problem is that the implementation of the iterator for ListImpl1 will be different to that for ListImpl2. I got around this by using a wrapper ListIterator containing a pointer to a ListIteratorImpl with subclasses ListIteratorImpl2 and ListIteratorImpl2, but it's all getting pretty messy, especially when you need to implement operator+ in the ListIterator.
Any thoughts on a better design to get around these issues?
If you can get away with making List<T>::iterator non-virtual, then delegating the virtualness off add to List makes things simple:
template<typename T>
class List
{
virtual void add_assign(iterator& left, int right) = 0;
public:
class iterator
{
const List* list;
const T* item;
public:
iterator(const List* list, const T* item) : list(list), item(item) {}
iterator& operator +=(int right)
{
list->add_assign(*this, right);
return *this;
}
static iterator operator +(iterator const& left, int right)
{
iterator result = left;
result += right;
return result;
}
};
virtual iterator begin() const = 0;
virtual iterator end() const = 0;
};
Otherwise (if the iterators need to store significantly different data, for example), then you have to do the regular, boring pointer-to-implementation to get your virtualness:
template<typename T>
class List
{
class ItImpl
{
virtual ItImpl* clone() = 0;
virtual void increment() = 0;
virtual void add(int right) = 0;
};
public:
class iterator
{
ItImpl* impl;
public:
// Boring memory management stuff.
iterator() : impl() {}
iterator(ItImpl* impl) : impl(impl) {}
iterator(iterator const& right) : impl(right.impl->clone()) {}
~iterator() { delete impl; }
iterator& operator=(iterator const& right)
{
delete impl;
impl = right.impl->clone();
return *this;
}
// forward operators to virtual calls through impl.
iterator& operator+=(int right)
{
impl->add(right);
return *this;
}
iterator& operator++()
{
impl->increment();
return *this;
}
};
};
template<typename T>
static List<T>::iterator operator+(List<T>::iterator const& left, int right)
{
List<T>::iterator result = left;
result += right;
return result;
}
template<typename T>
class MagicList : public List<T>
{
class MagicItImpl : public ItImpl
{
const MagicList* list;
const magic* the_magic;
// implement ...
};
public:
iterator begin() const { return iterator(new MagicItImpl(this, begin_magic)); }
iterator end() const { return iterator(new MagicItImpl(this, end_magic)); }
};
There is something very important among iterators, called Iterator Category:
InputIterator
OutputIterator
ForwardIterator
BidirectionalIterator
RandomAccessIterator
Each category define an exact set of operations that are supported, efficiently, by the iterator.
Here, it seems you wish to turn down that powerful identification mechanism to create some kind of bastard category in which the operations are all present, but no guarantee is made on their efficiency.
I think your design smells.
So the problem is that the
implementation of the iterator for
ListImpl1 will be different to that
for ListImpl2. I got around this by
using a wrapper ListIterator
containing a pointer to a
ListIteratorImpl with subclasses
ListIteratorImpl2 and
ListIteratorImpl2, but it's all
getting pretty messy, especially when
you need to implement operator+ in the
ListIterator.
This design is fine IMHO, I can't see anything messy about it. Except for equality and subtraction, operations of the iterator can be implemented by virtual function pretty easily, so you'll have something like
class ListIteratorInterface // abstract
{
protected:
virtual Data& operator*()=0;
// and other operations
};
class ListIteratorA;
class ListIteratorB; // implementation of the above
class ListIterator
{
ListIteratorInterface* impl_;
public:
// when you create this, allocate impl_ on the heap
// operations, forward anything to impl_
};
You could store operator+ as a private virtual method in the base class, and have the iterator call that.
Alternatively, you could consider statically polymorphic list classes, rather than runtime polymorphism.
Say I have a templated base class 'List' and two subclasses "ListImpl1" and "ListImpl2"
What exactly do you gain by using inheritance here?