C++ programming
boost library
multi_array
data manipulation
dimension-independent loop

Dimension-independent loop over boostmulti_array?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

boost::multi_array provides multidimensional arrays in C++, but iterating over them generically (regardless of dimensionality) requires recursive template techniques. A dimension-independent loop uses template recursion that processes one dimension at a time, bottoming out at the innermost dimension where the actual element access happens. This pattern enables writing algorithms that work with 1D, 2D, 3D, or N-dimensional arrays without code duplication.

Basic boost::multi_array Usage

cpp
1#include <boost/multi_array.hpp>
2#include <iostream>
3
4int main() {
5    // 3D array: 2x3x4
6    boost::multi_array<double, 3> arr(boost::extents[2][3][4]);
7
8    // Fill with values
9    int val = 0;
10    for (int i = 0; i < 2; ++i)
11        for (int j = 0; j < 3; ++j)
12            for (int k = 0; k < 4; ++k)
13                arr[i][j][k] = val++;
14
15    std::cout << arr[1][2][3] << std::endl;  // 23
16}

This hard-codes the number of nested loops. For a dimension-independent approach, we need compile-time recursion.

Dimension-Independent Loop with Template Recursion

cpp
1#include <boost/multi_array.hpp>
2#include <iostream>
3
4// Base case: 1D — iterate over elements directly
5template <typename Array, typename Func>
6void for_each_element(Array& arr, Func func,
7                      typename std::enable_if<Array::dimensionality == 1>::type* = 0)
8{
9    for (auto it = arr.begin(); it != arr.end(); ++it) {
10        func(*it);
11    }
12}
13
14// Recursive case: N-D — iterate over sub-arrays
15template <typename Array, typename Func>
16void for_each_element(Array& arr, Func func,
17                      typename std::enable_if<(Array::dimensionality > 1)>::type* = 0)
18{
19    for (auto it = arr.begin(); it != arr.end(); ++it) {
20        for_each_element(*it, func);  // Recurse into sub-array
21    }
22}
23
24int main() {
25    boost::multi_array<int, 3> arr(boost::extents[2][3][4]);
26
27    // Fill
28    int val = 0;
29    for_each_element(arr, [&val](int& elem) { elem = val++; });
30
31    // Print
32    for_each_element(arr, [](int elem) { std::cout << elem << " "; });
33    // 0 1 2 3 4 5 ... 23
34}

The dimensionality compile-time constant drives SFINAE to select the correct overload. The recursive version peels off one dimension, and the base case processes the innermost elements.

With Index Tracking

To know the coordinates of each element during iteration:

cpp
1#include <vector>
2
3template <typename Array, typename Func>
4void for_each_with_index(Array& arr, Func func, std::vector<size_t>& indices,
5                         typename std::enable_if<Array::dimensionality == 1>::type* = 0)
6{
7    for (size_t i = 0; i < arr.shape()[0]; ++i) {
8        indices.push_back(i);
9        func(arr[i], indices);
10        indices.pop_back();
11    }
12}
13
14template <typename Array, typename Func>
15void for_each_with_index(Array& arr, Func func, std::vector<size_t>& indices,
16                         typename std::enable_if<(Array::dimensionality > 1)>::type* = 0)
17{
18    for (size_t i = 0; i < arr.shape()[0]; ++i) {
19        indices.push_back(i);
20        for_each_with_index(arr[i], func, indices);
21        indices.pop_back();
22    }
23}
24
25// Convenience wrapper
26template <typename Array, typename Func>
27void for_each_with_index(Array& arr, Func func) {
28    std::vector<size_t> indices;
29    for_each_with_index(arr, func, indices);
30}
31
32// Usage
33boost::multi_array<double, 2> matrix(boost::extents[3][4]);
34
35for_each_with_index(matrix, [](double& elem, const std::vector<size_t>& idx) {
36    elem = idx[0] * 10 + idx[1];
37    std::cout << "[" << idx[0] << "," << idx[1] << "] = " << elem << "\n";
38});

C++17 if constexpr Version

