Boost logo

Boost Users :

Subject: Re: [Boost-users] [iterators][range][rangeex] range concatenation
From: Dmitry Vinogradov (sraider_at_[hidden])
Date: 2009-04-06 16:09:01


Thorsten Ottosen wrote:
> er skrev:
>> Dmitry Vinogradov wrote:
>>> Is it possible with Boost (or RangeEx) to concatenate ranges?
>>>
>>> Sample usage:
>>>
>>> template <class Range> void process_range(Range const &);
>>>
>>> std::vector<foo> v = ...;
>>> std::list<foo> l = ...;
>>> process_range(concat(v, l));
>>
>> I take it you have a good reason not to simply copy v and l into a new
>> container. iterator_facade might help, and iterator_range to obtain a
>> range.
>
> It will be impossible to implement a generic iterator that allows
> for fast iteration over the two ranges. The problem is that the
> iterator would have to check when the new container starts, similar to
> segments in a std::deque<T>.
>
> So I would just call process_range() twice. If you really need to
> view the two ranges as one range, then you have to pay an abstraction
> penalty, albeit it might be more efficiant than copying the two
> containers into a new one (and it is probably not that trivial to write
> the iterator that depends on two or more other iterator types).
>
> If "foo" is a heavy object, then copying the references of the elements
> to a new container std::vector<boost::reference_wrapper<foo>> is
> probably the most efficient alternative.
>

Calling process_range() twice is not a solution in my case. Copying
references to a new container is a better way.

Regarding efficiency, can you look thru my rough concat() implementation
attached to discover any its disadvantages?


#define BOOST_TEST_MODULE ConcatTest
#include <boost/test/unit_test.hpp>
#include <vector>
#include <list>
#include "concat.h"

template <typename Range1, typename Range2, typename Range3>
bool TestConcat(Range1 &r1, Range2 &r2, Range3 &r3)
{
        std::vector<int> v1, v2;
        v1.insert(v1.end(), r1.begin(), r1.end());
        v1.insert(v1.end(), r2.begin(), r2.end());
        v2.insert(v2.end(), r3.begin(), r3.end());
        return v1 == v2;
}

template <typename Range1, typename Range2>
bool TestConcat(Range1 &r1, Range2 &r2)
{
        return TestConcat(r1, r2, Concatenate(r1, r2));
}

BOOST_AUTO_TEST_CASE(Concat)
{
        int a1[] = { 1, 2, 3, 4, 5 };
        int a2[] = { 10, 11, 12 };
        std::vector<int> v(boost::begin(a1), boost::end(a1));
        std::list<int> l(boost::begin(a2), boost::end(a2));
        int value = 0;
        boost::iterator_range<int*> i(&value, &value+1), n((int*)NULL, (int*)NULL);
    
        BOOST_CHECK(TestConcat(l, v));
        BOOST_CHECK(TestConcat(v, l));
        BOOST_CHECK(TestConcat(v, v));
        BOOST_CHECK(TestConcat(n, v));
        BOOST_CHECK(TestConcat(v, n));
        BOOST_CHECK(TestConcat(n, n));
        BOOST_CHECK(TestConcat(n, i));
        BOOST_CHECK(TestConcat(i, n));
        BOOST_CHECK(TestConcat(i, v));
        BOOST_CHECK(TestConcat(i, i));
}


#pragma once

#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>

template <typename reference>
struct DereferenceVisitor : public boost::static_visitor<reference>
{
        template <typename T>
        reference operator()(T Iterator) const
        {
                return *Iterator;
        }
};

struct IncrementVisitor : public boost::static_visitor<void>
{
        template <typename T>
        void operator()(T &Value) const
        {
                ++Value;
        }
};

struct DecrementVisitor : public boost::static_visitor<void>
{
        template <typename T>
        void operator()(T &Value) const
        {
                --Value;
        }
};

template <class TIterator1, class TIterator2, class TValue = typename boost::iterator_value<TIterator1>::type >
class ConcatIterator : public boost::iterator_facade<ConcatIterator<TIterator1, TIterator2, TValue>, TValue, boost::bidirectional_traversal_tag>
{
public:
        template <class TIterator>
        ConcatIterator(TIterator It, size_t Range /* 0 for the first range, 1 for the second range */, TIterator1 End1, TIterator2 Begin2) :
                It(It), Range(Range), End1(End1), Begin2(Begin2)
        {
                Stabilize();
        }

private:
        void Stabilize()
        {
                if ((Range == 0) && (It == End1))
                {
                        It = Begin2;
                        Range = 1;
                }
        }

public:
        reference dereference() const
        {
                return boost::apply_visitor(DereferenceVisitor<reference>(), It);
        }

        void increment()
        {
                boost::apply_visitor(IncrementVisitor(), It);
                if ((Range == 0) && (It == End1))
                {
                        It = Begin2;
                        Range = 1;
                }
        }

        void decrement()
        {
                if ((Range == 1) && (It == Begin2))
                {
                        It = End1;
                        Range = 0;
                }
                boost::apply_visitor(DecrementVisitor(), It);
        }

        bool equal(const ConcatIterator& lhs) const
        {
                return (Range == lhs.Range) && (It == lhs.It);
        }

private:
        size_t Range;
        boost::variant<TIterator1, TIterator2> It, End1, Begin2;
};

template <class TRange1, class TRange2>
boost::iterator_range<ConcatIterator<typename TRange1::iterator, typename TRange2::iterator> >
Concatenate(TRange1 &Range1, TRange2 &Range2)
{
        typedef ConcatIterator<typename TRange1::iterator, typename TRange2::iterator> TIterator;
        return boost::make_iterator_range(
                TIterator(Range1.begin(), 0, Range1.end(), Range2.begin()),
                TIterator(Range2.end (), 1, Range1.end(), Range2.begin()));
}

template <class TRange1, class TRange2>
boost::iterator_range<ConcatIterator<typename TRange1::const_iterator, typename TRange2::const_iterator> >
Concatenate(TRange1 const &Range1, TRange2 const &Range2)
{
        typedef ConcatIterator<typename TRange1::const_iterator, typename TRange2::const_iterator> TIterator;
        return boost::make_iterator_range(
                TIterator(Range1.begin(), 0, Range1.end(), Range2.begin()),
                TIterator(Range2.end (), 1, Range1.end(), Range2.begin()));
}


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net