Consider the following (simplified) scenario:
class edgeOne {
private:
...
public:
int startNode();
int endNode();
};
class containerOne {
private:
std::vector<edgeOne> _edges;
public:
std::vector<edgeOne>::const_iterator edgesBegin(){
return _edges.begin();
};
std::vector<edgeOne>::const_iterator edgesEnd(){
return _edges.end();
};
};
class edgeTwo {
private:
...
public:
int startNode();
int endNode();
};
class containerTwo {
private:
std::vector<edgeTwo> _edges;
public:
std::vector<edgeTwo>::const_iterator edgesBegin(){
return _edges.begin();
};
std::vector<edgeTwo>::const_iterator edgesEnd(){
return _edges.end();
};
};
I.e., I have two mostly identical edge types and two mostly identical container types. I can iterate over each kind individually. So far, so fine.
But now my use case is the following: Based on some criteria, I get either a containerOne or a containerTwo object. I need to iterate over the edges. But because the types are different, I cannot easily do so without code duplication.
So my idea is the following: I want to have an iterator with the following properties:
- Regarding its traversal behavior, it behaves either like a std::vector<edgeOne>::const_iterator
or a std::vector<edgeTwo>::const_iterator
, depending on how it was initialized.
- Instead of returning a const edgeOne &
or const edgeTwo &
, operator*
should return a std::pair<int,int>
, i.e., apply a conversion.
I found the Boost.Iterator Library, in particular:
- iterator_facade, which helps to build a standard-conforming iterator and
- transform_iterator, which could be used to transform
edgeOne
andedgeTwo
tostd::pair<int,int>
, but I am not completely sure how the complete solution should look like. If I build nearly the entire iterator myself, is there any benefit to use transform_iterator, or will it just make the solution more heavy-weight?
I guess the iterator only needs to store the following data:
- A flag (bool would be sufficient for the moment, but probably an enum value is easier to extend if necessary) indicating whether the value type is edgeOne or edgeTwo.
- A
union
with entries for both iterator types (where only the one that matches the flag will ever be accessed).
Anything else can be computed on-the-fly.
I wonder if there is an existing solution for this polymorphic behavior, i.e. an iterator implementation combining two (or more) underlying iterator implementation with the same value type. If such a thing exists, I could use it to just combine two transform_iterator
s.
Dispatching (i.e., deciding whether a containerOne or a containerTwo object needs to be accessed) could easily be done by a freestanding function ...
Any thoughts or suggestions regarding this issue?