1

I'm asking myself if it is possible to extend boost-range by an adaptor, which I call adjacentAdaptor. This adaptor should basically iterate over all pairs of adjacent elements in a vector, list and so on.

I think this function is very useful in my use cases, where I often have to iterate over lists representing time steps.

The output of the last for loop should be something like:

0 1
1 2
2 3

A vector having only one element or no elements should produce nothing.

I tried using boost::adaptors::sliced producing the necessary sublist, but then I don't know how boost::range can help me to zip both subranges to one.

I just found a probable solution using boost::iterators, but I really don't like the amount of code one has to write. Also I'm missing the first and second instead I have to write a clumsy get<>. Unfortunately, the program crashes if the vector is empty!

#include <vector>
#include <iostream>
#include <boost/range.hpp>
#include <boost/range/algorithm/transform.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>

int main()
{
        std::vector<int> v = { 0,1,2,3 };
        for (auto iter : v | boost::adaptors::sliced(0, v.size() - 1)) {
            std::cout << "First: " << iter << std::endl;
        }
        for (auto iter : v | boost::adaptors::sliced(1, v.size())) {
            std::cout << "Second: "<< iter << std::endl;
        }
        auto s = boost::iterators::make_zip_iterator(boost::make_tuple(v.begin(), v.begin() + 1));
        auto e = boost::iterators::make_zip_iterator(boost::make_tuple(v.end()-1, v.end()));
        for (auto iter : boost::make_iterator_range(s, e)) {
            std::cout << iter.get<0>() << " " << iter.get<1>() << std::endl;
        }
//          for (auto iter : v | adjacentAdaptor) {
//              std::cout << iter.first << " " << iter.second << std::endl;
//          }
}

I'm very glad for any help I can receive in this question.

Own partial solution

After some template type deduction I came up with something relatively useable.

#include <vector>
#include <iostream>
#include <boost/range.hpp>
#include <boost/range/algorithm/transform.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>

template<typename T>
using retHelperType = decltype(boost::iterators::make_zip_iterator(boost::make_tuple(T().begin(), T().begin() + 1)));

template<typename T>
using retType = decltype(boost::make_iterator_range(retHelperType<T>(), retHelperType<T>()));

template<typename T>
retType<T> adjacentIterator(T& v) {
    if (v.empty()) {
        auto s = boost::iterators::make_zip_iterator(boost::make_tuple(v.end(), v.end()));
        auto e = boost::iterators::make_zip_iterator(boost::make_tuple(v.end(), v.end()));
        return boost::make_iterator_range(s, e);
    }
    else {
        auto s = boost::iterators::make_zip_iterator(boost::make_tuple(v.begin(), std::next(v.begin())));
        auto e = boost::iterators::make_zip_iterator(boost::make_tuple(std::prev(v.end()), v.end()));
        return boost::make_iterator_range(s, e);
    }
}

int main()
{
    retType<std::vector<int>> x;
    std::vector<int> v = { };

    for (auto iter : adjacentIterator(v)) {
        std::cout << iter.get<0>() << " " << iter.get<1>() << std::endl;
    }
}

Still, it would be nicer to access the elements with first and second, but I have no idea to achieve this behavior.

Aleph0
  • 5,816
  • 4
  • 29
  • 80
  • 1
    [range-v3](https://github.com/ericniebler/range-v3) has `view::sliding(2)` for that (but access in not with `first`/`second` neither). – Jarod42 Jun 28 '18 at 08:53
  • That is a very nice hint. Thanks for that. Seems that I have just to wait, till it enters the new boost libraries. We are still using boost 1.66.0 in my company. – Aleph0 Jun 28 '18 at 08:58

0 Answers0