2012-02-12 1 views
3

私は変更できないレガシーなクラス階層を持っています。外部ライブラリの要件のために、私はLineとRingのBoost.Rangesを定義する必要があります.Bost.Rangesは、1回の実行でポイントを公開するだけです(つまり、ラインとリングの両方がポイントのブーストです) 。ベクトルの複数のレイヤーを隠し、単一の範囲として公開するBoost.Rangeを作成する方法は?

説明するための擬似コード:

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.Range see the exampleで正常にこれを行っています)。しかし、私は各ラインのポイントに直接到達する必要があるので、私はリングでこれを行う方法がわかりません。

class Point 
{ 
    public: 
    double x, y; 
} 

class Line 
{ 
    public: 
    std::vector<Point> points; 
} 

class Ring 
{ 
    public: 
    std::vector<Line> lines; 
} 
+1

が前進トラバーサルによって範囲にすることができる、またはそれはランダムアクセスする必要がありますか。 –

+0

@EmileCormier Boost.Geometryの文書によると、「Boost.Rangeのランダムアクセス範囲のように動作しなければならない」[こちらを参照](http://www.boost.org/doc/libs/1_48_0/libs/geometry/ doc/html/geometry/reference/concepts/concept_ring.html) – meastp

+0

@EmileCormier ['boost :: iterator_facade'](http://www.boost.org/doc/libs/release/libs/iterator/doc/)から、 iterator_facade.html)リンクを使用すると、ランダムアクセスのサポートのように、 'distance_to'の定義を追加し、' boost :: forward_traversal_tag'を 'boost :: random_access_traversal_tag'に置き換える必要があります。これは正しいです? – meastp

答えて

7

それが有効範囲としてリングを認識するようにあなたはextend Boost.Rangeする必要があります。しかし、それを行う前に、vector< vector<T> >を1Dの範囲に平滑化するカスタムイテレータを定義する必要があります。

この例では、Method 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) 
+0

注:C++ 11とイニシャライザリストによるベクトル初期化では、 'boost :: assign'は必要なくなりました。 'BOOST_FOREACH'はC++ 11の範囲ベースのforループで置き換えることもできます。 –

関連する問題