| @@ -15,127 +15,180 @@ | |||||
| // | // | ||||
| // The narray class | // 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 <lol/vector> // lol::vec_t | ||||
| #include <vector> // std::vector | #include <vector> // std::vector | ||||
| #include <utility> // std::index_sequence | |||||
| namespace lol | 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: | 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) | inline value_type &operator[](size_t i) | ||||
| { | { | ||||
| return m_data[i]; | |||||
| return data()[i]; | |||||
| } | } | ||||
| inline value_type const &operator[](size_t i) const | 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> | template<typename... I> | ||||
| inline value_type &operator()(I... indices) | inline value_type &operator()(I... indices) | ||||
| { | { | ||||
| static_assert(N == sizeof...(I)); | static_assert(N == sizeof...(I)); | ||||
| return m_data[offset(indices...)]; | |||||
| return data()[offset(indices...)]; | |||||
| } | } | ||||
| template<typename... I> | template<typename... I> | ||||
| inline value_type const &operator()(I... indices) const | inline value_type const &operator()(I... indices) const | ||||
| { | { | ||||
| static_assert(N == sizeof...(I)); | static_assert(N == sizeof...(I)); | ||||
| return m_data[offset(indices...)]; | |||||
| return data()[offset(indices...)]; | |||||
| } | } | ||||
| inline value_type &operator()(vec_t<int, N> const &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 | 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: | 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; | std::vector<T> m_data; | ||||
| }; | }; | ||||
| template<typename T> using array2d = narray<T, 2>; | template<typename T> using array2d = narray<T, 2>; | ||||
| template<typename T> using array3d = narray<T, 3>; | 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 | } // namespace lol | ||||