Как лучше всего заполнить boost::multi_array из списка инициализаторов?

Я хотел бы инициализировать boost::multi_array, встроенный в какой-то код. Но я не думаю, что boost::multi_array поддерживает инициализацию из списка инициализаторов. Вот что у меня есть до сих пор:

// 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));

Мне нравится, что я могу компактно выразить содержимое матрицы, используя список инициализаторов в фигурных скобках. Но мне не нравится «memcpy», и мне не нравится использование одноразового примитивного массива. Есть ли более приятный способ заполнить мой boost::multi_array из читаемого встроенного набора значений в коде?


person Christopher Bruns    schedule 16.01.2017    source источник
comment
comment
@Tas вопрос, который вы связали, связан с созданием multi_array с определенными РАЗМЕРАМИ. Мой вопрос касается создания multi_array с определенным СОДЕРЖАНИЕМ.   -  person Christopher Bruns    schedule 17.01.2017
comment
@StephanLechner Спасибо за эту ссылку. Тот факт, что официальная документация по бустингу содержит пример с использованием memcpy, подразумевает, что мое решение может быть лучшим. Я не уверен насчет origin() и data().   -  person Christopher Bruns    schedule 17.01.2017
comment
Вы против написания собственной функции (также известной как create_multi_array<uint8_t, 2>(extents[4][8], { { 1, 1, ... }, ... }))? К сожалению, Boost.MultiArray не был обновлен с поддержкой initializer_list.   -  person Travis Gockel    schedule 17.01.2017
comment
Все еще пытаюсь выяснить, следует ли использовать data() или origin().   -  person Stephan Lechner    schedule 17.01.2017
comment
После дальнейшего изучения выяснилось, что origin() более подходит, чем data(), хотя обычно они имеют одно и то же значение.   -  person Christopher Bruns    schedule 17.01.2017
comment
Хм, я также провел некоторые исследования, которые позволили мне использовать data(). Я добавил эти мысли к ответу; может быть, вы можете просмотреть эти мысли.   -  person Stephan Lechner    schedule 17.01.2017


Ответы (1)


В следующем примере из официальной документации по Boost, касающейся multi_array, тоже используется memcpy, хотя и в сочетании с origin(). Так что, кажется, все в порядке, используя его:

#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';
} 

Что касается разницы между origin() и data(), то multiarray справочное руководство определяет следующее:

элемент* данные(); Это возвращает указатель на начало непрерывного блока, содержащего данные массива. Если все измерения массива имеют нулевой индекс и хранятся в порядке возрастания, это эквивалентно функции origin().

элемент* происхождение(); Это возвращает исходный элемент multi_array.

Таким образом, кажется, что при использовании data() и origin() вместе с memcpy следует учитывать две вещи, если массив содержит измерения, которые не индексированы 0 или не в порядке возрастания:

Во-первых, origin() может не указывать на начало непрерывного блока памяти, используемого массивом. Следовательно, копирование памяти размером с мультимассив в это место может превысить зарезервированный блок памяти.

Во-вторых, с другой стороны, копирование блока памяти по адресу data() может привести к структуре памяти, в которой индексы массива, доступ к которым осуществляется через мультимассив, не соответствуют индексам блока памяти, скопированному во внутренний буфер данных массива.

Поэтому мне кажется, что использование memcpy для (предварительного) заполнения мультимассива следует использовать с осторожностью и в идеале с индексами на основе 0 и в порядке возрастания.

person Stephan Lechner    schedule 16.01.2017
comment
Если внутренний порядок массива не восходящий, ни data(), ни origin() не спасут вас от того, что здесь сделает memcpy(). В этом случае вам понадобятся циклы или итераторы. В противном случае я предпочитаю origin(), потому что я ожидаю, что элемент [0][0] моего временного примитива окажется в позиции [0][0] multi_array. Внезапно я думаю, что, может быть, мне действительно следует использовать std::copy здесь, а не memcpy. - person Christopher Bruns; 17.01.2017