[bike] Многомерный динамический массив
От: Свободу rg45! СССР  
Дата: 15.10.20 15:15
Оценка:
Приветствую.

Вчера прошла не очень замеченной крайне калорийная тема
https://rsdn.org/forum/prj/7852771.all
Автор: Gaxis
Дата: 13.10.20


Юный гений пытается продавать крайне кривой велосипед поработе с динамическими массивами.

Задался вопросом, а как такое сделать на нормальных плюсах...
Двумерный вектор я изобретал еще в студенческие, досовские времена: первый operator [] возвращает указатель на строку элементов, второй (не переопределенный) — ссылку на сам элемент.

Вектор векторов тоже решение, но сомнительное по эффективности.

Помню, когда-то изобретал массив с фиксированным числом []. Последний оператор [] возвращает ссылку на элемент, непоследний возвращает шаблонизированный целым значением объект-индекс.

Здесь несколько изменил идею. Все индексы одного и того же типа. operator [] добавляет значение во внутренний контейнер объекта-индекса.
Преобразование индекса в тип-элемент приводит к вычислению плоского индекса в контейнере и выборке соответствующего элемента.

Массив содежит два вектора: описание размерностей dimensions_ и данные data_.

Шаблонизировать тип элементов не стал, это легко допилить.

Применение универсальных ссылок && не сделал — немного плаваю в теме. То же и с constexpr.

Конструктивная критика , смешочки и тухлые помидоры приветствуется.

UPD1: На реализацию ушло около 3 часов, в основном на приведение к функциональному стилю и отладку.
UPD2: Продавать я это не собираюсь. Думаю хороший сишник при необходимости переизобретет это в кратчайшие сроки.

Код:
#include <iostream>

#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>

#include <stdexcept>
#include <initializer_list>
    

namespace baklushi
{

typedef double BASE_TYPE;

class multidim_dynarray;

class multiindex
{
public:  

  multiindex operator[] (int i)
  {
    multiindex new_idx(*this);
    new_idx.index_.push_back(i);
    return new_idx;
  }

  operator BASE_TYPE &();
  BASE_TYPE operator = (BASE_TYPE val);
  
private:
  std::vector<size_t> index_;
  multidim_dynarray &container_;  

  multiindex(const multiindex &src):
    container_(src.container_),
    index_(src.index_)
  {
  } 

  multiindex(multidim_dynarray &arr):
    container_(arr)
  {
  }

  friend class multidim_dynarray;
};


class multidim_dynarray
{
public:  
  multiindex operator [](int i)
  {
    return multiindex(*this)[i];  
  }

  multidim_dynarray(std::initializer_list<size_t> dim);
  multidim_dynarray(std::initializer_list<size_t> dim,
    std::initializer_list<BASE_TYPE> data);

  void redim(std::initializer_list<size_t> lst);
  std::vector<size_t> dimensions(){ return dimensions_; }
private:
  std::vector<size_t> dimensions_;
  std::vector<BASE_TYPE> data_;

  friend class multiindex;
};


multidim_dynarray::multidim_dynarray(std::initializer_list<size_t> lst):
  dimensions_(lst)
{
  size_t total_el =
    std::accumulate(
      dimensions_.begin(),
      dimensions_.end(),
      1,
      std::multiplies<size_t>());
  data_.resize(total_el);
}

multidim_dynarray::multidim_dynarray(
  std::initializer_list<size_t> dim,
  std::initializer_list<BASE_TYPE> data):
dimensions_(dim)
{
  size_t total_el =
    std::accumulate(
      dimensions_.begin(),
      dimensions_.end(),
      1,
      std::multiplies<size_t>());
  data_.resize(total_el);

  std::copy(
    std::begin(data),
    std::min(std::end(data), std::begin(data)+total_el),
    data_.begin());
}

void multidim_dynarray::redim(std::initializer_list<size_t> dim)
{
  dimensions_.assign(dim);

  size_t total_el =
    std::accumulate(
      dimensions_.begin(),
      dimensions_.end(),
      1,
      std::multiplies<size_t>());
  data_.resize(total_el);
}

multiindex::operator BASE_TYPE &()
{
  if(!index_.size() || index_.size() !=
    container_.dimensions_.size())
  {
    throw std::out_of_range("Invalid indirection");
  }

  if(!std::equal(
    index_.begin(),
    index_.end(),
    container_.dimensions_.begin(),
    std::less<size_t>() ))
  {
    throw std::out_of_range("Out of range");
  }
  
  std::vector<int> partial_product(index_.size());
  
  std::partial_sum(
    container_.dimensions_.rbegin(),
    container_.dimensions_.rend()-1,
    partial_product.rbegin()+1,
    std::multiplies<int>());
  partial_product.back() = 1;

  std::transform(
    index_.begin(),
    index_.end(),
    partial_product.begin(),
    partial_product.begin(),
    std::multiplies<size_t>());

  size_t flat_idx = std::accumulate(
    partial_product.begin(),
    partial_product.end(),
    0);

   return container_.data_[flat_idx];
}

BASE_TYPE multiindex::operator = (BASE_TYPE val)
{
  return this->operator BASE_TYPE &() = val;
}

}; //namespace

