Многомерный массив с динамическими экстентами (без необработанных указателей) - PullRequest
0 голосов
/ 28 ноября 2018

Логическое обоснование этого массива состоит в том, чтобы эмулировать матрицу пикселей 2D / 3D.

После большого количества исследований и чтения я считаю, что Boost.MultiArray может пригодиться для этого.Тем не менее, мне все еще нужно создать аккуратную обертку поверх нее, чтобы сделать возможным менее подробное кодирование.

В конечном итоге я хочу добиться следующего:

PixMat<u8, 3, {2, 4, 3}> pixMat;

или

PixMat<u8, 3> pixMat(2,3,4);

, что в основном создаст матрицу 2x4x3 значений u8.

Я до сих пор придумал:

template <typename T, int Dims>
class PixMat {
public:
    typedef typename boost::multi_array<T, Dims> PixMatType;
    typedef typename PixMatType::index PixMatTypeIdx;
    PixMat(int dim1Ext, int dim2Ext) : pixMat(PixMatType(boost::extents[dim1Ext][dim2Ext])) {}
    PixMat(int dim1Ext, int dim2Ext, int dim3Ext) : pixMat(PixMatType(boost::extents[dim1Ext][dim2Ext][dim3Ext])) {}

private:
    PixMatType pixMat;
};

template <typename T>
class Pix2DMat : PixMat<T, 2> {
public:
    Pix2DMat(int dim1Ext, int dim2Ext) : PixMat<DataType, 2>(dim1Ext, dim2Ext) {}
};

template <typename T>
class Pix3DMat : PixMat<T, 3> {
public:
    Pix3DMat(int dim1Ext, int dim2Ext, int dim3Ext) : PixMat<DataType, 3>(dim1Ext, dim2Ext, dim3Ext) {}
};

Я не слишком заинтересован в этом решении.Как правило, матрица не будет двухмерной или трехмерной, но, тем не менее, я бы хотел более общее решение.

Вопросы:

  1. Есть ли способ предоставить экстенты?измерений в качестве аргументов шаблона, а не через C-TOR?
  2. Есть ли лучший способ, чем наследование, для достижения этого, например, специализация шаблонов, шаблоны с переменными параметрами?Но тогда как бороться с тем, чтобы не дублировать typedef s для повышения повсюду?

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

Если я вас правильно понял, вы хотите измерение времени компиляции, но экстенты времени выполнения.

Я бы использовал такой дизайн:

template <typename T,std::size_t Dim>
class mdvector
{
private:
  std::vector<T> Data;
  std::array<std::size_t,Dim> Extents;

private:
  std::size_t Offset(std::size_t const Sum) const
    { return Sum; }

  template <typename... IndexType>
  std::size_t Offset(std::size_t const Sum,std::size_t const Index,IndexType const... Indices) const
    { return Offset(Sum*Extents[Dim-sizeof...(Indices)-1u]+Index,Indices...); }

public:
  template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
  mdvector(IndexType const... Indices):
    Data((... * Indices)), // c++17 fold expression
    Extents{Indices...} {}

  template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
  T const &operator()(IndexType const... Indices) const
    { return Data[Offset(0u,Indices...)]; }

  template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
  T &operator()(IndexType const... Indices)
    { return Data[Offset(0u,Indices...)]; }
};

Данные хранятся вstd::vector, в то время как экстенты составляют std::array.

Поскольку я предполагаю, что вам нужен многомерный доступ, я использовал данное отображение через рекурсивную функцию Offset.У вас есть полная свобода в этом отношении.

Вот пример использования:

int main()
{
mdvector<double,3u> myvec(2u,3u,4u);

for (int i= 0; i<2; ++i)
  for (int j= 0; j<3; ++j)
    for (int k= 0; k<4; ++k)
      myvec(i,j,k)= i;

for (int k= 0; k<4; ++k)
  {
  for (int i= 0; i<2; ++i)
    {
    for (int j= 0; j<3; ++j)
      std::cout << myvec(i,j,k) << "\t";
    std::cout << "\n";
    }
  std::cout << "\n";
  }
}

Поскольку экстенты являются динамическими, вы можете изменить их во время выполнения, например:

template <typename T,std::size_t Dim>
template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
void mdvector<T,Dim>::Resize(IndexType const... Indices)
  { Data.resize((... * Indices)); Extents= {Indices...}; }

Если, с другой стороны, вы предпочитаете, чтобы экстенты были параметрами шаблона, они должны оставаться фиксированными во время выполнения.В этом случае вы бы включили их в объявление класса:

template <typename T,std::size_t... Indices>
class mdvector { /* ... */};

, но реализация была бы почти такой же.Обратите внимание, что указание размера не требуется, поскольку вы можете получить его с помощью sizeof...(Indices).


. Выражение сгиба в приведенном выше коде не является обязательным.Эквивалентный результат достигается с помощью:

static constexpr std::size_t Product()
  { return 1u; }

template <typename... IndexType>
static constexpr std::size_t Product(std::size_t const Index,IndexType const... Indices)
  { return Index*Product(Indices...); }

template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
mdvector(IndexType const... Indices):
  Data(Product(Indices...)), Extents{Indices...} {}
0 голосов
/ 28 ноября 2018

Вот несколько методов, которые, я думаю, вы могли бы использовать:

Аргумент конструктора Variadic

Вместо того, чтобы иметь отдельный конструктор для каждого возможного измерения, вы могли бы использоватьвариационные методы аргументов для создания универсального N-мерного конструктора.Что-то, что вам здесь нравится: boost::extents не требуется для аргумента конструктора, но вместо этого все, что соответствует требованиям Collection .Одним из примеров является простой STL или массив Boost:

template <typename... DimSizes>
PixMat(DimSizes&&... sizes)
 : pixMat(boost::array<std::size_t, sizeof...(DimSizes)>{{ static_cast<size_t>(sizes)...}}) {
}

Это не самая отработанная реализация;в частности, он не предъявляет особых требований к DimSizes, который в действительности должен быть одного целого типа без знака (см. этот вопрос для возможных улучшений).Также (для простоты) идеальная пересылка не реализована, но это, вероятно, просто требует переноса sizes с std::forward<DimSizes>(sizes) в конструкторе.Вы можете проконсультироваться с этим сообщением о потоке стека для возможных альтернативных реализаций.

Статическое утверждение / SFINAE

Ваш базовый класс шаблона имеет 2D-конструктор и 3D-конструктор--- или, если вы следуете вышеизложенному, шаблонный N-мерный конструктор --- независимо от значения фактического параметра шаблона.Вы можете использовать статическое утверждение или SFINAE, чтобы можно было компилировать только размерный конструктор Dims-D.Это преобразует ошибки времени выполнения в ошибки компиляции:

template <typename... DimSizes>
PixMat(DimSizes&&... sizes)
 : pixMat(boost::array<std::size_t, sizeof...(DimSizes)>{{ static_cast<size_t>(sizes)...}}) {
    static_assert(sizeof...(DimSizes) == Dims);
}

Размеры размеров в качестве шаблонов

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

Другие библиотеки

Возможно, вы захотите взглянуть на Эйгена, например.Это делает много вышеупомянутой тяжелой работы.

...