Making a class iterable with iteration order depending on class implementation - c++

I have a type that has two different implementations, using different data structures. One stores its data in a std::vector<std::unique_ptr<Data>>, the other in a 2D array Data***.
The elements are stored in a specific order, meaning that their position in the vector or 2D array matters. As such, when wanting to iterate over all data in my class, my for loops are dependent on the implementation, being basically one of the following:
for(auto& data : myClass->dataVector) { do Stuff }
for(int x = 0; x < myClass->xVals; x++) {
for(int y = 0; y < myClass->yVals; y++ {
do Stuff with myClass->dataArr[x][y]
}
}
Since the two version of my class share similarities, I want to have a proper parent class that is implemented by two inheriting classes, hopefully in a way that I can iterate over my data by simply doing something such as:
for(auto& data : myClass) { doStuff }
(notice how myClass acts as if it was a collection itself, even if it actually is just a container of a collection)
where the way and order in which this iteration works obviously depends on the implementation of the class.
How do make my class iterable in such a manner?

Lets assume you have a base with all the data, and two derived classes with traversal behavior:
class Base {
public:
std::vector<...> dataVector;
int xVals;
int yVals;
Data** dataArr;
};
Defining .begin() and .end() makes a class iterable with for_each. A simple forwarding to the vector iterators is enough for the first case:
class DerivedA : private Base {
public:
auto begin() { return this->dataVector.begin(); }
auto begin() const { return this->dataVector.begin(); }
auto end() { return this->dataVector.end(); }
auto end() const { return this->dataVector.end(); }
}
For the Data** case you will have to define a custom iterator:
class iterator {
public:
using value_type = Data;
using difference_type = std::ptrdiff_t;
using reference = Data&;
using pointer = Data*;
using iterator_category = std::forward_iterator_tag;
iterator() : m_base(), m_idx(0) { }
iterator(Base* b, std::size_t idx) : m_base(b), m_idx(idx) { }
reference operator*() const { return m_base->dataArr[m_idx / m_base->yVals][m_idx % m_base->y_vals]; }
pointer operator->() const { return &**this; }
friend iterator& operator++(iterator& rhs) { ++rhs.m_idx; return rhs; }
friend iterator operator++(iterator& lhs, int) { auto cp = lhs; ++lhs; return cp; }
friend bool operator==(iterator lhs, iterator rhs) { return lhs.m_idx == rhs.m_idx; }
friend bool operator!=(iterator lhs, iterator rhs) { return !(lhs == rhs); }
private:
Base* m_base;
std::size_t m_idx;
};
class const_iterator {
// equivalent but const. (reference = const Data& and pointer = const Data*)
// Make sure iterator is convertible to const_iterator.
};
class DerivedB : private Base {
iterator begin() { return { this, 0 }; }
const_iterator begin() const { return { this, 0 }; }
iterator end() { return { this, this->xVals*this->yVals }; }
const_iterator end() const { return { this, this->xVals*this->yVals }; }
};

Related

How to overload dereference operator of std::list for range-based for?

I am trying to handle std::list of pointer type, like this:
std::list<int*> pNums;
Originally, iterating this container with range-based for loop will look like this :
for(int* pNum : pNums)
{
std::cout << (*pNum) << std::endl;
}
However, I want to iterate this container with a value, not a pointer, like below:
for(int num : Range(pNums))
{
std::cout << num << std::endl;
}
|
Here, Range is a custom wrapping-class of std::list<int*>, something should be defined in this manner, I guess:
class Range
{
Range(std::list<int*>& _list) : list(_list) {}
std::list<int*>& list;
// Basically inherit the original iterator
class custom_const_iterator : std::list<int*>::const_iterator
{
// Define an overloaded dereference operator
const int& operator*() const
{
...
}
...
};
public:
custom_const_iterator begin() { return ...; }
custom_const_iterator end() { return ...; }
};
So, my question is, what should I write down for class Range?
I would take the following approach (explanation in the comments):
class Range
{
private:
// Store iterators to the begin and the end of the range,
// rather than a reference to the whole list
std::list<int*>::const_iterator first;
std::list<int*>::const_iterator last;
class iterator
{
private:
std::list<int*>::const_iterator it;
public:
explicit iterator(std::list<int*>::const_iterator i) : it(i) {}
// you should define all the other operators
// that a std::list iterator has!
iterator& operator++()
{
++it;
return *this;
}
iterator operator++(int)
{
it++;
return *this;
}
// just dereference to get the value
const int& operator*() const { return **it; }
// these two are quite important for basic functionality
bool operator==(const iterator& rhs) const { return it == rhs.it; }
bool operator!=(const iterator& rhs) const { return it != rhs.it; }
};
public:
Range(std::list<int*>& _list)
: first(_list.begin()), last(_list.end())
{}
public:
iterator begin() { return iterator(first); }
iterator end() { return iterator(last); }
};

How can I implement random access iterators for my container?

This is the header file for the container including a try to implement random access iterators :
using namespace std;
template <class Element, class Compare = std::equal_to<Element>>
class UniqueArray {
public:
Element** data;
unsigned int curr_size;
unsigned int max_size;
int* availability_array;
explicit UniqueArray(unsigned int size);
UniqueArray(const UniqueArray& other);
~UniqueArray();
// UniqueArray& operator=(const UniqueArray&) = delete;
unsigned int insert(const Element& element);
bool getIndex(const Element& element, unsigned int& index) const;
const Element* operator[] (const Element& element) const;
bool remove(const Element& element);
unsigned int getCount() const;
unsigned int getSize() const;
class Filter {
public:
virtual bool operator() (const Element&) const = 0;
};
UniqueArray filter(const Filter& f) const;
class UniqueArrayIsFullException{};
typedef Element ua_iterator;
typedef const Element ua_const_iterator;
ua_iterator begin(){
return **data;
}
ua_const_iterator begin() const {
return **data;
}
ua_iterator end(){
return *(*data + max_size);
}
ua_const_iterator end() const {
return *(*data + max_size);
}
};
A summary of the errors I get :
error: no match for ‘operator++’
error: no match for ‘operator*’
error: no type named ‘value_type’ in ‘struct std::iterator_traits<MtmParkingLot::Vehicle>
error: no match for ‘operator!=’
error: no match for ‘operator-’
On my implementation Element gets Vehicle and all these missing operators refer to Vehicle
I'm not quite sure on how to work on these errors because for example substracting Vehicles makes no sense..
If you want an iterator to return a reference to an object when it is dereferenced, you have to define a special Iterator class to do it. With boost::indirect_iterator this is pretty simple:
#include <boost/iterator/indirect_iterator.hpp>
template <class Element, class Compare = std::equal_to<Element>>
class UniqueArray {
// ...
auto begin() {
return boost::indirect_iterator<Element**, Element>(data_);
}
auto end() {
return boost::indirect_iterator<Element**, Element>(data_ + curr_size);
}
};
Simple demo
If you want to code it yourself, the idea is:
template<class Element>
class UniqueArray {
public:
//...
class Iterator {
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = Element;
using reference = Element&;
// ...
Iterator(Element** d) : data(d) { }
reference operator*() {
return **data;
}
Iterator& operator++() {
++data;
return *this;
}
friend bool operator!=(Iterator it1, Iterator it2) {
return it1.data != it2.data;
}
// ...
private:
Element** data;
};
Iterator begin() {
return Iterator(data);
}
Iterator end() {
return Iterator(data + curr_size);
}
};
Note, the full random access iterator requires implementation of many other member and non-member functions. You might want to make it a forward iterator to simplify things a little bit.
Simple demo
If you write that without the type aliases, you get definitions like
Element begin(){
return **data;
}
and use like
UniqueArray<Vehicle>::ua_iterator it = something.begin();
it++;
becomes
Vehicle it = something.begin();
it++;
which makes the cause of the errors more obvious - you're trying to apply iterator operations to your element type.
If you're fine with iteration producing Element*, the simple solution is
typedef Element** ua_iterator;
ua_iterator begin(){
return data;
}
ua_iterator end(){
return data + curr_size;
}
and similar for the const versions.
Then you could write
UniqueArray<Vehicle>::ua_iterator it = something.begin();
Vehicle* v = *it;
If you want iteration to produce Element&, you need to write an iterator class with the appropriate overloads and traits.
It's not very difficult, but it gets tedious.

Automatically create iterator from class that implements operator[]

Suppose I have a class that implements operator[], e.g.:
class Array {
public:
Array(size_t s) : data(new int[s]) {}
~Array() { delete[] data; }
int& operator[](size_t index) {
return data[index];
}
private:
int* data;
};
Is there a way to create a random access iterator from the class without having to manually create the iterator class and all its methods? I could manually define the class as follows:
class ArrayIterator : public std::iterator<std::random_access_iterator_tag, int> {
public:
ArrayIterator(Array& a) : arr(a), index(0) {}
reference operator[](difference_type i) {
return arr[index + i];
}
ArrayIterator& operator+=(difference_type i) {
index += i;
return *this;
}
bool operator==(const ArrayIterator& rhs) {
return &arr == &rhs.arr && index == rhs.index;
}
// More methods here...
private:
Array& arr;
difference_type index;
};
But doing so is time consuming since there are so many methods to implement, and each iterator for a class with operator[] would have the exact same logic. It seems it would be possible for the compiler to do this automatically, so is there a way to avoid implementing the entire iterator?
Is there a way to create a random access iterator from the class without having to manually create the iterator class and all its methods?
The simplest way to create a random-access iterator is to just use a raw pointer, which satisfies all of the requirements of the RandomAccessIterator concept (the STL even provides a default template specialization of std::iterator_traits for raw pointers), eg:
class Array {
public:
Array(size_t s) : data(new int[s]), dataSize(s) {}
~Array() { delete[] data; }
int& operator[](size_t index) {
return data[index];
}
size_t size() const { return dataSize; }
int* begin() { return data; }
int* end() { return data+dataSize; }
const int* cbegin() const { return data; }
const int* cend() const { return data+dataSize; }
private:
int* data;
size_t dataSize;
};
Implementing the random access operator by using operator[] may work, but it can be very inefficient and that's why compilers dont do that automatically. Just imagine adding operator[] to a class like std::list, where "going to element i" may take up to i steps. Incrementing an iterator based on operator[] would then have complexity O(n), where n is the size of the list. However, users of random access iterators, expect a certain efficiency, typically O(1).

C++17 custom iterator in a pimpl idiom context

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.

Wrapping a legacy C API in C++ with write iterator support

I have a legacy C API to a container-like object (specifically, the Python C API to tuples) which I would like to wrap in a nice C++14 API, so that I can use an iterator. How should I go about implementing this?
Here's some more details. We have the following existing C API which cannot be changed:
Py_ssize_t PyTuple_GET_SIZE(PyObject *p);
PyObject* PyTuple_GET_ITEM(PyObject *p, Py_ssize_t pos);
void PyTuple_SET_ITEM(PyObject *p, Py_ssize_t pos, PyObject *o)
We want to create a class which allows you to get access to a read/write iterator on the elements in the tuple.
The forward read-only iterator is not too difficult to define. Here is what I have:
class PyTuple {
private:
PyObject* tuple;
public:
PyTuple(PyObject* tuple) : tuple(tuple) {}
class iterator {
// iterator traits
PyObject* tuple;
Py_ssize_t index;
public:
iterator(PyObject *tuple, Py_ssize_t index) : tuple(tuple), index(index) {}
iterator& operator++() { index++; return *this; }
iterator operator++(int) { auto r = *this; ++(*this); return r; }
bool operator==(iterator other) const { return tuple == other.tuple && index == other.index; }
bool operator!=(iterator other) const { return !(*this == other); }
PyObject* operator*() { return PyTuple_GET_ITEM(tuple, index); }
// iterator traits
using difference_type = Py_ssize_t;
using value_type = PyObject*;
using pointer = PyObject**;
using reference = PyObject*&;
using iterator_category = std::forward_iterator_tag;
};
iterator begin() {
return iterator(tuple, 0);
}
iterator end() {
return iterator(tuple, PyTuple_GET_SIZE(tuple));
}
}
However, I am not too sure how to support writes. I have to somehow make *it = pyobj_ptr work. Conventionally, this would be done by changing the type to PyObject*& operator*() (so that it gives an lvalue) but I can't do this because the tuple "write" needs to go through PyTuple_SET_ITEM. I have heard that you can use operator= to solve this case but I am not sure if I should use a universal reference (Why no emplacement iterators in C++11 or C++14?) or a proxy class (What is Proxy Class in C++), and am not exactly sure what the code should look like exactly.
What you're looking for is basically a proxy reference. You don't want to dereference into PyObject*, you want to dereference into something that itself can give you a PyObject*. This is similar to how the iterators for types like vector<bool> behave.
Basically, you want operator*() to give you something like:
class PyObjectProxy {
public:
// constructors, etc.
// read access
operator PyObject*() const { return PyTuple_GET_ITEM(tuple, index); }
// write access
void operator=(PyObject* o) {
PyTuple_SET_ITEM(tuple, index, o); // I'm guessing here
}
private:
PyObject* tuple;
Py_ssize_t index;
};