Сообщение [bike] Многомерный динамический массив от 15.10.2020 15:15
Изменено 15.10.2020 15:23 Bill Baklushi
[bike] Многомерный динамический массив
Приветствую.
Вчера прошла не очень замеченной крайне калорийная тема
https://rsdn.org/forum/prj/7852771.all
Юный гений пытается продавать крайне кривой велосипед поработе с динамическими массивами.
Задался вопросом, а как такое сделать на нормальных плюсах...
Двумерный вектор я изобретал еще в студенческие, досовские времена: первый operator [] возвращает указатель на строку элементов, второй (не переопределенный) — ссылку на сам элемент.
Вектор векторов тоже решение, но сомнительное по эффективности.
Помню, когда-то изобретал массив с фиксированным числом []. Последний оператор [] возвращает ссылку на элемент, непоследний возвращает шаблонизированный целым значением объект-индекс.
Здесь несколько изменил идею. Все индексы одного и того же типа. operator [] добавляет значение во внутренний контейнер объекта-индекса.
Преобразование индекса в тип-элемент приводит к вычислению плоского индекса в контейнере и выборке соответствующего элемента.
Массив содежит два вектора: описание размерностей dimensions_ и данные data_.
Шаблонизировать тип элементов не стал, это легко допилить.
Применение универсальных ссылок && не сделал — немного плаваю в теме. То же и с constexpr.
Конструктивная критика, смешочки и тухлые помидоры приветствуется.
UPD1: На реализацию ушло около 3 часов, в основном на приведение к функциональному стилю и отладку.
Код:
Вчера прошла не очень замеченной крайне калорийная тема
https://rsdn.org/forum/prj/7852771.all
Автор: Gaxis
Дата: 13.10.20
Дата: 13.10.20
Юный гений пытается продавать крайне кривой велосипед поработе с динамическими массивами.
Задался вопросом, а как такое сделать на нормальных плюсах...
Двумерный вектор я изобретал еще в студенческие, досовские времена: первый operator [] возвращает указатель на строку элементов, второй (не переопределенный) — ссылку на сам элемент.
Вектор векторов тоже решение, но сомнительное по эффективности.
Помню, когда-то изобретал массив с фиксированным числом []. Последний оператор [] возвращает ссылку на элемент, непоследний возвращает шаблонизированный целым значением объект-индекс.
Здесь несколько изменил идею. Все индексы одного и того же типа. operator [] добавляет значение во внутренний контейнер объекта-индекса.
Преобразование индекса в тип-элемент приводит к вычислению плоского индекса в контейнере и выборке соответствующего элемента.
Массив содежит два вектора: описание размерностей dimensions_ и данные data_.
Шаблонизировать тип элементов не стал, это легко допилить.
Применение универсальных ссылок && не сделал — немного плаваю в теме. То же и с constexpr.
Конструктивная критика
UPD1: На реализацию ушло около 3 часов, в основном на приведение к функциональному стилю и отладку.
Код:
#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';
}
}[bike] Многомерный динамический массив
Приветствую.
Вчера прошла не очень замеченной крайне калорийная тема
https://rsdn.org/forum/prj/7852771.all
Юный гений пытается продавать крайне кривой велосипед поработе с динамическими массивами.
Задался вопросом, а как такое сделать на нормальных плюсах...
Двумерный вектор я изобретал еще в студенческие, досовские времена: первый operator [] возвращает указатель на строку элементов, второй (не переопределенный) — ссылку на сам элемент.
Вектор векторов тоже решение, но сомнительное по эффективности.
Помню, когда-то изобретал массив с фиксированным числом []. Последний оператор [] возвращает ссылку на элемент, непоследний возвращает шаблонизированный целым значением объект-индекс.
Здесь несколько изменил идею. Все индексы одного и того же типа. operator [] добавляет значение во внутренний контейнер объекта-индекса.
Преобразование индекса в тип-элемент приводит к вычислению плоского индекса в контейнере и выборке соответствующего элемента.
Массив содежит два вектора: описание размерностей dimensions_ и данные data_.
Шаблонизировать тип элементов не стал, это легко допилить.
Применение универсальных ссылок && не сделал — немного плаваю в теме. То же и с constexpr.
Конструктивная критика, смешочки и тухлые помидоры приветствуется.
UPD1: На реализацию ушло около 3 часов, в основном на приведение к функциональному стилю и отладку.
UPD2: Продавать я это не собираюсь.
Думаю хороший сишник при необходимости переизобретет это в кратчайшие сроки.
Код:
Вчера прошла не очень замеченной крайне калорийная тема
https://rsdn.org/forum/prj/7852771.all
Автор: Gaxis
Дата: 13.10.20
Дата: 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';
}
}