Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I apply the DRY principle to iterators in C++? (iterator, const_iterator, reverse_iterator, const_reverse_iterator)

OK, so I have two (completely unrelated, different project) classes using iterators now. One has iterator and reverse_iterator working as intended, and the other, current one has iterator and a semi-broken const_iterator (specifically, because const_iterator derives from iterator, the code LinkedList<int>::iterator i = const_list.begin() is valid and allows you to modify the const defined list...).
I intend to add all four types to this class... If I can.

How would I proceed to minimize copy/pasting code and changing only the return type? Create a base class like base_iterator to inherit from? Create an iterator or const_iterator and inherit from that? Inherit from some std:: class? If any of these cases are the "best" approach, what code goes where?
Perhaps none of the alternatives are good? I'm quite lost here, and can't find much reference material.

Any advice is appreciated, but please keep in mind that I'm new to the subject (both iterators and C++ in general, especially OOP). I've tried, in vain, to study the header files shipped with GCC - they're not exactly the tutorial I'm looking for.

like image 362
exscape Avatar asked Nov 21 '09 20:11

exscape


2 Answers

Sometimes, blanket application of the so-called DRY rule (Don't Repeat Yourself, for those who aren't familiar) is not the best approach. Especially if you're new to the language (C++ and iterators) and OOP itself (methodology), there's little benefit in trying to minimise the amount of code you need to write right now.

I would implement the two iterators using appropriate code for each of them. Perhaps after you have more experience with the language, tools, and techniques, then go back and see whether you can reduce the amount of code by factoring out common code.

like image 190
Greg Hewgill Avatar answered Oct 23 '22 04:10

Greg Hewgill


It's actually extremely simple.

First of all, take a look at Boost.Iterator library.

Second: you need to declare a base class (it's well explained in the example how to proceed) that will be similar to this one.

template <class Value>
class BaseIterator: boost::iterator_adaptor< ... > {};

You implement the operations to move your pointer around there. Note that because it's an adaptation over an already existing iterator, you can implement it with only a few strokes. It's really impressive.

Third, you simply typedef it with the const and non-const versions:

typedef BaseIterator<Value> iterator;
typedef BaseIterator<const Value> const_iterator;

The library explictly show you how to make the const_iterator version be constructible from the iterator version.

Fourth, for the reverse thing, there is a special reverse_iterator object, that is built on a regular iterator and move backwards :)

All in all, a really elegant and yet fully functional way of defining iterators on custom classes.

I regularly write my own container adaptors, and it's less about DRY than simply saving myself some typing!

like image 3
Matthieu M. Avatar answered Oct 23 '22 02:10

Matthieu M.