c++ - Polymorphic converting iterator -
consider 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., have 2 identical edge types , 2 identical container types. can iterate on each kind individually. far, fine.
but use case following: based on criteria, either containerone or containertwo object. need iterate on edges. because types different, cannot easily without code duplication.
so idea following: want have iterator following properties: - regarding traversal behavior, behaves either std::vector<edgeone>::const_iterator
or std::vector<edgetwo>::const_iterator
, depending on how initialized. - instead of returning const edgeone &
or const edgetwo &
, operator*
should return std::pair<int,int>
, i.e., apply conversion.
i found boost.iterator library, in particular:
- iterator_facade, helps build standard-conforming iterator and
- transform_iterator, used transform
edgeone
,edgetwo
std::pair<int,int>
, not sure how complete solution should like. if build entire iterator myself, there benefit use transform_iterator, or make solution more heavy-weight?
i guess iterator needs store following data:
- a flag (bool sufficient moment, enum value easier extend if necessary) indicating whether value type edgeone or edgetwo.
- a
union
entries both iterator types (where 1 matches flag ever accessed).
anything else can computed on-the-fly.
i wonder if there existing solution polymorphic behavior, i.e. iterator implementation combining 2 (or more) underlying iterator implementation same value type. if such thing exists, use combine 2 transform_iterator
s.
dispatching (i.e., deciding whether containerone or containertwo object needs accessed) done freestanding function ...
any thoughts or suggestions regarding issue?
template<typename t1, typename t2> boost::variant<t1*, t2*> get_nth( boost::variant< std::vector<t1>::iterator, std::vector<t2>::iterator > begin, std::size_t n ) { // either t1 or t2 whichever vector have pointer } // implement using boost::iterator utilities, think fascade might right 1 // takes function takes index, , returns nth element. compares // equality based on index, , moves position based on index: template<typename lambda> struct generator_iterator { std::size_t index; lambda f; }; template<typename lambda> generator_iterator< typename std::decay<lambda>::type > make_generator_iterator( lambda&&, std::size_t index=0 ); boost::variant< it1, it2 > begin; // set either 1 of begins auto double_begin = make_generator_iterator( [begin](std::size_t n){return get_nth( begin, n );} ); auto double_end = double_begin + number_of_elements; // number_of_elements how big container type-erasing
now have iterator can iterate on one, or other, , returns boost::variant<t1*, t2*>
.
we can write helper function uses visitor extract 2 fields want returned variant
, , treat adt. if dislike adts, can instead write class wraps variant
, provides methods, or change get_nth
less generic , instead return struct
data produced.
there going equivalent of branch on each access, there no virtual
function overhead in plan. requires auto
typed variable, can write explicit functor replace lambda [begin](std::size_t n){return get_nth( begin, n );}
, issue goes away.
easier solutions:
write for_each
function iterates on each of containers, , passes in processed data passed in function.
struct simple_data { int x,y; }; std::function<std::function<void(simple_data)>> for_each() const { auto begin = _edges.begin(); auto end = _edges.end(); return [begin,end](std::function<void(simple_data)> f) { for(auto = begin; != end; ++it) { simple_data d; d.x = it->getx(); d.y = it->gety(); f(d); } }; };
and similar in other. can iterate on contents without caring details calling foo->foreach()( [&](simple_data d) { /*code*/ } );
, , because stuffed foreach
std::function
return value instead of doing directly, can pass concept of looping around function.
as mentioned in comments, other solutions can include using boost
's type-erased iterator wrappers, or writing python-style generator mutable generator returns either simple_data
. directly use boost iterator-creation functions create iterator on boost::variant<t1, t2>
.
Comments
Post a Comment