From e0ac1c173fe05f25dcc9b7347beffd4bc159754c Mon Sep 17 00:00:00 2001 From: namark <namark@disroot.org> Date: Sat, 16 Nov 2019 03:37:30 +0400 Subject: [PATCH] Vector of vectors as multidimensional array, in particular vector indexing and vector size for vectors. --- source/simple/geom/vector.hpp | 98 +++++++++++++++++++++++++++++++- unit_tests/point.cpp | 102 ++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 1 deletion(-) diff --git a/source/simple/geom/vector.hpp b/source/simple/geom/vector.hpp index 88bdde3..50306c5 100644 --- a/source/simple/geom/vector.hpp +++ b/source/simple/geom/vector.hpp @@ -6,6 +6,7 @@ #include <type_traits> #include <ostream> #include <numeric> +#include <cassert> #include "simple/support/logic.hpp" #include "simple/support/carcdr.hpp" @@ -275,7 +276,8 @@ namespace simple::geom return result; } - template <typename C = Coordinate, std::enable_if_t<std::is_same_v<C,bool>>* = nullptr> + template <typename C = Coordinate, size_t D = Dimensions, + std::enable_if_t<std::is_same_v<C,bool> && (D != 1)>* = nullptr> [[nodiscard]] constexpr operator bool() const noexcept { @@ -360,15 +362,106 @@ SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR(<=, bool_vector) [[nodiscard]] constexpr const coordinate_type & operator[](size_t dimension) const& { + assert(dimension < Dimensions); return raw[dimension]; } [[nodiscard]] constexpr coordinate_type & operator[](size_t dimension) & { + assert(dimension < Dimensions); return raw[dimension]; } + // TODO: not sure if it's ok that this is backwards + // make sure to change meta::dimensions as well if change this + // maybe make use of order parameter to decide + // also definitely not ok that const and non const are so duplicated + template <typename IndexType, size_t Size, typename O, size_t Depth = Size, + std::enable_if_t<(Depth > 1)>* = nullptr> + [[nodiscard]] + constexpr auto & operator[](vector<IndexType,Size,O> index) & + { + return operator[](index[Depth - 1]) + .template operator[]<IndexType, Size, O, Depth-1>(index); + } + template <typename IndexType, size_t Size, typename O, size_t Depth, + std::enable_if_t<Depth == 1>* = nullptr> + [[nodiscard]] + constexpr coordinate_type & operator[](vector<IndexType,Size,O> index) & + { + return operator[](index[0]); + } + + template <typename IndexType, size_t Size, size_t Depth = Size, typename O, + std::enable_if_t<(Depth > 1)>* = nullptr> + [[nodiscard]] + constexpr const auto & operator[](vector<IndexType,Size> index) const& + { + return raw[index[Depth - 1]] + .template operator[]<IndexType, Size, O, Depth-1>(index); + } + template <typename IndexType, size_t Size, typename O, size_t Depth, + std::enable_if_t<Depth == 1>* = nullptr> + [[nodiscard]] + constexpr const coordinate_type & operator[](vector<IndexType,Size,O> index) const& + { + return raw[index[0]]; + } + + class meta + { + template <typename T, typename = std::nullptr_t> + struct has_dimesntions_s { constexpr static bool value = false; }; + template <typename T> + struct has_dimesntions_s<T, decltype(void(T::dimensions), nullptr)> { constexpr static bool value = true; }; + template <typename T> + constexpr static bool has_dimesntions = has_dimesntions_s<T>::value; + + public: + template <typename C = coordinate_type, + std::enable_if_t<has_dimesntions<C>>* = nullptr> + constexpr static size_t depth() + { + return depth<typename C::coordinate_type>() + 1; + } + template <typename C = coordinate_type, + std::enable_if_t<not(has_dimesntions<C>)>* = nullptr> + constexpr static size_t depth() + { + return 1; + } + + private: + template <size_t DepthIndex = depth(), typename Vector = vector> + static constexpr size_t get_sub_dimentions() + { + static_assert( DepthIndex < depth(), "Invalid depth" ); + if constexpr (DepthIndex == 0) + return Vector::dimensions; + else return get_sub_dimentions<DepthIndex-1, typename Vector::coordinate_type>(); + } + + template <typename O, typename SizeType = size_t, size_t DepthIndex = 0, size_t Depth = depth()> + static constexpr void set_sub_dimentions(vector<SizeType,Depth,O>& out) + { + out[Depth - 1 - DepthIndex] = get_sub_dimentions<DepthIndex>(); + if constexpr (DepthIndex+1 < Depth) + set_sub_dimentions<O,SizeType,DepthIndex+1>(out); + } + + template <typename SizeType = size_t, size_t Depth = depth()> + static constexpr auto get_dimensions() + { + vector<SizeType,Depth> ret{}; + set_sub_dimentions(ret); + return ret; + } + public: + template <typename SizeType = size_t, size_t Depth = meta::depth()> // have to explicitly qualify for depth gcc-7 + static constexpr vector<SizeType,Depth> dimensions = get_dimensions<SizeType,Depth>(); + }; + [[nodiscard]] constexpr auto begin() noexcept { return std::begin(raw); } [[nodiscard]] constexpr auto end() noexcept { return std::end(raw); } [[nodiscard]] constexpr auto begin() const noexcept { return std::cbegin(raw); } @@ -768,6 +861,9 @@ SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR(<=, bool_vector) -> vector<F, sizeof...(R) + 1>; // ugh, clang is really nitpickey about default template arguments + // actually, might be this bug + // https://stackoverflow.com/questions/57133186/g-and-clang-different-behaviour-when-stdmake-index-sequence-and-stdin + // very annoying in many different places :/ template <typename C, size_t D, typename O, void* SFINAE> vector(vector<C,D,O,SFINAE>) -> vector<vector<C,D,O,SFINAE>,1>; diff --git a/unit_tests/point.cpp b/unit_tests/point.cpp index 4fbda40..1008913 100644 --- a/unit_tests/point.cpp +++ b/unit_tests/point.cpp @@ -106,6 +106,107 @@ void Mixing() assert( (geom::vector<int, 3>{1,2,0} == p.mix<3>({0,1,4}, 0)) ); } +void MultidimensionalElementAccess() +{ + + auto spiral_walk = [](auto room) + { + constexpr auto csize = decltype(room)::meta::template dimensions<int>; + vector < + std::remove_reference_t<decltype(room[csize-1])>, + csize.x()*csize.y() + > ret{}; + + auto size = csize; + size_t i = 0; + + auto direction = 1; + auto begin = vector(-1,0); + while(size > vector(0,0)) + { + auto end = begin + direction * (size - vector(0,1)) ; + + do + { + begin.x() += direction; + ret[i++] = room[begin]; + } + while(begin.x() != end.x()); + while(begin.y() != end.y()) + { + begin.y() += direction; + ret[i++] = room[begin]; + } + //do + + --size; + direction = -direction; + } + + return ret; + }; + + assert( spiral_walk(vector( + vector(1,2,3), + vector(4,5,6), + vector(7,8,9) + )) == vector(1,2,3,6,9,8,7,4,5)); + + assert( spiral_walk(vector( + vector(1), + vector(2) + )) == vector(1,2)); + + assert( spiral_walk(vector( + vector(1) + )) == vector(1)); + + assert( spiral_walk(vector( + vector(1,2) + )) == vector(1,2)); + + // // well clang really doesn't like zero size vectors so... + // assert( spiral_walk(vector( + // vector<int,0>() + // )) == (vector<int,0>{})); + // + // assert( spiral_walk(vector( + // vector<int,0>(), + // vector<int,0>(), + // vector<int,0>(), + // vector<int,0>() + // )) == (vector<int,0>{})); + // + // assert( spiral_walk(vector<vector<int,10>,0>() + // ) == (vector<int,0>{})); + + assert( spiral_walk(vector( + vector(1,2), + vector(3,4) + )) == vector(1,2,4,3)); + + assert( spiral_walk(vector( + vector( 1, 2, 3, 4), + vector( 5, 6, 7, 8), + vector( 9,10,11,12) + )) == vector(1,2,3,4,8,12,11,10,9,5,6,7)); + + assert( spiral_walk(vector( + vector( 1, 2, 3), + vector( 4, 5, 6), + vector( 7, 8, 9), + vector(10,11,12) + )) == vector(1,2,3,6,9,12,11,10,7,4,5,8)); + + assert( spiral_walk(vector( + vector( 1, 2, 3, 4), + vector( 5, 6, 7, 8), + vector( 9,10,11,12), + vector(13,14,15,16) + )) == vector(1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10)); + +} + void RangeBasedLooping() { const float4 p{1.0f, 2.0f, 3.0f, 4.0f}; @@ -454,6 +555,7 @@ int main() OtherConstruction(); Mutation(); Mixing(); + MultidimensionalElementAccess(); RangeBasedLooping(); Arithmetic(); DiscreteArithmetic(); -- GitLab