Multiple custom iterators for same container - c++

I have a created a dynamic container for storing bytes as uint8_t and have implemented an iterator class inside the container class. The iterator traverses through the bytes as expected. But these bytes are formatted with headers so I want to implement another iterator to skip through the byte array based on byte headers. Is it possible to have both iterator classes run when used with for(auto &x: container) based on which class is declared first? or is there some other way to do this?
Current iterator and container
class container {
public:
container() : s(0), c(2), data(new uint8_t[2]) {};
container(size_t reserve) : s(0), c(reserve + 1), data(new uint8_t[reserve + 1]) {};
~container();
// container insert, operators etc...
// iterator class
class Iterator {
public:
uint8_t* ptr;
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = uint8_t;
using pointer = uint8_t*;
using reference = uint8_t&;
Iterator(pointer m_ptr) : ptr(m_ptr) { std::cout << "In iterator 1" << std::endl; }
reference operator*() const;
pointer operator->();
Iterator& operator++();
Iterator operator++(int);
Iterator& operator+=(int const& lhs);
Iterator operator+(int const& lhs);
friend bool operator== (const Iterator& a, const Iterator& b);
friend bool operator!= (const Iterator& a, const Iterator& b);
};
Iterator begin() const;
Iterator end() const;
private:
uint8_t* data;
size_t s;
size_t c;
};

You can't make iteration behave differently based on declaration order. What would that even mean? Somehow, that's like asking for the same class to behave differently based on context or perhaps randomly.
However, what you can do is implement code like this:
for (auto x: my_container.raw()) { ... }
for (auto x: my_container.rows()) { ... }
for (auto x: my_container.columns()) { ... }
for (auto x: my_container.pixels()) { ... }
Note: I'm just guessing that the header you mention is a bitmap header and that you're processing an image file.
BTW: There's a bunch of things wrong with your code that are not catastrophic, but still good opportunities to improve, like e.g. using raw new[] memory allocation which should be a std::vector instead. Consider posting your working (!) code on
codereview.stackexchange.com.

No, range iteration syntax is what it is: a proxy for calling the container's begin() and end(); and then iterating over the returned sequence. That's the only way it works.
C++20 adds the ranges library which can be used to combine two separate iterator sequences together. Until C++20 the most common option in this kind of a situation is to declare a third iterator class that proxies over the combined sequence of the two underlying iterator classes, and then use this proxy iterator class with begin() and end(). The basic approach is fairly straightforward:
begin() returns the third iterator class initialized with the beginning and the ending iterator values of both underlying iterator classes, end() returns the third iterator class initialized with the ending iterator values of both underlying iterator classes for both the proxied, internal beginning and ending iterator values (i.e. begin=end for both of them).
The * and/or the -> operator overloads check if the first iterator class's proxied beginning iterator value already reached its ending value. If not then use it to implement * and ->, otherwise use the second proxied beginning iterator value.
The various forward/increment overloads advance the first iterator class's proxied beginning iterator value. If it's already at its iterator class's ending iterator value then the second iterator class's proxied beginning iterator value gets advanced.

Related

Inserting into a vector of move-only type

