C++ Range/Xrange Equivalent in Stl or Boost

C++ range/xrange equivalent in STL or boost?

Boost has counting_iterator as far as I know, which seems to allow only incrementing in steps of 1. For full xrange functionality you might need to implement a similar iterator yourself.

All in all it could look like this (edit: added an iterator for the third overload of xrange, to play around with boost's iterator facade):

#include <iostream>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/foreach.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <cassert>

template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T to)
{
//these assertions are somewhat problematic:
//might produce warnings, if T is unsigned
assert(T() <= to);
return boost::make_iterator_range(boost::counting_iterator<T>(0), boost::counting_iterator<T>(to));
}

template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T from, T to)
{
assert(from <= to);
return boost::make_iterator_range(boost::counting_iterator<T>(from), boost::counting_iterator<T>(to));
}

//iterator that can do increments in steps (positive and negative)
template <class T>
class xrange_iterator:
public boost::iterator_facade<xrange_iterator<T>, const T, std::forward_iterator_tag>
{
T value, incr;
public:
xrange_iterator(T value, T incr = T()): value(value), incr(incr) {}
private:
friend class boost::iterator_core_access;
void increment() { value += incr; }
bool equal(const xrange_iterator& other) const
{
//this is probably somewhat problematic, assuming that the "end iterator"
//is always the right-hand value?
return (incr >= 0 && value >= other.value) || (incr < 0 && value <= other.value);
}
const T& dereference() const { return value; }
};

template <class T>
boost::iterator_range<xrange_iterator<T> > xrange(T from, T to, T increment)
{
assert((increment >= T() && from <= to) || (increment < T() && from >= to));
return boost::make_iterator_range(xrange_iterator<T>(from, increment), xrange_iterator<T>(to));
}

int main()
{
BOOST_FOREACH(int i, xrange(10)) {
std::cout << i << ' ';
}
BOOST_FOREACH(int i, xrange(10, 20)) {
std::cout << i << ' ';
}
std::cout << '\n';
BOOST_FOREACH(int i, xrange(0, 46, 5)) {
std::cout << i << ' ';
}
BOOST_FOREACH(int i, xrange(10, 0, -1)) {
std::cout << i << ' ';
}
}

As others are saying, I don't see this buying you much over a normal for loop.

Is there a compact equivalent to Python range() in C++/STL

In C++11, there's std::iota:

#include <vector>
#include <numeric> //std::iota

int main() {
std::vector<int> x(10);
std::iota(std::begin(x), std::end(x), 0); //0 is the starting number
}

C++20 introduced a lazy version (just like Python) as part of the ranges library:

#include <iostream>
#include <ranges>

namespace views = std::views;

int main() {
for (int x : views::iota(0, 10)) {
std::cout << x << ' '; // 0 1 2 3 4 5 6 7 8 9
}
}

Numerical range iterators in boost?

boost::counting_iterator

#include <boost/iterator/counting_iterator.hpp>

std::for_each( boost::counting_iterator<int>(0),
boost::counting_iterator<int>(100),
do_something );

Is boost Range library going to be part of next C++ standard?

There's still a lot of discussion and work going on around ranges.

http://isocpp.org/blog/2013/01/update-from-the-ranges-study-group

It's unlikely that the existing boost ranges will be standardized in their current form. But that doesn't mean that you shouldn't go ahead and use the existing, available tools to improve your own programs.

Cycles in c++ like in python (range-based for)

A Python-like range notion is not provided out-of-the-box, but you could roll your own Range class with a simple iterator, like this:

#include <iostream>

template <typename T>
class Range
{
public:
class iterator
{
public:
explicit iterator(T val, T stop, T step) : m_val(val), m_stop(stop), m_step(step) { }
iterator& operator ++ ()
{
m_val += m_step;
if ((m_step > 0 && m_val >= m_stop) ||
(m_step < 0 && m_val <= m_stop))
{
m_val = m_stop;
}
return *this;
}
iterator operator ++ (int) { iterator retval = *this; ++(*this); return retval; }
bool operator == (iterator other) const {return m_val == other.m_val;}
bool operator != (iterator other) const {return !(*this == other);}
T operator * () const { return m_val; }
private:
T m_val, m_stop, m_step;
};

explicit Range(T stop)
: m_start(0), m_stop(stop), m_step(1)
{ }

explicit Range(T start, T stop, T step = 1)
: m_start(start), m_stop(stop), m_step(step)
{ }

iterator begin() const { return iterator(m_start, m_stop, m_step); }
iterator end() const { return iterator(m_stop, m_stop, m_step); }

private:
T m_start, m_stop, m_step;
};

template <typename T>
Range<T> range(T stop) { return Range<T>(stop); }

template <typename T>
Range<T> range(T start, T stop, T step = 1) { return Range<T>(start, stop, step); }

int main()
{
for (auto i : range(10)) { std::cout << " " << i; }
std::cout << std::endl;
for (auto i : range(4, 10, 2)) { std::cout << " " << i; }
std::cout << std::endl;
for (auto i : range(0.5, 1.0, 0.1)) { std::cout << " " << i; }
std::cout << std::endl;
}

In order to support range-based for, an iterator type and begin()/end() functions will do the job. (Of course my implementation above is quick and dirty, and could probably be improved.)

You will not get around rolling your own class like that, but once you have it, the usage is very much akin to the Python approach:

for (auto i : range(stop)) { ... }
for (auto i : range(start, stop, step)) { ... }

The example outputs (see live version here):

$ g++ -std=c++11 -o test test.cpp && ./test
0 1 2 3 4 5 6 7 8 9
4 6 8
0.5 0.6 0.7 0.8 0.9 1

If you only need integer ranges, you can also use boost::irange (thanks to Yakk for the reminder).

Does boost offer make_zip_range?

Boost.Range is providing combine() function as zip_iterator's range.

http://www.boost.org/doc/libs/1_56_0/libs/range/doc/html/range/reference/utilities/combine.html

Set std::vectorint to a range

You could use std::iota if you have C++11 support or are using the STL:

std::vector<int> v(14);
std::iota(v.begin(), v.end(), 3);

or implement your own if not.

If you can use boost, then a nice option is boost::irange:

std::vector<int> v;
boost::push_back(v, boost::irange(3, 17));

Why boost uniform_int_distribution takes a closed range (instead of a half-open range, following common C++ usage)?

Only with closed ranges, you can create a uniform_int_distribution, that produces any integer:

uniform_int_distribution<int> dist(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());

If this would be a half-open range, you could never reach std::numeric_limits<int>::max(), but only std::numeric_limits<int>::max() - 1.

It's the same situation for std::uniform_int_distribution in the C++11 standard library.

Half-open ranges for iterators are common, because one can easily express empty ranges (by setting begin == end). This doesn't make sense for distributions.


Reference: Stephan T. Lavavej mentions this exact reason in his talk "rand() Considered Harmful" at Going Native 2013 (around minute 14). This talk is about C++11 <random>, but of course the same reasoning applies to boost as well.



Related Topics



Leave a reply



Submit