Add util::array - an N-dim array with variable bounds

This commit is contained in:
Nikita Lisitsa 2020-09-10 11:05:19 +03:00
parent 195867ac31
commit 30448d2ec1

View file

@ -0,0 +1,301 @@
#pragma once
#include <psemek/util/assert.hpp>
#include <memory>
#include <array>
#include <type_traits>
namespace psemek::util
{
template <typename T, std::size_t N>
struct array
{
static_assert(N >= 1);
using value_type = T;
static constexpr std::size_t dimension = N;
using dims_type = std::array<std::size_t, N>;
array();
array(dims_type const & dims);
array(dims_type const & dims, T const & value);
array(dims_type const & dims, std::unique_ptr<T[]> data);
array(array &&);
array(array const &) = delete;
array & operator = (array &&);
array & operator = (array const &) = delete;
std::size_t dim(std::size_t i) const
{
assert(i < N);
return dims_[i];
}
std::size_t size() const;
std::size_t width() const
{
static_assert(N >= 1);
return dims_[0];
}
std::size_t height()
{
static_assert(N >= 2);
return dims_[1];
}
std::size_t depth()
{
static_assert(N >= 3);
return dims_[2];
}
T & operator()(dims_type const & index);
T const & operator()(dims_type const & index) const;
template <typename ... Ixs>
T & operator()(Ixs ... ixs);
template <typename ... Ixs>
T const & operator()(Ixs ... ixs) const;
array copy() const;
void resize(dims_type const & dims);
void resize(dims_type const & dims, T const & value);
T * data() { return data_.get(); }
T const * data() const { return data_.get(); }
T * begin() { return data_.get(); }
T const * begin() const { return data_.get(); }
T const * cbegin() const { return data_.get(); }
T * end() { return data_.get() + size(); }
T const * end() const { return data_.get() + size(); }
T const * cend() const { return data_.get() + size(); }
std::unique_ptr<T[]> release();
bool empty() const;
void clear();
void fill(T const & value);
private:
std::unique_ptr<T[]> data_;
std::array<std::size_t, N> dims_;
void resize_impl(std::unique_ptr<T[]> data, dims_type const & dims);
};
namespace detail
{
template <std::size_t N>
std::size_t product(std::array<std::size_t, N> const & dims)
{
std::size_t r = 1;
for (auto const & d : dims)
r *= d;
return r;
}
template <std::size_t N>
bool empty(std::array<std::size_t, N> const & dims)
{
for (auto const & d : dims)
if (d == 0) return true;
return false;
}
template <std::size_t N>
std::size_t index(std::array<std::size_t, N> const & i, std::array<std::size_t, N> const & dims)
{
std::size_t r = 0;
for (std::size_t d = N; d --> 0;)
{
assert(i[d] < dims[d]);
r = i[d] + r * dims[d];
}
return r;
}
template <std::size_t N>
bool next(std::array<std::size_t, N> const & i, std::array<std::size_t, N> const & dims)
{
for (std::size_t d = 0; d < N; ++d)
{
++i[d];
if (i[d] < dims[d])
return true;
i[d] = 0;
}
return false;
}
}
template <typename T, std::size_t N>
array<T, N>::array()
{
dims_.fill(0);
}
template <typename T, std::size_t N>
array<T, N>::array(dims_type const & dims)
: dims_{dims}
{
data_.reset(new T[size()]);
}
template <typename T, std::size_t N>
array<T, N>::array(dims_type const & dims, T const & value)
: array(dims)
{
fill(value);
}
template <typename T, std::size_t N>
array<T, N>::array(dims_type const & dims, std::unique_ptr<T[]> data)
: dims_{dims}
{
data_ = std::move(data);
}
template <typename T, std::size_t N>
array<T, N>::array(array && other)
: data_{std::move(other.data_)}
, dims_{other.dims_}
{
other.dims_.fill(0);
}
template <typename T, std::size_t N>
array<T, N> & array<T, N>::operator = (array && other)
{
if (this == &other)
return *this;
data_ = std::move(other.data_);
dims_ = other.dims_;
other.dims_.fill(0);
return *this;
}
template <typename T, std::size_t N>
std::size_t array<T, N>::size() const
{
return detail::product(dims_);
}
template <typename T, std::size_t N>
T & array<T, N>::operator()(dims_type const & index)
{
return data_[detail::index(index, dims_)];
}
template <typename T, std::size_t N>
T const & array<T, N>::operator()(dims_type const & index) const
{
return data_[detail::index(index, dims_)];
}
template <typename T, std::size_t N>
template <typename ... Ixs>
T & array<T, N>::operator()(Ixs ... ixs)
{
dims_type dims{ixs...};
return (*this)(dims);
}
template <typename T, std::size_t N>
template <typename ... Ixs>
T const & array<T, N>::operator()(Ixs ... ixs) const
{
dims_type dims{ixs...};
return (*this)(dims);
}
template <typename T, std::size_t N>
array<T, N> array<T, N>::copy() const
{
std::unique_ptr<T[]> data(new T[size()]);
std::copy(begin(), end(), data.get());
return array{dims_, std::move(data)};
}
template <typename T, std::size_t N>
void array<T, N>::resize(dims_type const & dims)
{
std::unique_ptr<T[]> data(new T[detail::product(dims)]);
resize_impl(std::move(data), dims);
}
template <typename T, std::size_t N>
void array<T, N>::resize(dims_type const & dims, T const & value)
{
auto const size = detail::product(dims);
std::unique_ptr<T[]> data(new T[size]);
std::fill(data.get(), data.get() + size, value);
resize_impl(std::move(data), dims);
}
template <typename T, std::size_t N>
std::unique_ptr<T[]> array<T, N>::release()
{
dims_.fill(0);
return data_.release();
}
template <typename T, std::size_t N>
bool array<T, N>::empty() const
{
return detail::empty(dims_);
}
template <typename T, std::size_t N>
void array<T, N>::clear()
{
data_.reset();
dims_.fill(0);
}
template <typename T, std::size_t N>
void array<T, N>::fill(T const & value)
{
std::fill(data_.get(), data_.get() + size(), value);
}
template <typename T, std::size_t N>
void array<T, N>::resize_impl(std::unique_ptr<T[]> data, dims_type const & dims)
{
std::array<std::size_t, N> min_dim;
for (std::size_t d = 0; d < N; ++d)
min_dim[d] = std::min(dims_[d], dims[d]);
if (!detail::empty(min_dim))
{
std::array<std::size_t, N> i;
i.fill(0);
do
{
data[detail::index(i, dims)] = (*this)(i);
} while (detail::next(i, min_dim));
}
dims_ = dims;
data_ = std::move(data);
}
}