I have a std::vector<std::unique_ptr<T>>. I would like to insert some nullptrs into the middle of this vector. I tried vec.insert(first, size, nullptr) but this obviously doesn’t work because the nullptr needs to be copied. I could repeatedly call the singular version of insert but I was wondering if there was a more efficient way.
"Efficient" is something to be measured. But if your desire is to shift the elements in one go instead of constantly moving them one item to the right, you can do it with std::rotate. Here's how
vec.resize(vec.size() + size); // Add the "null" pointers to the end.
// Obtain valid first after resize
std::rotate(first, vec.end() - size, vec.end());
Since the function of rotate is to make the middle iterator the "new first" of the range, while the iterator preceding it is the "new last", the above choice of iterators will shift the range of null pointers to their intended location (before first).
Furthermore, since you tagged C++17, you can also pass the standard algorithm an execution policy, and hopefully gain some parallelism to boot.
You can put together your own iterator that creates default instances upon being dereferenced. These are prvalues, which enables construction of move-only types in a vector. Use counting instances of such an iterator to call std::vector::insert. Here's an example that's probably not standard compliant, but works.
template <class T>
class DefaultCtorInputIt {
public:
DefaultCtorInputIt(size_t n) : n(n) {}
using value_type = T;
using reference = T;
using pointer = T;
using iterator_category = std::input_iterator_tag ;
using difference_type = int;
using Self = DefaultCtorInputIt; // save typing (below)
value_type operator *() { return T(); }
Self& operator ++() { ++n; return *this; }
friend bool operator == (const Self& lhs, const Self& rhs) {
return lhs.n == rhs.n;
}
friend bool operator != (const Self& lhs, const Self& rhs) {
return !(lhs == rhs);
}
private:
size_t n;
};
This way, you can
std::vector<std::unique_ptr<int>> v;
// Fill v with some values...
using NullUPtr =DefaultCtorInputIt<std::unique_ptr<int>>; // save typing
// Insert 10 default constructed instances at desired position:
v.insert(v.begin() + 42, NullUPtr(0), NullUPtr(10));
Note that for this to be as efficient as possible, you should probably make sure the above iterator qualifies as a random access iterator, such that the size of the range [NullUPtr(0), NullUPtr(10)) can be computed with O(1) upfront to allocate memory only once. When handcrafting your own iterator type, it's also worth having a look at Boost iterator facade.
Resize the vector and use
std::move_backward.
e.g.
vec.resize(vec.size() + num_new)
std::move_backward(vec.begin() + num_unmoved,
vec.end() - num_new,
vec.end());

How to return a pointer as an iterator?

I need to implement iterators, and i don't have time to make nice iterator classes, so i have decided to just return pointers. It is something like this
int* begin()
{
return p;
}
But i want them to behave as usual stl iterators
*++begin(); // doesn't work because returned pointer isn't l-value
std::vector<int> vi{ 0, 1 };
*++vi.begin(); // works fine
int* p = begin();
*++p; // works fine as well
How can i do this?
Pointers do meet the iterator requirements prefectly (a pointer meets even the most-specialised Random access iterator requirements). Your problem comes from the fact that in the implementation of the standard library which you're using, the iterators provided by e.g. std::vector support more operations than the iterator requirements require.
In other words, the standard does not guarantee that ++vi.begin() will work for a std::vector iterator vi. It happens to work on your implementation of the standard library, but it's an implementation detail. An iterator which would not support that is still a perfectly valid iterator.
So, to answer your question: if you want a quick stand-in for an iterator which will support all iterator operations, you can certainly use a pointer. If you want a quick stand-in for an iterator which will additionally support all the operations your standard library implementation supports in addition to iterator requirements, you might have to roll out your own class.
A minimal iterator is quite easy to whip up using boost:
#include <boost/iterator/iterator_facade.hpp>
using namespace boost;
struct Int100
{
int arr[100];
struct iterator : iterator_facade<iterator,int,forward_traversal_tag>
{
iterator( int* p = nullptr ) : p(p) {}
void increment() { ++p; }
bool equal(const iterator& other) const { return p == other.p; }
int& dereference() const { return *p; }
int* p;
};
iterator begin() { return {arr}; }
iterator end() { return {arr+100}; }
};
This supports the *++begin() syntax you where looking for.

Is it safe to do a const cast here?

