I'd like to initialize a boost::multi_array inline in some code. But I don't think the boost::multi_array supports initialization from an initializer list. Here's what I have so far:
// First create a primitive array, which can be directly initialized
uint8_t field_primitive[4][8] = {
{ 1,1,1,1,1,1,1,1 },
{ 1,2,1,2,1,2,1,2 },
{ 1,1,2,2,2,2,2,2 },
{ 1,2,2,2,2,2,2,2 }
};
// Create the boost::multi_array I actually want to use
boost::multi_array<uint8_t, 2> field(boost::extents[4][8]);
// Compact but yucky approach to copying the primitive array contents into the multi_array.
memcpy(field.data(), field_primitive, field.num_elements() * sizeof(uint8_t));
I like that I can compactly express the matrix contents using the curly brace intializer list. But I don't like the "memcpy" and I don't like the use of a throwaway primitive array. Is there a nicer way to populate my boost::multi_array from a readable inline set of values in the code?
The following example from the official boost documentation concerning multi_arrayuses memcpy
, too, though in combination with origin()
. So it seems to be OK using it:
#include <boost/multi_array.hpp>
#include <algorithm>
#include <iostream>
#include <cstring>
int main()
{
boost::multi_array<char, 2> a{boost::extents[2][6]};
typedef boost::multi_array<char, 2>::array_view<1>::type array_view;
typedef boost::multi_array_types::index_range range;
array_view view = a[boost::indices[0][range{0, 5}]];
std::memcpy(view.origin(), "tsooB", 6);
std::reverse(view.begin(), view.end());
std::cout << view.origin() << '\n';
boost::multi_array<char, 2>::reference subarray = a[1];
std::memcpy(subarray.origin(), "C++", 4);
std::cout << subarray.origin() << '\n';
}
Concerning the difference between origin()
and data()
, the multiarray reference manual defines the following:
element* data(); 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().
element* origin(); This returns the origin element of the multi_array.
So there seem to be two things to consider when using data()
and origin()
together with memcpy
, ff the array contains dimensions that are not 0-indexed or not in ascending order:
First, origin()
might not point to the start of the continuous memory block used by the array. Hence, copying memory in the size of the multiarray to this location might exceed the reserved memory block.
Second, on the other hand, copying a memory block to data()
's address might lead to a memory layout where array indices as accessed through multiarray do not correspond to the indizes of the memory block copied into the array's internal data buffer.
So it seems to me that using memcpy
to (pre)-fill a multiarray should be used with care and ideally with 0-based indices and ascending order.