rangeをより小さな部分に分割するadaptor

rangeを一定サイズの部分rangeに分割するadaptorを書いてみました。こんな感じに使えます。

#include <iostream>
#include <boost/lambda/lambda.hpp>
#include <boost/range/algorithm/for_each.hpp>
#include <boost/range/as_literal.hpp>

#include "chunked.hpp"

int main()
{
    boost::for_each(
        // 文字列を4文字ごとに分割する
        boost::as_literal("Hello, world!") | iorate::adaptors::chunked(4),
        std::cout << boost::lambda::_1 << "\n");
}

出力

Hell
o, w
orld
!

iterator_rangeのforward rangeを返しているので、単体では使えません。pstade::oven::delimitedなどと組み合わせて使うことを想定しています。
実装は以下から。
chunked.hpp

#ifndef IORATE_ADAPTOR_CHUNKED_HPP
#define IORATE_ADAPTOR_CHUNKED_HPP

#include <iterator>
#include <boost/iterator/iterator_categories.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/adaptor/define_adaptor.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator_range.hpp>

namespace iorate {

namespace detail {

template <class Iterator, class Distance>
inline Iterator next_impl(Iterator it, Iterator last, Distance n,
                          boost::random_access_traversal_tag)
{
    return last - it < n ? last : it + n;
}

template <class Iterator, class Distance>
inline Iterator next_impl(Iterator it, Iterator last, Distance n, ...)
{
    if (n >= 0)
        while (n-- && it != last)
            ++it;
    else
        while (n++)
            --it;
    return it;
}

template <class Iterator, class Distance>
inline Iterator next(Iterator it, Iterator last, Distance n)
{
    return next_impl(it, last, n,
                     typename boost::iterator_traversal<Iterator>::type());
}

template <class Iterator>
class chunk_iterator
  : public boost::iterator_facade<
        chunk_iterator<Iterator>,
        boost::iterator_range<Iterator>,
        boost::forward_traversal_tag,
        boost::iterator_range<Iterator> const &
    >
{
public:
    typedef boost::iterator_range<Iterator> chunk_type;

    chunk_iterator(Iterator first, Iterator last, std::size_t size)
      : chunk_(first, next(first, last, size)), last_(last), size_(size)
    {}

private:
    friend class boost::iterator_core_access;

    void increment()
    {
        chunk_ = chunk_type(chunk_.end(), next(chunk_.end(), last_, size_));
    }

    bool equal(chunk_iterator const &other) const
    {
        return chunk_ == other.chunk_;
    }

    chunk_type const &dereference() const
    {
        return chunk_;
    }

    chunk_type chunk_;
    Iterator last_;
    std::size_t size_;
};

template <class Range>
struct chunk_range
  : boost::iterator_range<
        chunk_iterator<typename boost::range_iterator<Range>::type>
    >
{
    typedef boost::iterator_range<
        chunk_iterator<typename boost::range_iterator<Range>::type>
    > base;

    typedef typename base::iterator iterator;

    chunk_range(Range &range, std::size_t size)
      : base(iterator(boost::begin(range), boost::end(range), size),
             iterator(boost::end(range), boost::end(range), size))
    {}
};

} // namespace detail

namespace adaptors {

BOOST_DEFINE_RANGE_ADAPTOR_1(chunked, detail::chunk_range, std::size_t)

}

} // namespace iorate

#endif

Boost1.45.0ではマクロが多重定義されている旨の警告が出ます。ヘッダでBOOST_DEFINE_RANGE_ADAPTOR_1が2回定義されているのが原因です。