How to use `boost::range` iterators with standard iterators
Asked Answered
T

2

6

I have functions that take in std::vector iterators, as in

typedef std::vector<Point> Points;

Points ConvexHull(Points::const_iterator first, Points::const_iterator last);

I usually pass the std iterators to them, but occasionally I need to work with boost iterators, such as boost::join's range iterator. How should I change the parametrizations of my functions, ideally without templates, so that they accept both iterators? Moreover, how do I indicate in each type which iterator concepts I need?

I tried looking at the boost::range documentation but it's overwhelmingly confusing for me and I don't know where to start.

For example, I couldn't find the difference between boost::range_details::any_forward_iterator_interface and boost::range_details::any_forward_iterator_wrapper, and whether I should use either of those to specify that I need a forward iterator.


Edit:

If I use boost::any_range, how can I pass non-const lvalue references?

For example:

template<typename T>
using Range = boost::any_range<T, boost::random_access_traversal_tag, 
                               T, std::ptrdiff_t>;


f(Range<Point> &points);  // defined elsewhere

// -------------

vector<Point> vec;
f(vec);  // error; cannot bind non-const lvalue reference to unrelated type
Trisect answered 19/3, 2019 at 11:27 Comment(0)
I
1

You should strongly consider using a template. Doing so let's the compiler keep useful information about what operations are actually occurring, which greatly helps it generate optimised output. The std:: convention is to name the type parameter for the concept required. E.g.

template< class BidirIt, class UnaryPredicate > // anything bidirectional (which includes random access)
BidirIt std::partition( BidirIt first, BidirIt last, UnaryPredicate p );

If you really don't want a template, you still shouldn't name anything in a detail namespace. Something like

#include <boost/range/any_range.hpp>

using PointRange = boost::any_range<Point, boost::random_access_traversal_tag>; // or another traversal tag.
using PointIterator = PointRange::iterator;

You will likely need to pass PointRange & less frequently than, say, int *&. Almost always passing by value is the correct behaviour. It is cheap to copy, as it holds a begin and end iterator from the Range that it was constructed from, nothing more.

Imago answered 19/3, 2019 at 11:49 Comment(0)
H
4

has the any_range for this purpose and it suits both purposes for your case.

https://www.boost.org/doc/libs/1_60_0/libs/range/doc/html/range/reference/ranges/any_range.html

From your example it would look like this:

#include <boost/range/any_range.hpp>

typedef boost::any_range<Point,
                         boost::bidirectional_traversal_tag,
                         Point,
                         std::ptrdiff_t
                        > PointRange;
Hibernaculum answered 19/3, 2019 at 11:41 Comment(5)
Thanks! Yes, I believe this is what I'm looking for. But is there a way to access the range's size? It seems size is undefined for boost::any_range<Point, boost::bidirectional_traversal_tag, Point, std::ptrdiff_t>;Trisect
You can use the free function boost::sizeImago
just call boost::size(range)Hibernaculum
Thanks! And is there a way to pass non-const lvalue references? (see edit above)Trisect
@Trisect its more of a c++ thing than boost::range - that is not possible unless you vector is of the range type (ie. inheritance) - you need to create the range object somehow, that is remove the reference - i like to think of a range as just a pair of iterators.Hibernaculum
I
1

You should strongly consider using a template. Doing so let's the compiler keep useful information about what operations are actually occurring, which greatly helps it generate optimised output. The std:: convention is to name the type parameter for the concept required. E.g.

template< class BidirIt, class UnaryPredicate > // anything bidirectional (which includes random access)
BidirIt std::partition( BidirIt first, BidirIt last, UnaryPredicate p );

If you really don't want a template, you still shouldn't name anything in a detail namespace. Something like

#include <boost/range/any_range.hpp>

using PointRange = boost::any_range<Point, boost::random_access_traversal_tag>; // or another traversal tag.
using PointIterator = PointRange::iterator;

You will likely need to pass PointRange & less frequently than, say, int *&. Almost always passing by value is the correct behaviour. It is cheap to copy, as it holds a begin and end iterator from the Range that it was constructed from, nothing more.

Imago answered 19/3, 2019 at 11:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.