subsurface/core/range.h
Berthold Stoeger cea171ffd4 core: implement an enumerating iterator
In the printing-template code, we loop through a vector and
then determine the index of the current element by searching
the vector. This irks me.

Since looping over a collection with an index is a rather
common theme, implement an enumerating iterator that can
be used as in:
	for (auto [idx, item]: enumerated_range(v)) {
		...
	}

For now, use it for the above vexing case. Convert other
iterations of this theme later.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-10-30 21:57:39 +01:00

85 lines
2.2 KiB
C++

// SPDX-License-Identifier: GPL-2.0
// Helper functions for range manipulations
#ifndef RANGE_H
#define RANGE_H
#include <utility> // for std::pair
#include <vector> // we need a declaration of std::begin() and std::end()
// Move a range in a vector to a different position.
// The parameters are given according to the usual STL-semantics:
// v: a container with STL-like random access iterator via std::begin(...)
// rangeBegin: index of first element
// rangeEnd: index one *past* last element
// destination: index to element before which the range will be moved
// Owing to std::begin() magic, this function works with STL-like containers:
// QVector<int> v{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// move_in_range(v, 1, 4, 6);
// as well as with C-style arrays:
// int array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// move_in_range(array, 1, 4, 6);
// Both calls will have the following effect:
// Before: 0 1 2 3 4 5 6 7 8 9
// After: 0 4 5 1 2 3 6 7 8 9
// No sanitizing of the input arguments is performed.
template <typename Range>
void move_in_range(Range &v, int rangeBegin, int rangeEnd, int destination)
{
auto it = std::begin(v);
if (destination > rangeEnd)
std::rotate(it + rangeBegin, it + rangeEnd, it + destination);
else if (destination < rangeBegin)
std::rotate(it + destination, it + rangeBegin, it + rangeEnd);
}
// A rudimentary adaptor for looping over ranges with an index:
// for (auto [idx, item]: enumerated_range(v)) ...
// The index is a signed integer, since this is what we use more often.
template <typename Range>
class enumerated_range
{
Range &base;
public:
using base_iterator = decltype(std::begin(std::declval<Range>()));
class iterator {
int idx;
base_iterator it;
public:
std::pair<int, decltype(*it)> operator*() const
{
return { idx, *it };
}
iterator &operator++()
{
++idx;
++it;
return *this;
}
iterator(int idx, base_iterator it) : idx(idx), it(it)
{
}
bool operator==(const iterator &it2) const
{
return it == it2.it;
}
bool operator!=(const iterator &it2) const
{
return it != it2.it;
}
};
iterator begin()
{
return iterator(0, std::begin(base));
}
iterator end()
{
return iterator(-1, std::end(base));
}
enumerated_range(Range &base) : base(base)
{
}
};
#endif