我有一个无法修改的遗留类层次结构。由于外部库的要求,我需要为Line和Ring定义Boost.Langes,其中两者都只在一次运行中暴露点(即,对于Line和Rink,它都应该是Boost.Range of points)。
要说明的伪代码:
Line l1 = Line{{1.0,2.0},{3.0,4.0},{5.0,6.0}} // init Line with three Points
Line l2 = Line{{7.0,8.0},{9.0,10.0},{11.0,12.0}} // init Line with three Points
auto lit = boost::begin(l1); // points to the Point{1.0,2.0}
++lit; // points to the Point{3.0,4.0}
Ring r1 = Ring{l1,l2} // init Ring with two Lines
auto rit = boost::begin(r1); // points to the Point{1.0,2.0}
++rit; // points to the Point{3.0,4.0}
++rit; // points to the Point{5.0,6.0}
++rit; // points to the Point{7.0,8.0}
++rit; // points to the Point{9.0,10.0}
// etc...
直线很容易,因为点是直接存储的(我已经用Boost成功地做到了这一点。范围见示例)。然而,我不知道如何使用Ring来做到这一点,因为我需要直接到达每条线的点。
class Point
{
public:
double x, y;
}
class Line
{
public:
std::vector<Point> points;
}
class Ring
{
public:
std::vector<Line> lines;
}
您需要扩展Boost.Range,以便它将Ring识别为有效范围。但在此之前,您需要定义一个自定义迭代器,将vector< vector<T> >
平坦化为1D范围。
这个例子使用方法2来扩展Boost.Range。它还使用boost::iterator_facade
来方便编写自定义迭代器,并假设迭代器只需要支持前向遍历。
#include <iostream>
#include <vector>
#include <boost/assign/std/vector.hpp> // for 'operator+=()'
#include <boost/foreach.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range.hpp>
struct Point
{
Point(double x, double y) : x(x), y(y) {}
double x, y;
};
struct Line {std::vector<Point> points;};
struct Ring {std::vector<Line> lines;};
/* Custom iterator type that flattens a 2D array into a 1D array */
template <class I, // Line iterator type
class R // Point reference type
>
class RingIteratorImpl : public boost::iterator_facade<
RingIteratorImpl<I,R>, Point, boost::forward_traversal_tag, R>
{
public:
RingIteratorImpl() : lineIter_(0), pointIndex_(0) {}
explicit RingIteratorImpl(I lineIter)
: lineIter_(lineIter), pointIndex_(0) {}
private:
friend class boost::iterator_core_access;
void increment()
{
++pointIndex_;
if (pointIndex_ >= lineIter_->points.size())
{
++lineIter_;
pointIndex_ = 0;
}
}
bool equal(const RingIteratorImpl& other) const
{
return (lineIter_ == other.lineIter_) &&
(pointIndex_ == other.pointIndex_);
}
R dereference() const {return lineIter_->points[pointIndex_];}
I lineIter_;
size_t pointIndex_;
};
typedef RingIteratorImpl<std::vector<Line>::iterator, Point&> RingIterator;
typedef RingIteratorImpl<std::vector<Line>::const_iterator, const Point&>
ConstRingIterator;
namespace boost
{
// Specialize metafunctions. We must include the range.hpp header.
// We must open the 'boost' namespace.
template <>
struct range_mutable_iterator<Ring> { typedef RingIterator type; };
template<>
struct range_const_iterator<Ring> { typedef ConstRingIterator type; };
} // namespace 'boost'
// The required Range functions. These should be defined in the same namespace
// as Ring.
inline RingIterator range_begin(Ring& r)
{return RingIterator(r.lines.begin());}
inline ConstRingIterator range_begin(const Ring& r)
{return ConstRingIterator(r.lines.begin());}
inline RingIterator range_end(Ring& r)
{return RingIterator(r.lines.end());}
inline ConstRingIterator range_end(const Ring& r)
{return ConstRingIterator(r.lines.end());}
int main()
{
Line l1, l2;
Ring ring;
{
using namespace boost::assign; // bring 'operator+=()' into scope
typedef Point P;
l1.points += P(1.1,1.2), P(1.3,1.4), P(1.5,1.6);
l2.points += P(2.1,2.2), P(2.3,2.4), P(2.5,2.6);
ring.lines += l1, l2;
}
// Boost Foreach treats ring as a Boost Range.
BOOST_FOREACH(Point p, ring)
{
std::cout << "(" << p.x << ", " << p.y << ") ";
}
std::cout << "n";
}
我得到以下输出:
(1.1, 1.2) (1.3, 1.4) (1.5, 1.6) (2.1, 2.2) (2.3, 2.4) (2.5, 2.6)