Boost.Rangeでdropを実装してみた

なぜかBoost.Range.Adaptorsにdropがなかったので作ってみた。slicedでdropを書けなくはないんですが、長さがわからないrangeに適用できないのが問題です。実際はtakeを先に作ったんですが、dropの方が短かったので…。

boost::copy(input | taco::drop(5), output.begin());

のように書けます。g++ 4.5必須。VC系列は動くか見てません。

Random Access Rangeである必要はない*1ので、このような処理も書けます。

std::stringstream ss("1 2 3 4 5 6 7 8 9 10");
boost::copy(boost::make_iterator_range(
                std::istream_iterator<int>(ss),
                std::istream_iterator<int>())
            | taco::drop(5),
            output.begin());
#include <utility>
#include <boost/range.hpp>

#define TACO_DEFUN(name, params, body)          \
    auto name params -> decltype(body) { return (body); }

namespace taco { namespace range {

namespace details {

struct drop_adaptor {
    size_t n;
};

template <class Rng>
boost::iterator_range<
    typename boost::range_iterator<Rng>::type
>
make_drop_range(Rng& r, std::random_access_iterator_tag, size_t const n) {
    return boost::make_iterator_range(std::min(boost::begin(r) + n, boost::end(r)),
                                      boost::end(r));
}

template <class Rng, class Category>
boost::iterator_range<
    typename boost::range_iterator<Rng>::type
>
make_drop_range(Rng& r, Category, size_t const n) {
    auto it = boost::begin(r), end = boost::end(r);
    for (size_t i = 0; i < n && it != end; ++i) {
	++it;
    }
    return boost::make_iterator_range(it, end);
}

template <class Rng>
TACO_DEFUN(operator |,
           (Rng& r, drop_adaptor drop),
           make_drop_range(r, typename boost::range_category<Rng>::type(), drop.n))

template <class Rng>
TACO_DEFUN(operator |,
           (const Rng& r, drop_adaptor drop),
           make_drop_range<const Rng>(r, typename boost::range_category<const Rng>::type(), drop.n))

}

details::drop_adaptor drop(size_t const n) {
    return details::drop_adaptor{n};
}

}}

namespace taco { using taco::range::drop; }

*1:ただしRandom Access Rangeの方が実装効率が良いです