Search code examples
c++templatesboostnamespacesboost-multi-array

for_each algorithm to loop over boost::multi_array


Related questions have been asked here before, but I still haven't found a satisfactory answer, so I will try to explain my problem and hope someone can enlighten me.

I am currently writing some code using boost::multi_array, and the code itself is also dimension independent. I need to loop over all elements stored in the multi_array and do something with them. I am looking to do this in a STL-like way:

for_each(begin(array), end(array), function);

Or something similar. Other questions have pointed me to an example on the boost page itself:

for_each example
for_each implementation

Which is more or less exactly what I want. The problem comes when one tries to simply import this code into a larger program. One would naturally like to both wrap the function itself in some namespace, and use for example the C++ functionals as the function object. Doing any of these two will create template lookup problems for the compiler.

Does anyone know either how I can work around the template lookup issue, or an alternative way of doing it (that is hopefully as pretty)?

Additional info:

Compilation error when wrapping the for_each definitions in a namespace

./for_each.hpp:28:3: error: call to function 'for_each' that is neither visible in the template definition nor found by
argument-dependent lookup
  for_each(type_dispatch,A.begin(),A.end(),xform);
  ^
./for_each.hpp:41:5: note: in instantiation of function template specialization
'boost_utilites::for_each<boost::detail::multi_array::sub_array<double, 1>,
      double, times_five>' requested here
    for_each(type_dispatch,*begin,xform);
    ^
./for_each.hpp:50:3: note: in instantiation of function template specialization 'boost_utilites::for_each<double,
      boost::detail::multi_array::array_iterator<double, double *, mpl_::size_t<2>, boost::detail::multi_array::sub_array<double,
1>,
      boost::random_access_traversal_tag>, times_five>' requested here
  for_each(boost::type<typename Array::element>(),A.begin(),A.end(),xform);
  ^
foreach_test.cpp:46:19: note: in instantiation of function template specialization
'boost_utilites::for_each<boost::multi_array<double, 2,
      std::allocator<double> >, times_five>' requested here
  boost_utilites::for_each(A,times_five());
                  ^
./for_each.hpp:37:6: note: 'for_each' should be declared prior to the call site or in an associated namespace of one of its
arguments
void for_each (const boost::type<Element>& type_dispatch,
     ^
1 error generated.

When using a std::function object instead of the times_five object in the example, one gets basically the same compilation error.

Compiled with clang version 3.4-1ubuntu3.


Solution

  • Just

    std::for_each(array.data(), array.data() + array.num_elements(), function);
    

    To make it work with functions that expect an random-access range (with .begin(), .end() and .size()) use

    auto elements = boost::make_iterator_range(array.data(), array.data() + array.num_elements();
    
    // e.g.
    for (auto& element : elements) {
    
        ...
    }
    

    I personally like to use this with generate_n or fill_n etc.:

    std::for_each(array.data(), array.num_elements(), 0);
    

    Reference docs

    • element* data();
    • const element* data() const;

      This returns a pointer to the beginning of the contiguous block that contains the array's data. If all dimensions of the array are 0-indexed and stored in ascending order, this is equivalent to origin(). Note that const_multi_array_ref only provides the const version of this function.

    and

    • size_type a.num_elements()

      This returns the number of elements contained in the array. It is equivalent to the following code:

      std::accumulate(a.shape(),a.shape+a.num_dimensions(), 
                 size_type(1),std::multiplies<size_type>());>