.
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 func
to repeat the values index
. This can be done the way you did it when you go through the callback, or you can implement next
that 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]--; }
}
}