Say I've got an N-dimensional boost::multi_array (of type int for simplicity), where N
is known at compile time but can vary (i.e. is a non-type template parameter). Let's assume that all dimensions have equal size m
.
typedef boost::multi_array<int, N> tDataArray;
boost::array<tDataArray::index, N> shape;
shape.fill(m);
tDataArray A(shape);
Now I would like to loop over all entries in A
, e.g. to print them. If N was 2 for example I think I would write something like this
boost::array<tDataArray::index, 2> index;
for ( int i = 0; i < m; i++ )
{
for ( int j = 0; j < m; j++ )
{
index = {{ i, j }};
cout << A ( index ) << endl;
}
}
I've used an index object to access the elements as I think this is more flexible than the []-operator here.
But how could I write this without knowing the number of dimensions N
. Is there any built-in way? The documentation of multi_array is not very clear on which types of iterators exist, etc.
Or would I have to resort to some custom method with custom pointers, computing indices from the pointers, etc.? If so - any suggestions how such an algorithm could look like?
Ok, based on the Google groups discussion already mentioned in one of the comments and on one of the examples from the library itself, here is a possible solution that lets you iterate over all values in the multi-array in a single loop and offers a way to retrieve the index for each of these elements (in case this is needed for some other stuff, as in my scenario).
#include <iostream>
#include <boost/multi_array.hpp>
#include <boost/array.hpp>
const unsigned short int DIM = 3;
typedef double tValue;
typedef boost::multi_array<tValue,DIM> tArray;
typedef tArray::index tIndex;
typedef boost::array<tIndex, DIM> tIndexArray;
tIndex getIndex(const tArray& m, const tValue* requestedElement, const unsigned short int direction)
{
int offset = requestedElement - m.origin();
return(offset / m.strides()[direction] % m.shape()[direction] + m.index_bases()[direction]);
}
tIndexArray getIndexArray( const tArray& m, const tValue* requestedElement )
{
tIndexArray _index;
for ( unsigned int dir = 0; dir < DIM; dir++ )
{
_index[dir] = getIndex( m, requestedElement, dir );
}
return _index;
}
int main()
{
double* exampleData = new double[24];
for ( int i = 0; i < 24; i++ ) { exampleData[i] = i; }
tArray A( boost::extents[2][3][4] );
A.assign(exampleData,exampleData+24);
tValue* p = A.data();
tIndexArray index;
for ( int i = 0; i < A.num_elements(); i++ )
{
index = getIndexArray( A, p );
std::cout << index[0] << " " << index[1] << " " << index[2] << " value = " << A(index) << " check = " << *p << std::endl;
++p;
}
return 0;
}
The output should be
0 0 0 value = 0 check = 0
0 0 1 value = 1 check = 1
0 0 2 value = 2 check = 2
0 0 3 value = 3 check = 3
0 1 0 value = 4 check = 4
0 1 1 value = 5 check = 5
0 1 2 value = 6 check = 6
0 1 3 value = 7 check = 7
0 2 0 value = 8 check = 8
0 2 1 value = 9 check = 9
0 2 2 value = 10 check = 10
0 2 3 value = 11 check = 11
1 0 0 value = 12 check = 12
1 0 1 value = 13 check = 13
1 0 2 value = 14 check = 14
1 0 3 value = 15 check = 15
1 1 0 value = 16 check = 16
1 1 1 value = 17 check = 17
1 1 2 value = 18 check = 18
1 1 3 value = 19 check = 19
1 2 0 value = 20 check = 20
1 2 1 value = 21 check = 21
1 2 2 value = 22 check = 22
1 2 3 value = 23 check = 23
so the memory layout goes from the outer to the inner indices. Note that the getIndex
function relies on the default memory layout provided by boost::multi_array. In case the array base or the storage ordering are changed, this would have to be adjusted.
There is a lack of simple boost multi array examples. So here is a very simple example of how to fill a boost multi array using indexes and how to read all the entries using a single pointer.
typedef boost::multi_array<double, 2> array_type;
typedef array_type::index index;
array_type A(boost::extents[3][2]);
// ------> x
// | 0 2 4
// | 1 3 5
// v
// y
double value = 0;
for(index x = 0; x < 3; ++x)
for(index y = 0; y < 2; ++y)
A[x][y] = value++;
double* it = A.origin();
double* end = A.origin() + A.num_elements();
for(; it != end; ++it){
std::cout << *it << " ";
}
// -> 0 1 2 3 4 5
If you don't need the index, you can simply do:
for (unsigned int i = 0; i < A.num_elements(); i++ )
{
tValue item = A.data()[i];
std::cout << item << std::endl;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With