Search code examples
c++boostboost-multi-array

Iterate through a boost::multi_array view


I want to understand how to use the view functionality provided by boost::multi_array. Specifically, I want to be able to iterate within a single loop over all elements of a view that represents a particular submatrix of the initial matrix (not necessarily continuous). It seems that the iterator provided will not do what I want (or anything, it will not compile).

In the following example, I have a 2x6 matrix and I want to get its 2x4 submatrix so, if I try to print it I would expect to get "BoosLion". Indeed this is the case if I iterate for each dimension. But when I try to do the iteration with a single iterator, the program will not compile.

#include <boost/multi_array.hpp>
#include <iostream>


int main()
{
  boost::multi_array<char, 2> a{boost::extents[2][6]};

  a[0][0] = 'B';
  a[0][1] = 'o';
  a[0][2] = 'o';
  a[0][3] = 's';
  a[0][4] = 't';
  a[0][5] = '\0';

  a[1][0] = 'L';
  a[1][1] = 'i';
  a[1][2] = 'o';
  a[1][3] = 'n';
  a[1][4] = 's';
  a[1][5] = '\0';
  typedef boost::multi_array<char, 2>::array_view<2>::type array_view;
  typedef boost::multi_array_types::index_range range;
  array_view b = a[boost::indices[range{0,2}][range{0,4}] ];

  for (unsigned int i = 0; i < 2; i++ ) {
    for (unsigned int j = 0; j < 4; j++ ) {
      std::cout << b[i][j] << std::endl;
    }
  }
//  I want to do something like this:
//  for (auto itr = b.begin(); itr < b.end(); ++itr) {
//    std::cout << *itr << std::endl;
//  }
}

Does anyone know how to iterate with only a single loop? I tried searching the documentation but have been unable to find anything relevant. Also, if anyone knows of another library that can do this, let me know, thank you!


Solution

  • Here is one way to do this:

    #include <iostream>
    #include <boost/multi_array.hpp>
    
    // Functor to iterate over a Boost MultiArray concept instance.
    template<typename T, typename F, size_t Dimensions = T::dimensionality>
    struct IterateHelper {
       void operator()(T& array, const F& f) const {
          for (auto element : array)
             IterateHelper<decltype(element), F>()(element, f);
       }
    };
    
    // Functor specialization for the final dimension.
    template<typename T, typename F>
    struct IterateHelper<T, F, 1> {
       void operator()(T& array, const F& f) const {
          for (auto& element : array)
             f(element);
       }
    };
    
    // Utility function to apply a function to each element of a Boost
    // MultiArray concept instance (which includes views).
    template<typename T, typename F>
    static void iterate(T& array, const F& f) {
       IterateHelper<T, F>()(array, f);
    }
    
    int main() {
       boost::multi_array<char, 2> a{boost::extents[2][6]};
    
       a[0][0] = 'B';
       a[0][1] = 'o';
       a[0][2] = 'o';
       a[0][3] = 's';
       a[0][4] = 't';
       a[0][5] = '\0';
    
       a[1][0] = 'L';
       a[1][1] = 'i';
       a[1][2] = 'o';
       a[1][3] = 'n';
       a[1][4] = 's';
       a[1][5] = '\0';
    
       typedef boost::multi_array<char, 2>::array_view<2>::type array_view;
       typedef boost::multi_array_types::index_range range;
       array_view b = a[boost::indices[range{0,2}][range{0,4}] ];
    
       // Use the utility to apply a function to each element.
       iterate(b, [](char& c) {
          std::cout << c << std::endl;
       });
    
       return 0;
    };
    

    The code above defines a utility function iterate(), to which you pass an object satisfying the Boost MultiArray concept (which includes views) and a function to apply to each element. The utility function works by using a Functor that iterates over each dimension recursively.

    CoLiRu