7

I have a n-dimensional Boost.MultiArray I initialize as follows:

const int n=3, size=4; //# of dimensions and size of one dimension
boost::multi_array<char,n> arr;
boost::array<size_t,n> extents; //size of each dimension
extents.assign(size); //assign size to each dimension -> {{4, 4, 4}}
arr.resize(extents);

So I have 4 lines of code to get the MultiArray, but I'd like to do it in one line. Is there any simple way to generate an MultiArray with n dimensions each having size length (so I can write arr(samevaluearray(n,size))) or did I miss a handy constructor for MultiArray?

Edit: It should work without depending on a certain value of n, i.e. arr({{size,size}} would only work for n=2.

Since it may not be clear: boost::multi_array<char,n>(boost::extents[4][4][4]) correctly initializes a 4x4x4-array, but every time n is changed in the sourcecode, every initialization has to be updated by hand, so it's not an option.

tstenner
  • 9,119
  • 10
  • 48
  • 83

3 Answers3

8

You can encapsulate the creation of the array into an helper function:

template <typename T, size_t N>
boost::multi_array<T, N> make_regular_matrix(const size_t m)
{
    boost::multi_array<T, N> arr;
    boost::array<size_t, N> extents;
    extents.assign(m);
    arr.resize(extents);

    return arr;
}

const int n = 3;
int size = 4; // Can be const as well, but this is not mandatory

auto arr = make_regular_matrix<char, n>(size);

If you can't use auto, you'll have to duplicate the template parameters:

boost::multi_array<char, n> arr = make_regular_matrix<char, n>(size);

The make_regular_matrix function could be shortened to use std::vector, as you did in your answer; I don't know if this implementation would be better. The aim of the helper function is to hide the creation of the array, but other versions could be written, for example to initialize the array elements with a given value:

template <size_t N, typename T> //switched order for deduction
boost::multi_array<T, N> make_regular_matrix(const size_t m, const T & value)
{
     boost::multi_array<T, N> arr(std::vector<size_t>(n, m));

     std::fill(arr.data(), arr.data() + arr.num_elements(), value);

     return arr;
}

auto arr = make_regular_matrix<4>(3, 'z'); //creates a 3x3x3x3 matrix
                                           //filled with 'z's
Luc Touraille
  • 72,907
  • 15
  • 82
  • 134
5

Turns out, std::vector has a constructor, that constructs a vector with a constant value repeated n times, so a possible solution looks like this:

const int n=2, size=4; //# of dimensions and size of one dimension
boost::multi_array<char,n> arr(std::vector<size_t>(n,size));

This initializes a n-dimensional multi_array with each dimension's size set to size.

Luc Touraille
  • 72,907
  • 15
  • 82
  • 134
tstenner
  • 9,119
  • 10
  • 48
  • 83
  • Right, you didn't solve the problem + forgot to specify array type that you are creating. Not to mention a huuuuge overhead of creating a vector :-D –  Jan 15 '12 at 17:45
  • @VladLazarenko Forgot to change < to < so the type wasn't visible. It works, but feel free to explain why it wouldn't work. – tstenner Jan 15 '12 at 17:50
  • Well, it would work to some extent. But I am afraid you will bump into a problem later and will have to change your code all over the place. This is because I have a feeling that you don't really understand the role of `const int n` in this place - it is not a variable, but a constant compile-time expression. Now, try to write a function that multiplies two arrays, accepts `N`, `size` and two arrays as its arguments. Your code will quickly fail because `n` will not be known by compiler at compile-time. –  Jan 15 '12 at 17:58
  • Multiplying 2 arrays is trivial, as I'd just use a 1D-view of both arrays, but since you're not convinced I know what I'm doing, here's a function to iterate over a n-dimensional array: `template void iterate(boost::multi_array& arr, boost::array index){for(int i=0;i(arr,index);}} template<> void iterate(boost::multi_array& arr, boost::array index){ arr(index)++;}` – tstenner Jan 15 '12 at 18:17
  • This is a terrible function. The amount of code generated by the compiler will blow its size into three-dimensions... You also forgot to specify `n` as a template argument along with `level`.. But, whatever... –  Jan 15 '12 at 18:30
  • @VladLazarenko I could specify it as a template parameter, but since it's a global constant I don't need to, and the amount of generated code isn't much larger than writing everything by hand. – tstenner Jan 15 '12 at 18:34
  • So, from what you write, why this doesn't work : boost::multi_array arr((array).assign(size)); – AxFab Jan 18 '12 at 22:34
  • @aesgar array.assign returns void, so this won't work. – tstenner Jan 19 '12 at 09:16
3

From the Boost Multi-Array documentation, yes, you can initialize it one line:

typedef boost::multi_array<double, 3> array_type;
typedef array_type::index index;
array_type A(boost::extents[3][4][2]);

The typedefs are for readability, one can just as easily do for your example:

boost::multi_array<int, 2> arr(boost::extents[2][4]);
Nathan Ernst
  • 4,300
  • 21
  • 37
  • This works, but only for n=2. For every other n, the code needs to be adjusted manually. – tstenner Jan 11 '12 at 18:50
  • 2
    @tstenner: Erm... what else did you expect? You have to adjust *some* code atleast. What did you want to do instead to create a new `multi_array`? – Xeo Jan 15 '12 at 17:51
  • Actually, you have came up with the exactly what OP was looking for, so +1. For some reason he still haven't got it and answered his own question, saying exactly the same thing... Sad. –  Jan 15 '12 at 21:35
  • Actually, it generates a 3x4x2-array, the second version generates a 2x4-array. If I wanted to edit the code every time I wanted to change the number of dimensions, I would have done so myself. – tstenner Jan 16 '12 at 15:23