.
template<std::size_t N>
using index=std::array<std::size_t, N>;
template<class T, size_t size, size_t... sizes>
struct MArr {
using my_index=index<sizeof...(sizes)+1>;
ref. gsl::span, :
namespace utility {
template<class It>
struct range {
It b,e;
It begin()const{return b;}
It end()const{return e;}
std::size_t size()const{return std::distance(begin(),end());}
bool empty()const{return begin()==end();}
using reference=typename std::iterator_traits<It>::reference;
reference front()const{return *begin();}
reference back()const{return *std::prev(end());}
};
template<class T>
struct span:range<T*> {
span(T* s, T*f):range<T*>{s,f}{}
span():span(nullptr,nullptr){}
span(T*s,std::size_t len):span(s,s+len){}
T&operator[](std::size_t i)const{return this->begin()[i];}
T* data()const{return this->begin();}
span without_front(std::size_t n=1)const{ return {this->begin()+(std::min)(n,this->size()), end()}; }
span without_back(std::size_t n=1)const{ return {this->begin(), end()-(std::min)(n,this->size())}; }
span only_front(std::size_t n=1)const{ return {this->begin(),this->begin()+(std::min)(n,this->size())}; }
span only_back(std::size_t n=1)const{ return {end()-(std::min)(n,this->size()),end()}; }
span mid(std::size_t start, std::size_t len)const{ return without_front(start).only_front(len); }
template< class U >
using compatible=std::integral_constant<bool, std::is_convertible<U*,T*>{}&&(sizeof(U)==sizeof(T))>;
template<class R>
using compatible_range=compatible< std::decay_t<decltype( *std::declval<R>().data() )> >;
template<class C,
std::enable_if_t< compatible_range< C& >, bool> =true,
std::enable_if_t< !std::is_same<span, std::decay_t<C>>{}, bool> =true
>
span(C&& c): span( c.data(), c.size() ){}
template<std::size_t N>
span( T(&arr)[N] ):span(arr, N){}
};
}
( , gsl), :
template<std::size_t N>
using index=std::array<std::size_t, N>;
using index_cref=utility::span<std::size_t const>;
using index_ref=utility::span<std::size_t>;
template<class T, size_t size, size_t... sizes>
struct MArr {
using my_index=index<sizeof...(sizes)+1>;
T& operator[]( index_cref r ){ return data[r.front()][ r.without_front() ]; }
T& operator[](index_cref r) {
return data[r.front()];
}
Once we do, your problem will become easier.
First we rewrite yours functo repeat the values index. This can be done the way you did it when you go through the callback, or you can implement nextthat promotes the index.
bool next_index( index_ref r, index_cref bounds ){
if (r.empty()||bounds.empty()) return false;
++r.back();
if (r.back()!=bounds.back()) return true;
r.back()=0;
return next_index( r.without_back(), bounds.without_back() );
}
now the iteration might look like this:
template<class MArr, class F>
void foreach_index( MArr const&, F&& f ){
using index=typename MArr::index;
index const bounds = MArr::bounds();
index cur = {{0}};
do {
f(cur);
} while( next_index(cur, bounds) );
}
which is cleaner, simpler and more efficient than your version (no type erasure).
The closest neighbor can be easily written in terms index_ref.
template<class F>
void foreach_neighbour( index_ref where, index_cref bounds, F&& f ){
for(std::size_t i=0; i<(std::min)(where.size(),bounds.size());++i){
if (where[i]){ where[i]--; f(where); where[i]++; }
if (where[i]+1<bounds[i]) { where[i]++; f(where); where[i]--; }
}
}