C++17 simplifies the recursion with if constexpr:

cpp
1template <typename Array, typename Func>
2void for_each_element(Array& arr, Func func) {
3    if constexpr (Array::dimensionality == 1) {
4        for (auto& elem : arr) {
5            func(elem);
6        }
7    } else {
8        for (auto it = arr.begin(); it != arr.end(); ++it) {
9            for_each_element(*it, func);
10        }
11    }
12}

No SFINAE needed — if constexpr discards the unused branch at compile time.

Accumulate / Reduce Pattern

cpp
1template <typename Array, typename T, typename BinaryOp>
2T accumulate_elements(const Array& arr, T init, BinaryOp op) {
3    if constexpr (Array::dimensionality == 1) {
4        for (const auto& elem : arr) {
5            init = op(init, elem);
6        }
7    } else {
8        for (auto it = arr.begin(); it != arr.end(); ++it) {
9            init = accumulate_elements(*it, init, op);
10        }
11    }
12    return init;
13}
14
15// Usage: sum all elements
16boost::multi_array<int, 3> arr(boost::extents[2][3][4]);
17// ... fill arr ...
18
19int total = accumulate_elements(arr, 0, std::plus<int>{});
20std::cout << "Sum: " << total << std::endl;

Transform Pattern

Apply a function to every element and write the result to another multi_array:

cpp
1template <typename SrcArray, typename DstArray, typename Func>
2void transform_elements(const SrcArray& src, DstArray& dst, Func func) {
3    if constexpr (SrcArray::dimensionality == 1) {
4        for (size_t i = 0; i < src.shape()[0]; ++i) {
5            dst[i] = func(src[i]);
6        }
7    } else {
8        for (size_t i = 0; i < src.shape()[0]; ++i) {
9            transform_elements(src[i], dst[i], func);
10        }
11    }
12}
13
14// Usage: double every element
15boost::multi_array<int, 2> src(boost::extents[3][4]);
16boost::multi_array<int, 2> dst(boost::extents[3][4]);
17// ... fill src ...
18
19transform_elements(src, dst, [](int x) { return x * 2; });

Using data() for Flat Iteration

If you just need to iterate over all elements without caring about indices, multi_array::data() gives a pointer to the contiguous storage:

cpp
1boost::multi_array<int, 3> arr(boost::extents[2][3][4]);
2
3int* data = arr.data();
4int total = arr.num_elements();
5
6for (int i = 0; i < total; ++i) {
7    data[i] = i;  // Flat access
8}

This bypasses the multi-dimensional structure entirely. Useful for bulk operations and interoperability with C APIs.

Common Pitfalls

  • Forgetting dimensionality is a compile-time constant: The SFINAE or if constexpr branch is resolved at compile time. You cannot use a runtime variable to select the dimensionality — the array dimensions must be known at compile time.
  • Iterator invalidation after reshape: Calling resize() on a multi_array invalidates all iterators and sub-array references. Complete any iteration before reshaping.
  • Performance of recursive sub-array access: Each operator[] on a multi-dimensional multi_array creates a sub-array view object. For performance-critical code, use data() with manual index calculation or multi_array_ref for views without copies.
  • Mixing const and non-const in recursion: A const multi_array produces const sub-arrays on iteration. If your function modifies elements, the array parameter must be non-const at every recursion level. Use separate overloads for const and non-const.
  • Assuming row-major storage: boost::multi_array defaults to C storage order (row-major). If you change the storage order to Fortran (column-major), flat iteration via data() traverses elements in a different order than nested loops.

Summary

  • Use template recursion with dimensionality to write dimension-independent loops over boost::multi_array
  • The base case handles 1D arrays; the recursive case iterates over sub-arrays and recurses
  • C++17 if constexpr simplifies the recursion by eliminating SFINAE
  • Track indices with a vector<size_t> passed through the recursion
  • For flat iteration without index awareness, use arr.data() and arr.num_elements()
  • The same pattern extends to accumulate, transform, and other algorithmic patterns

Course illustration
Course illustration

All Rights Reserved.