Using range-based for with alternative ranges

J

Juha Nieminen

I was thinking: How hard would it be to use the range-based for syntax
for ranges other than the full begin-end range. For instance, what if
you wanted it to traverse the container backwards instead of forwards?

What I mean is that one could write something like this:

//------------------------------------------------------------------
int table[] = { 1, 3, 5, 7, 9 };
std::vector<int> v(table, std::end(table));

std::cout << "Forwards:\n";

for(int element: table) std::cout << " " << element;
for(int element: v) std::cout << " " << element;

std::cout << "\nBackwards:\n";

for(int element: reverseRange(table)) std::cout << " " << element;
for(int element: reverseRange(v)) std::cout << " " << element;

std::cout << "\n";
//------------------------------------------------------------------

In other words, we would have a reverseRange() function that returns a
wrapper object that has reverse iterators for its begin() and end()
functions.

Other applications would be to traverse only part of the range, such as:

for(int element: subrange(table, 0, 3)) std::cout << " " << element;

I haven't found any utility wrappers for this in the new standard, so
I suppose the only way is to write such wrappers oneself.

So my question is: What would be the simplest implementation for eg. the
'reverseRange()' function above?

This is the "simplest" implementation I could come up with:

//------------------------------------------------------------------
#include <iterator>

template<typename Cont_t>
struct RevRange
{
typename Cont_t::reverse_iterator b, e;
RevRange(Cont_t& c): b(c.rbegin()), e(c.rend()) {}
typename Cont_t::reverse_iterator begin() { return b; }
typename Cont_t::reverse_iterator end() { return e; }
};

template<typename Elem_t, std::size_t size>
struct RevRangeArray
{
std::reverse_iterator<Elem_t*> b, e;
RevRangeArray(Elem_t (&array)[size]): b(array+size), e(array) {}
std::reverse_iterator<Elem_t*> begin() { return b; }
std::reverse_iterator<Elem_t*> end() { return e; }
};

template<typename Container_t>
inline RevRange<Container_t> reverseRange(Container_t& c)
{ return RevRange<Container_t>(c); }

template<typename Elem_t, std::size_t size>
inline RevRangeArray<Elem_t, size> reverseRange(Elem_t (&array)[size])
{ return RevRangeArray<Elem_t, size>(array); }
//------------------------------------------------------------------

It's a bit complicated and verbose, and I was wondering if a simpler
solution could be possible (and also if this could be done with one
single class rather than having to use two).
 
J

Juha Nieminen

Juha Nieminen said:
It's a bit complicated and verbose, and I was wondering if a simpler
solution could be possible (and also if this could be done with one
single class rather than having to use two).

This is slightly simpler:

//-------------------------------------------------------------------
#include <iterator>

template<typename Iter_t>
struct RangeWrapper
{
Iter_t mBegin, mEnd;
RangeWrapper(Iter_t b, Iter_t e): mBegin(b), mEnd(e) {}
Iter_t begin() { return mBegin; }
Iter_t end() { return mEnd; }
};

template<typename Container_t>
RangeWrapper<typename Container_t::reverse_iterator>
revRange(Container_t& container)
{
return { container.rbegin(), container.rend() };
}

template<typename Elem_t, std::size_t size>
RangeWrapper<std::reverse_iterator<Elem_t*>>
revRange(Elem_t (&array)[size])
{
typedef std::reverse_iterator<Elem_t*> Iter_t;
return { Iter_t(array + size), Iter_t(array) };
}
//-------------------------------------------------------------------
 
W

woodbrian77

I was thinking: How hard would it be to use the range-based for syntax
for ranges other than the full begin-end range. For instance, what if
you wanted it to traverse the container backwards instead of forwards?

What I mean is that one could write something like this:

//------------------------------------------------------------------
int table[] = { 1, 3, 5, 7, 9 };
std::vector<int> v(table, std::end(table));

std::cout << "Forwards:\n";

for(int element: table) std::cout << " " << element;
for(int element: v) std::cout << " " << element;

std::cout << "\nBackwards:\n";

for(int element: reverseRange(table)) std::cout << " " << element;
for(int element: reverseRange(v)) std::cout << " " << element;

std::cout << "\n";
//------------------------------------------------------------------

In other words, we would have a reverseRange() function that returns a
wrapper object that has reverse iterators for its begin() and end()
functions.

Other applications would be to traverse only part of the range, such as:

for(int element: subrange(table, 0, 3)) std::cout << " " << element;

I haven't found any utility wrappers for this in the new standard, so
I suppose the only way is to write such wrappers oneself.

I'm not sure about reversing, but for ranges it
might make sense to use a range class that has
begin and end function members. The following
is from the C++ Middleware Writer.


void
bigtest::Marshal :):cmw::SendBufferCompressed& buf
, boost::sub_range<std::vector<int32_t> > const& az1
)
{
::cmw::Counter cntr(msg_length_max);
cntr.Add(sizeof(int));
cntr.MultiplyAndAdd(boost::distance(az1), sizeof(int32_t));
buf.Receive32(boost::distance(az1));
for (auto const& it35 : az1) {
buf.Receive(&it35, sizeof(int32_t));
}
buf.Compress();
}


Shalom,
Brian
Ebenezer Enterprises
http://webEbenezer.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top