| @@ -15,127 +15,180 @@ | |||
| // | |||
| // The narray class | |||
| // ———————————————— | |||
| // An N-dimensional row-major array template class | |||
| // An n-dimensional row-major array template class | |||
| // | |||
| #include <lol/vector> // lol::vec_t | |||
| #include <vector> // std::vector | |||
| #include <utility> // std::index_sequence | |||
| namespace lol | |||
| { | |||
| template<typename T, size_t N> | |||
| class [[nodiscard]] narray | |||
| template<typename T, size_t N, typename container_type> | |||
| class [[nodiscard]] narray_base | |||
| { | |||
| public: | |||
| typedef T value_type; | |||
| inline narray() = default; | |||
| inline ~narray() = default; | |||
| // Construct array with args: size1, size2, ..., sizen | |||
| template<typename... I> inline narray(I... sizes) | |||
| { | |||
| static_assert(N == sizeof...(I)); | |||
| resize(sizes...); | |||
| } | |||
| using value_type = T; | |||
| // Construct array with integer vec_t arg: | |||
| inline narray(vec_t<int, N> const &sizes) | |||
| // Return sizes of each dimension | |||
| inline vec_t<int, N> sizes() const | |||
| { | |||
| resize(sizes); | |||
| return vec_t<int, N>(m_sizes); | |||
| } | |||
| // Empty array | |||
| inline void clear() { resize(vec_t<size_t, N>(0)); } | |||
| // Size in number of elements and in bytes | |||
| inline size_t size() const { return size_helper(std::make_index_sequence<N>{}); } | |||
| inline size_t bytes() const { return size() * sizeof(value_type); } | |||
| // Access element i | |||
| // Access element i in row-major fashion | |||
| inline value_type &operator[](size_t i) | |||
| { | |||
| return m_data[i]; | |||
| return data()[i]; | |||
| } | |||
| inline value_type const &operator[](size_t i) const | |||
| { | |||
| return m_data[i]; | |||
| return data()[i]; | |||
| } | |||
| // Access element (i, j, .., z) in row-major fashion | |||
| // Access element (i, j, .., z) | |||
| template<typename... I> | |||
| inline value_type &operator()(I... indices) | |||
| { | |||
| static_assert(N == sizeof...(I)); | |||
| return m_data[offset(indices...)]; | |||
| return data()[offset(indices...)]; | |||
| } | |||
| template<typename... I> | |||
| inline value_type const &operator()(I... indices) const | |||
| { | |||
| static_assert(N == sizeof...(I)); | |||
| return m_data[offset(indices...)]; | |||
| return data()[offset(indices...)]; | |||
| } | |||
| inline value_type &operator()(vec_t<int, N> const &indices) | |||
| { | |||
| return m_data[offset_helper(indices, std::make_index_sequence<N>{})]; | |||
| return data()[offset_helper(indices, std::make_index_sequence<N>{})]; | |||
| } | |||
| inline value_type const &operator()(vec_t<int, N> const &indices) const | |||
| { | |||
| return m_data[offset_helper(indices, std::make_index_sequence<N>{})]; | |||
| return data()[offset_helper(indices, std::make_index_sequence<N>{})]; | |||
| } | |||
| // Resize array with a list of sizes (size1, size2, ..., sizen) | |||
| template<typename... I> | |||
| inline void resize(I... sizes) | |||
| protected: | |||
| template <size_t... I> | |||
| inline size_t size_helper(std::index_sequence<I...>) const | |||
| { | |||
| static_assert(N == sizeof...(I)); | |||
| m_data.resize((size_t(sizes) * ... * 1)); | |||
| m_sizes = { size_t(sizes)... }; | |||
| return (size_t(m_sizes[I]) * ... * 1); | |||
| } | |||
| inline void resize(vec_t<int, N> const &sizes) | |||
| template<typename... I> | |||
| inline size_t offset(size_t i, I... indices) const | |||
| { | |||
| resize_helper(sizes, std::make_index_sequence<N>{}); | |||
| if constexpr(sizeof...(I) > 0) | |||
| i += m_sizes[N - sizeof...(I) - 1] * offset(indices...); | |||
| return i; | |||
| } | |||
| // Return sizes of each dimension | |||
| inline vec_t<int, N> sizes() const | |||
| template <size_t... I> | |||
| inline size_t offset_helper(vec_t<int, N> const &sizes, std::index_sequence<I...>) const | |||
| { | |||
| return vec_t<int, N>(this->m_sizes); | |||
| return offset(size_t(sizes[I])...); | |||
| } | |||
| inline value_type *data() { return m_data.data(); } | |||
| inline value_type const *data() const { return m_data.data(); } | |||
| inline size_t size() const { return m_data.size(); } | |||
| inline size_t bytes() const { return size() * sizeof(value_type); } | |||
| vec_t<size_t, N> m_sizes { 0 }; | |||
| private: | |||
| template<typename... I> size_t offset(size_t i, I... indices) const | |||
| // Use CRTP to access data() from the child class | |||
| inline value_type *data() | |||
| { | |||
| if constexpr(sizeof...(I) > 0) | |||
| i += m_sizes[N - sizeof...(I) - 1] * offset(indices...); | |||
| return i; | |||
| return static_cast<container_type *>(this)->data(); | |||
| } | |||
| template <std::size_t... I> | |||
| size_t offset_helper(vec_t<int, N> const &sizes, std::index_sequence<I...>) const | |||
| inline value_type const *data() const | |||
| { | |||
| return offset(size_t(sizes[I])...); | |||
| return static_cast<container_type const *>(this)->data(); | |||
| } | |||
| }; | |||
| template<typename T, size_t N> | |||
| class [[nodiscard]] narray : public narray_base<T, N, narray<T, N>> | |||
| { | |||
| public: | |||
| using value_type = T; | |||
| template <std::size_t... I> | |||
| void resize_helper(vec_t<int, N> const &sizes, std::index_sequence<I...>) | |||
| inline narray() = default; | |||
| inline ~narray() = default; | |||
| // Construct array with args: size1, size2, ..., sizen | |||
| template<typename... I> inline narray(I... sizes) | |||
| { | |||
| resize(sizes[I]...); | |||
| static_assert(N == sizeof...(I)); | |||
| resize(sizes...); | |||
| } | |||
| vec_t<size_t, N> m_sizes { 0 }; | |||
| // Construct array with integer vec_t arg: | |||
| inline narray(vec_t<int, N> const &sizes) | |||
| { | |||
| resize(sizes); | |||
| } | |||
| // Empty array | |||
| inline void clear() { resize(vec_t<size_t, N>(0)); } | |||
| // Resize array with a list of sizes (size1, size2, ..., sizen) | |||
| template<typename... I> | |||
| inline void resize(I... sizes) | |||
| { | |||
| static_assert(N == sizeof...(I)); | |||
| this->m_sizes = { size_t(sizes)... }; | |||
| m_data.resize(this->size()); | |||
| } | |||
| template<typename U> | |||
| inline void resize(vec_t<U, N> const &sizes) | |||
| { | |||
| this->m_sizes = vec_t<size_t, N>(sizes); | |||
| m_data.resize(this->size()); | |||
| } | |||
| // Access data directly | |||
| inline value_type *data() { return m_data.data(); } | |||
| inline value_type const *data() const { return m_data.data(); } | |||
| private: | |||
| std::vector<T> m_data; | |||
| }; | |||
| template<typename T> using array2d = narray<T, 2>; | |||
| template<typename T> using array3d = narray<T, 3>; | |||
| template<typename T, size_t N> | |||
| class [[nodiscard]] narray_view : public narray_base<T, N, narray_view<T, N>> | |||
| { | |||
| public: | |||
| using value_type = T; | |||
| template<typename U> | |||
| inline narray_view(narray_base<T, N, U> &other) | |||
| : m_data(&other[0]) | |||
| { | |||
| this->m_sizes = vec_t<size_t, N>(other.sizes()); | |||
| } | |||
| // Access data directly | |||
| inline value_type *data() { return m_data; } | |||
| inline value_type const *data() const { return m_data; } | |||
| private: | |||
| T *m_data; | |||
| }; | |||
| template<typename T> using array2d_view = narray_view<T, 2>; | |||
| template<typename T> using array3d_view = narray_view<T, 3>; | |||
| } // namespace lol | |||