I've written my own generic tree implementation, and when writing iterators for it, I am having trouble with const correctness. The issue I am having currently is as follows:
This is the header file for a DFS iterator I wrote:
template<class Item>
class DFSIterator
{
public:
DFSIterator(const Item& rRootNode);
~DFSIterator();
DFSIterator* First();
DFSIterator* operator++(int rhs);
Item* operator*() const;
Item* operator->() const;
bool isDone() const;
template <class Node> friend class Node;
private:
void initListIterator(const Item* currentNode);
bool m_bIsDone;
const Item* m_pRootNode;
const Item* m_pCurrentNode;
ListIterator<Item>* m_pCurrentListIter;
std::map<const Item*, ListIterator<Item>*> m_listMap;
};
So the bit I am concerned about is the dereference operator:
template<class Item>
Item* DFSIterator<Item>::operator*() const
{
if(isDone())
{
return NULL;
}
else
{
return const_cast<Item*>(m_pCurrentNode);
}
}
Is it appropriate to do a const_cast there? I'm wondering if this would cause problems if a user put const objects into the container?
As your constructor uses a const Item, your operator shoud return a const pointer.
If you want to return a non-const item, you should use a non const parameter for your constructor. A solution is having a base class using const objects, and a subclass working with non-const objects (A bit in a way it is done in Objc, eg NSString and NSMutableString).
Do not cast the const away, as it will break its meaning!
There is a reason why the STL has a const_iterator and iterator class. If you want const correctness you will have to implement two seperate iterator classes. One of the goals of a iterator is to mimic a save pointer. So you do not want to return null if you're beyond the iteration range, you want to raise a debug assertion if this actually happens.
A const iterator class could look something like this:
template<class Item>
class DFSIteratorConst
{
public:
DFSIteratorConst(const Item& node)
{
m_pCurrentNode = &node;
};
const Item& operator*() const
{
assert(!IsDone());
return *m_pCurrentNode;
}
void operator++()
{
assert(!IsDone());
m_pCurrentNode = m_pCurrentNode->next;
}
operator bool() const
{
return !IsDone();
}
bool IsDone() const
{
return m_pCurrentNode == nullptr;
}
private:
Item const * m_pCurrentNode;
};
A few things to note:
consider returning a reference to the node (a const reference). This causes you to not be able to return null if the iterator is past the last element. Dereferencing a past the end iterator is usually bad behaviour and you want to find those issues during debug builds and testing
the const iterator class has a pointer to a const element. Meaning that it can change the pointer (otherwise you would not be able to iterate) but can not modify the element itself
the implicit conversion to bool is practical if you want to check the iterator in a while loop. This allows you to write: while(it) { it++; } instead of while(!it.IsDone()) { it++; } again, something that resembles a classic pointer.
use nullptr if available
As I can see from your use of std::map your are already using the STL. Maybe it would be easier to use exsiting STL iterators?
const_casting in itself is always safe, but any attempt to write to const_casted values who have been declared const is undefined behaviour.
In this case, if your returned pointer points to a value that is declared const and you attempt to modify it, you'll get undefined behavior.
Rule of thumb: If you need to use const_cast for anything but interoperation with const-incorrect code, your design is broken.
The standard says in 7.1.6.1 The cv-qualifiers:
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
Normally, you write two iterators versions, an iterator and a const_iterator.
There are template tricks to avoid the code duplication that is exposed in the Boost.Iterator library documentation. See how the Iterator Adaptor is defined as it is quite long.
The thing is that you will write a BaseIterator that is const conscious, and then provide aliases:
typedef BaseIterator<Item> Iterator;
typedef BaseIterator<Item const> ConstIterator;
The trickery is in how you define the conversion constructor so that Iterator to ConstIterator is possible but the reverse is not.
The real problem is that this code/interface is "lying".
The constructor says: I don't change you (the root).
But the iterator gives changeable Item away.
To be "honest" either the constructor takes a changeable root, even when it will not changed within this class,
or this class does not give changeable Item away.
However, Item itself defines whether it give changeable childs away.
Depending on that code may not compile. Maybe this is the reason for your construction.
Long talking, short meaning: this design is poor and should be changed
Probably you need two templates depending on the "const"-ness of given childs away

Using boost_foreach without const_iterator