int main()
{
  baklushi::multidim_dynarray arr({3,4,5},
  {
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
    10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
    20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
    30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
    40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
    50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
    60, 61, 62, 63, 64, 65, 66, 67, 68, 69  //redundant line, will be ignored

  } );

  try
  {
    arr[1][2][1] += 5.0;

    std::cout << arr[1][2][3] << std::endl;
  }
  catch(const std::out_of_range &e)
  {
    std::cerr << "Out of range exception raised: " << e.what() << '\n';
  }
}
Отредактировано 15.10.2020 15:23 Bill Baklushi . Предыдущая версия . Еще …
Отредактировано 15.10.2020 15:18 Bill Baklushi . Предыдущая версия .
Re: [bike] Многомерный динамический массив
От: Muxa  
Дата: 15.10.20 16:37
Оценка:
СR>Вчера прошла не очень замеченной крайне калорийная тема
СR>https://rsdn.org/forum/prj/7852771.all
Автор: Gaxis
Дата: 13.10.20


Этого товарища надо нашему русскому парню представить.
Они же горы вместе свернут.
Re: [bike] Многомерный динамический массив
От: PM  
Дата: 15.10.20 17:48
Оценка:
Здравствуйте, Свободу rg45!, Вы писали:

СR>Вчера прошла не очень замеченной крайне калорийная тема

СR>https://rsdn.org/forum/prj/7852771.all
Автор: Gaxis
Дата: 13.10.20


СR>Юный гений пытается продавать крайне кривой велосипед поработе с динамическими массивами.


СR>Задался вопросом, а как такое сделать на нормальных плюсах...

СR>Двумерный вектор я изобретал еще в студенческие, досовские времена: первый operator [] возвращает указатель на строку элементов, второй (не переопределенный) — ссылку на сам элемент.

Ну такое только для самообразования написать, если не знать про <valarray> или какую-нибудь glm

Также есть предложение mdspan в стандарт, p0009 и даже реализация https://github.com/kokkos/mdspan

Один самых интересных докладов был про это на CppCon 2020, на мой взгляд
https://youtu.be/wHu6TGela6A
Re[2]: [bike] Многомерный динамический массив
От: Свободу rg45! СССР  
Дата: 15.10.20 18:13
Оценка:
PM:

PM>Ну такое только для самообразования написать, если не знать про <valarray> или какую-нибудь glm


PM>Также есть предложение mdspan в стандарт, p0009 и даже реализация https://github.com/kokkos/mdspan


Это тема-этюд про массив, поддерживающий несколько последовательных операций [].
У valarray этого нет, про остальное не знаю, посмотрю.

PM>Один самых интересных докладов был про это на CppCon 2020, на мой взгляд

PM>https://youtu.be/wHu6TGela6A

Посмотрю.
Re: [bike] Многомерный динамический массив
От: Vzhyk2  
Дата: 27.10.20 07:19
Оценка:
Здравствуйте, Свободу rg45!, Вы писали:

СR>Приветствую.


СR>Вчера прошла не очень замеченной крайне калорийная тема

armadillo, eigen прилично написаны и всё сделано за вас для C++.
для питона numpy отличен.
Re: [bike] Многомерный динамический массив
От: σ  
Дата: 27.10.20 16:02
Оценка:
Массивы с определяемым в рантайме числом размерностей — не нужны.

А доступ должен быть через operator(), а не operator[].
Re[2]: [bike] Многомерный динамический массив
От: Свободу rg45! СССР  
Дата: 27.10.20 16:42
Оценка:
σ:

σ>Массивы с определяемым в рантайме числом размерностей — не нужны.

Почему?
σ>А доступ должен быть через operator(), а не operator[].
Почему?
Re: [bike] Многомерный динамический массив
От: кт  
Дата: 30.10.20 09:26
Оценка:
подходы к реализации динамических массивов могут быть самые разные, например, даже так:

с помощью изменения кода
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.