Is there a way to use boost foreach without defining a const_iterator?
My use-case for this is an iterator for a vector, that can contain invalid elements. The iterator should traverse the vector, and yield only the valid elements. It should also repair the vector in the sense that it should swap each invalid item with the next valid item and resize the vector at the end. For example if -1 represents invalid values the vector [6,-1,-1,9,-1,2] should iterate over 6,9 and 2 and leave the vector as [6,9,2].
I tried implementing this with boost::iterator_facade but I could not think of a way to implement a const_iterator, because the vector can change by removing invalid values and thus cannot be const.
Separation of concerns: the container is responsible for its invariants, the iterators for traversal. If you move the repairing to the container you can separate the logical const from the mutable, hidden parts.
Can you write your iterators the 'dumbest' way possible to disentangle them from the container? For instance storing a numerical index (if it makes sense for your container), then calling a private friend (or more) of the container to access the logical n-th element.
The private friend(s) can then be overloaded on const and may still modify the mutable parts to do the repairing you describe, and then return the element.
An (abridged) example of container supporting random-access (and thus numerical indices for access):
template<typename T>
class vector {
mutable std::vector<std::weak_ptr<T>> data; // notice mutable
T&
fetch(int n);
T const&
fetch(int n) const; // notice const overload
public:
class const_iterator;
friend class const_iterator;
const_iterator
begin() const;
};
template<typename T>
class vector<T>::const_iterator {
int index;
vector<T> const* this_; // notice const
public:
// constructors go here
const_iterator&
operator++()
{ ++index; }
// ...
T const&
operator*() const
{ return this_->fetch(index); } // this will call the const version of fetch
};
// example implementation of the const version of fetch
template<typename T>
T const&
vector<T>::fetch(int n) const
{
auto removed = std::remove_if(data.begin(), data.end(), [](std::weak_ptr<T>& element)
{ return element.expired(); });
// mutate mutable data member in a logically const member
data.erase(data.begin(), removed);
// this assumes that there is no race condition
// bear with me for the sake of understanding the mutable keyword
return *data[n].lock();
}
All forms of "foreach" are specifically for iterating over every element of a container. You are not just iterating over every element of a container. You are modifying the container as you iterate.
So just write a regular for-loop. There's no need for special cleverness or anything.
Here's the code for it:
std::vector<int> it = vec.begin();
for(; it != vec.end;)
{
if(*it < 0)
{
it = vec.erase(it);
continue;
}
else
{
//Do stuff with `it`.
++it;
}
}
See, just a simple loop. No need for fancy iterator facades or other such gimmickry.

Custom container with custom iterator doesn't work when container defined as const

More or less everything is in the topic.
when I have
func(my_cont& c)
{ c.begin() };
things works, but
func(const my_cont& c)
{ c.begin() };
doesn't work, compiler claims that cant convert this to const my_cont<..>.... from my_cont<..>
What are the requirements for the container and my custom iterator to handle this?
Your container class should have two implementations of begin() and end() - one returning iterator and another returning const_iterator:
iterator begin ();
const_iterator begin () const;
iterator end ();
const_iterator end () const;
const_iterator cannot be used to modify the object that it points to.
You need to add a const begin(), something like this:
class my_cont
{
public:
const_iterator begin() const;
};
You'll have to define a const_iterator type also.
Here is a good article on writing iterators:
http://www.aristeia.com/Papers/CUJ_June_2001.pdf
As most people have noted you need a const version of begin() and end() that returns a const_iterator.
What most people have forgotten is that an iterator should be able to implicitly convert into a const_iterator. Otherwise it is hard to get a const iterator from a non cost object (without a lot of nasty casts).
my_cont data;
for(my_cont::const_iterator loop = data.begin(); loop != data.end(); ++loop)
{
/* STUFF */
}
Note above: The above calls will actually call the non cost versions begin() and end(). But they are being assigned to a const_iterator. So your iterators must be convertible to const_iterator for the above code to work (Note: There is no implicit conversion from const_iterator to iterator. That should take an explicit const_cast as it is inherently dangerous).
class my_cont
{
public:
class iterator { /* STUFF */ }
class const_iterator
{
public: const_iterator(iterator const& rhs);
/* STUFF */
}
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
/* STUFF*/
};
This is exactly the reason why STL implements cost_iterator for containers. Just check how is it implemented in STL, I'm not sure you can find any better solution.
You can only call const member functions on a const object (or volatile member functions on a volatile object).
The compiler is telling that iterator my_cont::begin() const is missing.