From 6c71982951625959b72c6167ae036a5c94650bd5 Mon Sep 17 00:00:00 2001 From: Christopher Lackner Date: Tue, 20 Aug 2019 18:16:03 +0200 Subject: [PATCH 1/7] Range(obj) does respect index type now. If obj has a function Range it calls the function --- libsrc/core/array.hpp | 41 ++++++++++++++++++++++++++++++------- libsrc/core/utils.hpp | 20 ++++++++++++++++++ libsrc/general/ngarray.hpp | 1 + libsrc/meshing/meshtype.hpp | 14 ++++++------- tests/catch/CMakeLists.txt | 1 + tests/catch/array.cpp | 36 ++++++++++++++++++++++++++------ 6 files changed, 93 insertions(+), 20 deletions(-) diff --git a/libsrc/core/array.hpp b/libsrc/core/array.hpp index c16cc3bc..d523d96b 100644 --- a/libsrc/core/array.hpp +++ b/libsrc/core/array.hpp @@ -59,6 +59,26 @@ namespace ngcore } + namespace detail + { + + // Type trait to check if a class implements a 'range_type Range()' function + template + struct has_Range + { + private: + template + static constexpr auto check(T2*) -> + std::enable_if().Range(), std::true_type> { std::true_type(); } + template + static constexpr std::false_type check(...); + using type = decltype(check(nullptr)); // NOLINT + public: + NGCORE_API static constexpr bool value = type::value; + }; + } + template + constexpr bool has_range = detail::has_Range::value; template class AOWrapperIterator @@ -210,7 +230,7 @@ namespace ngcore template - constexpr size_t IndexBASE () { return 0; } + constexpr T IndexBASE () { return T(0); } template class FlatArray; @@ -293,6 +313,11 @@ namespace ngcore return T_Range(a,b); } + template + NETGEN_INLINE auto Range (const T& ao) + -> typename std::enable_if, decltype(std::declval().Range())>::type + { return ao.Range(); } + template NETGEN_INLINE T_Range Range_impl (T n, std::true_type) { @@ -301,13 +326,15 @@ namespace ngcore template NETGEN_INLINE auto Range_impl (const T & ao, std::false_type) - -> T_Range + -> T_Range> { - return T_Range (0, ao.Size()); + return T_Range> (IndexBASE>(), + IndexBASE>() + index_type(ao.Size())); } template - auto Range(const T & x) -> decltype(Range_impl(x, std::is_integral())) { + auto Range(const T & x) + -> typename std::enable_if, decltype(Range_impl(x, std::is_integral()))>::type { return Range_impl(x, std::is_integral()); } @@ -402,7 +429,7 @@ namespace ngcore class FlatArray : public BaseArrayObject > { protected: - static constexpr size_t BASE = IndexBASE(); + static constexpr IndexType BASE = IndexBASE(); /// the size size_t size; /// the data @@ -500,9 +527,9 @@ namespace ngcore return data[i-BASE]; } - NETGEN_INLINE T_Range Range () const + NETGEN_INLINE T_Range Range () const { - return T_Range (BASE, Size()+BASE); + return T_Range (BASE, size+BASE); } NETGEN_INLINE const CArray Addr (size_t pos) const diff --git a/libsrc/core/utils.hpp b/libsrc/core/utils.hpp index 1eb6d7bd..7f38c948 100644 --- a/libsrc/core/utils.hpp +++ b/libsrc/core/utils.hpp @@ -126,6 +126,26 @@ namespace ngcore return current; } + namespace detail + { + template + struct IndexTypeHelper + { + private: + template + static constexpr auto check(T2* t) -> typename T2::index_type { return *t; } + static constexpr auto check(...) -> decltype(std::declval().Size()) + { return decltype(std::declval().Size())(); } + public: + using type = decltype(check((T*) nullptr)); // NOLINT + }; + + } // namespace detail + + // Get index type of object. If object has a typedef index_type this type is returned + // else decltype(obj.Size()) is returned. + template + using index_type = typename detail::IndexTypeHelper::type; } // namespace ngcore #endif // NETGEN_CORE_UTILS_HPP diff --git a/libsrc/general/ngarray.hpp b/libsrc/general/ngarray.hpp index 213a24c2..f3eea9d4 100644 --- a/libsrc/general/ngarray.hpp +++ b/libsrc/general/ngarray.hpp @@ -84,6 +84,7 @@ namespace netgen T * data; public: typedef T TELEM; + using index_type = TIND; /// provide size and memory NgFlatArray (size_t asize, T * adata) diff --git a/libsrc/meshing/meshtype.hpp b/libsrc/meshing/meshtype.hpp index 3f0cf751..0d22722d 100644 --- a/libsrc/meshing/meshtype.hpp +++ b/libsrc/meshing/meshtype.hpp @@ -162,7 +162,7 @@ namespace netgen PointIndex & operator= (const PointIndex&) = default; PointIndex & operator= (PointIndex&&) = default; - PointIndex (int ai) : i(ai) + constexpr PointIndex (int ai) : i(ai) { #ifdef DEBUG if (ai < PointIndex::BASE) @@ -172,7 +172,7 @@ namespace netgen } constexpr PointIndex (t_invalid inv) : i(PointIndex::BASE-1) { ; } // PointIndex & operator= (const PointIndex &ai) { i = ai.i; return *this; } - operator int () const { return i; } + constexpr operator int () const { return i; } PointIndex operator++ (int) { PointIndex hi(*this); i++; return hi; } PointIndex operator-- (int) { PointIndex hi(*this); i--; return hi; } PointIndex operator++ () { i++; return *this; } @@ -180,9 +180,9 @@ namespace netgen void Invalidate() { i = PointIndex::BASE-1; } bool IsValid() const { return i != PointIndex::BASE-1; } #ifdef BASE0 - enum { BASE = 0 }; + static constexpr size_t BASE = 0; #else - enum { BASE = 1 }; + static constexpr size_t BASE = 1; #endif void DoArchive (Archive & ar) { ar & i; } @@ -193,7 +193,7 @@ namespace netgen namespace ngcore { template<> - constexpr size_t IndexBASE () { return netgen::PointIndex::BASE; } + constexpr netgen::PointIndex IndexBASE () { return netgen::PointIndex(netgen::PointIndex::BASE); } } namespace netgen @@ -255,14 +255,14 @@ namespace netgen int i; public: SurfaceElementIndex () = default; - SurfaceElementIndex (int ai) : i(ai) { ; } + constexpr SurfaceElementIndex (int ai) : i(ai) { ; } /* SurfaceElementIndex & operator= (const SurfaceElementIndex & ai) { i = ai.i; return *this; } */ SurfaceElementIndex & operator= (const SurfaceElementIndex & ai) = default; SurfaceElementIndex & operator= (int ai) { i = ai; return *this; } - operator int () const { return i; } + constexpr operator int () const { return i; } SurfaceElementIndex operator++ (int) { SurfaceElementIndex hi(*this); i++; return hi; } SurfaceElementIndex operator-- (int) { SurfaceElementIndex hi(*this); i--; return hi; } SurfaceElementIndex & operator++ () { ++i; return *this; } diff --git a/tests/catch/CMakeLists.txt b/tests/catch/CMakeLists.txt index 3b727af8..29f234ff 100644 --- a/tests/catch/CMakeLists.txt +++ b/tests/catch/CMakeLists.txt @@ -26,6 +26,7 @@ macro(add_unit_test name sources) endmacro() add_unit_test(archive archive.cpp) +add_unit_test(array array.cpp) add_unit_test(symboltable symboltable.cpp) add_unit_test(utils utils.cpp) add_unit_test(version version.cpp) diff --git a/tests/catch/array.cpp b/tests/catch/array.cpp index 471806c0..e7564324 100644 --- a/tests/catch/array.cpp +++ b/tests/catch/array.cpp @@ -1,9 +1,11 @@ #include "catch.hpp" -#include +#include using namespace ngcore; using namespace std; +#include "meshing.hpp" + TEST_CASE("Array") { @@ -15,13 +17,14 @@ TEST_CASE("Array") #endif // DEBUG Array a_initlst = { 1., 2., 3.}; CHECK(a_initlst[1] == 2.); - CHECK(a_initlst.size() == 3); + CHECK(a_initlst.Size() == 3); FlatArray fa_a = a_initlst; CHECK(typeid(fa_a) == typeid(FlatArray)); - CHECK(fa_a.size() == 3); - CHECK(a.Last() == 3.); - a.DeleteLast(); - CHECK(a.Last() == 2. && a.Size() == 2); + CHECK(fa_a.Size() == 3); + CHECK(fa_a.Last() == 3.); + a_initlst.DeleteLast(); + CHECK(a_initlst.Last() == 2.); + CHECK(a_initlst.Size() == 2); #ifdef DEBUG CHECK_THROWS_AS(fa_a[5], RangeException); #endif // DEBUG @@ -34,4 +37,25 @@ TEST_CASE("Array") CHECK(val == 2); } CHECK(count == 4); + + // range tests + CHECK(typeid(array.Range()) == typeid(T_Range)); + Array intarray; + CHECK(typeid(intarray.Range()) == typeid(T_Range)); + CHECK(typeid(Range(intarray)) == typeid(T_Range)); + int i = 0; + for(auto j : Range(b)) + CHECK(j == i++); + i = 0; + for(auto j : b.Range()) + CHECK(j == i++); + + // pointindex is still 1 based + Array piarray(2); + i = 1; + for(auto j : Range(piarray)) + CHECK(j == i++); + i = 1; + for(auto j : piarray.Range()) + CHECK(j == i++); } From 2fe62c846e3dfdff120ea88950a5b6ebe0695e4e Mon Sep 17 00:00:00 2001 From: Christopher Lackner Date: Wed, 21 Aug 2019 09:44:31 +0200 Subject: [PATCH 2/7] workaround for some compilers evaluating the declval in has_Range --- libsrc/core/array.hpp | 24 ++---------------------- libsrc/core/type_traits.hpp | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/libsrc/core/array.hpp b/libsrc/core/array.hpp index d523d96b..dedd974e 100644 --- a/libsrc/core/array.hpp +++ b/libsrc/core/array.hpp @@ -59,27 +59,6 @@ namespace ngcore } - namespace detail - { - - // Type trait to check if a class implements a 'range_type Range()' function - template - struct has_Range - { - private: - template - static constexpr auto check(T2*) -> - std::enable_if().Range(), std::true_type> { std::true_type(); } - template - static constexpr std::false_type check(...); - using type = decltype(check(nullptr)); // NOLINT - public: - NGCORE_API static constexpr bool value = type::value; - }; - } - template - constexpr bool has_range = detail::has_Range::value; - template class AOWrapperIterator { @@ -334,7 +313,8 @@ namespace ngcore template auto Range(const T & x) - -> typename std::enable_if, decltype(Range_impl(x, std::is_integral()))>::type { + -> typename std::enable_if || !has_range, + decltype(Range_impl(x, std::is_integral()))>::type { return Range_impl(x, std::is_integral()); } diff --git a/libsrc/core/type_traits.hpp b/libsrc/core/type_traits.hpp index 3863940b..17da6253 100644 --- a/libsrc/core/type_traits.hpp +++ b/libsrc/core/type_traits.hpp @@ -28,6 +28,29 @@ namespace ngcore template constexpr bool is_any_pointer = is_any_pointer_impl::value; } // namespace detail + + + // Type trait to check if a class implements a 'range_type Range()' function + namespace detail + { + template + struct has_Range + { + private: + template + static constexpr auto check(T2*) -> + std::enable_if_t().Range()), void>, std::true_type> + { std::true_type(); } + template + static constexpr std::false_type check(...); + using type = decltype(check(nullptr)); // NOLINT + public: + NGCORE_API static constexpr bool value = type::value; + }; + } + template + constexpr bool has_range = detail::has_Range::value; + } // namespace ngcore #endif // NETGEN_CORE_TYPE_TRAITS_HPP From a363524a98388ffed974f0fa85a5f3cc3f05e995 Mon Sep 17 00:00:00 2001 From: Christopher Lackner Date: Wed, 21 Aug 2019 10:00:31 +0200 Subject: [PATCH 3/7] more range tests --- tests/catch/array.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/catch/array.cpp b/tests/catch/array.cpp index e7564324..6bcb808e 100644 --- a/tests/catch/array.cpp +++ b/tests/catch/array.cpp @@ -6,6 +6,24 @@ using namespace std; #include "meshing.hpp" +template +class ClsWithIndexType +{ + size_t size; +public: + ClsWithIndexType(size_t asize) : size(asize) {} + using index_type = TIND; + size_t Size() const { return size; } +}; + +template +class ClsWithRange : public ClsWithIndexType +{ +public: + ClsWithRange(size_t size) : ClsWithIndexType(size) {} + T_Range Range() const { return {1, 1+this->Size()}; } +}; + TEST_CASE("Array") { @@ -58,4 +76,18 @@ TEST_CASE("Array") i = 1; for(auto j : piarray.Range()) CHECK(j == i++); + // a class can implement index_type and Size as well. + ClsWithIndexType clsi(3); + CHECK(typeid(Range(clsi)) == typeid(T_Range)); + i = 0; + for(auto j : Range(clsi)) + CHECK(j == i++); + // if the class has a Range function prefer that one + ClsWithRange clsr(3); + CHECK(typeid(Range(clsr)) == typeid(T_Range)); + i=1; + for(auto j : Range(clsr)) + CHECK(j == i++); + CHECK(typeid(Range(size_t(4))) == typeid(T_Range)); + CHECK(typeid(Range(4)) == typeid(T_Range)); } From 3869392f0a96017d0b586efe433fa20732a78ebf Mon Sep 17 00:00:00 2001 From: Christopher Lackner Date: Wed, 21 Aug 2019 11:03:27 +0200 Subject: [PATCH 4/7] workaround for windows in index_type typetrait --- libsrc/core/utils.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libsrc/core/utils.hpp b/libsrc/core/utils.hpp index 7f38c948..e18a09f7 100644 --- a/libsrc/core/utils.hpp +++ b/libsrc/core/utils.hpp @@ -134,6 +134,9 @@ namespace ngcore private: template static constexpr auto check(T2* t) -> typename T2::index_type { return *t; } + // this function is needed for visual because it seems to not lazy evaluate template arguments... + template + static constexpr auto check(T2* t) -> typename enable_if_t> {} static constexpr auto check(...) -> decltype(std::declval().Size()) { return decltype(std::declval().Size())(); } public: From 22de6f2c56ea0bb551e87be43ed23a602a0207a4 Mon Sep 17 00:00:00 2001 From: Christopher Lackner Date: Wed, 21 Aug 2019 11:06:00 +0200 Subject: [PATCH 5/7] fix typos --- libsrc/core/utils.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/core/utils.hpp b/libsrc/core/utils.hpp index e18a09f7..f6df8c9e 100644 --- a/libsrc/core/utils.hpp +++ b/libsrc/core/utils.hpp @@ -136,7 +136,7 @@ namespace ngcore static constexpr auto check(T2* t) -> typename T2::index_type { return *t; } // this function is needed for visual because it seems to not lazy evaluate template arguments... template - static constexpr auto check(T2* t) -> typename enable_if_t> {} + static constexpr auto check(T2* t) -> typename std::enable_if_t> {} static constexpr auto check(...) -> decltype(std::declval().Size()) { return decltype(std::declval().Size())(); } public: From b12ef20fb7a45f889729ec7c3bdf0688aafacae5 Mon Sep 17 00:00:00 2001 From: Christopher Lackner Date: Wed, 21 Aug 2019 11:24:37 +0200 Subject: [PATCH 6/7] index type can only be deduced from class, else it is size_t --- libsrc/core/utils.hpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/libsrc/core/utils.hpp b/libsrc/core/utils.hpp index f6df8c9e..84d1dade 100644 --- a/libsrc/core/utils.hpp +++ b/libsrc/core/utils.hpp @@ -134,21 +134,18 @@ namespace ngcore private: template static constexpr auto check(T2* t) -> typename T2::index_type { return *t; } - // this function is needed for visual because it seems to not lazy evaluate template arguments... - template - static constexpr auto check(T2* t) -> typename std::enable_if_t> {} - static constexpr auto check(...) -> decltype(std::declval().Size()) - { return decltype(std::declval().Size())(); } + static constexpr size_t check(...); + public: using type = decltype(check((T*) nullptr)); // NOLINT }; } // namespace detail - // Get index type of object. If object has a typedef index_type this type is returned - // else decltype(obj.Size()) is returned. + // Get index type of object. If object has a typedef index_type it is this type, else size_t template using index_type = typename detail::IndexTypeHelper::type; + } // namespace ngcore #endif // NETGEN_CORE_UTILS_HPP From d61e9d10cd4aa36db47ec44df5a3bdb22d9abb5a Mon Sep 17 00:00:00 2001 From: Christopher Lackner Date: Wed, 21 Aug 2019 11:56:26 +0200 Subject: [PATCH 7/7] array returns index of appended element on Append, some documentation --- libsrc/core/array.hpp | 48 ++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/libsrc/core/array.hpp b/libsrc/core/array.hpp index dedd974e..4e460f66 100644 --- a/libsrc/core/array.hpp +++ b/libsrc/core/array.hpp @@ -303,14 +303,24 @@ namespace ngcore return T_Range (0, n); } - template - NETGEN_INLINE auto Range_impl (const T & ao, std::false_type) - -> T_Range> + template + NETGEN_INLINE auto Range_impl (const TA & ao, std::false_type) + -> T_Range> { - return T_Range> (IndexBASE>(), - IndexBASE>() + index_type(ao.Size())); + return T_Range> (IndexBASE>(), + IndexBASE>() + index_type(ao.Size())); } + /* + Range(obj) will create a range in using the following steps: + + * if obj is an integral type it will create T_Range(0, obj) + * if obj has a function Range() it will return obj.Range() + * if obj has a typedef index_type it will return + T_Range(IndexBASE(), IndexBASE() + index_type(obj.Size())) + * else it will return T_Range (0, obj.Size()) + + */ template auto Range(const T & x) -> typename std::enable_if || !has_range, @@ -630,6 +640,7 @@ namespace ngcore using FlatArray::BASE; public: + using index_type = typename FlatArray::index_type; /// Generate array of logical and physical size asize NETGEN_INLINE explicit Array() : FlatArray (0, nullptr) @@ -795,42 +806,42 @@ namespace ngcore } /// Add element at end of array. reallocation if necessary. - NETGEN_INLINE size_t Append (const T & el) + /// Returns index of new element. + NETGEN_INLINE index_type Append (const T & el) { if (size == allocsize) ReSize (size+1); data[size] = el; - size++; - return size; + return BASE + size++; } /// Add element at end of array. reallocation not necessary. - NETGEN_INLINE size_t AppendHaveMem (const T & el) + /// Returns index of new element. + NETGEN_INLINE index_type AppendHaveMem (const T & el) { + NETGEN_CHECK_RANGE(size, 0, allocsize); data[size] = el; - size++; - return size; + return BASE + size++; } /// Add element at end of array. reallocation if necessary. - NETGEN_INLINE size_t Append (T && el) + /// Returns index of new element. + NETGEN_INLINE index_type Append (T && el) { if (size == allocsize) ReSize (size+1); data[size] = std::move(el); - size++; - return size; + return BASE + size++; } // Add elements of initializer list to end of array. Reallocation if necessary. - NETGEN_INLINE size_t Append(std::initializer_list lst) + NETGEN_INLINE void Append(std::initializer_list lst) { if(allocsize < size + lst.size()) ReSize(size+lst.size()); for(auto val : lst) data[size++] = val; - return size; } /// Add element at end of array. reallocation if necessary. @@ -852,8 +863,7 @@ namespace ngcore /// Append array at end of array. reallocation if necessary. - // int Append (const Array & source) - NETGEN_INLINE size_t Append (FlatArray source) + NETGEN_INLINE void Append (FlatArray source) { if(size + source.Size() >= allocsize) ReSize (size + source.Size() + 1); @@ -862,7 +872,6 @@ namespace ngcore data[i] = source[j]; size += source.Size(); - return size; } @@ -879,6 +888,7 @@ namespace ngcore /// Delete element i. Move all remaining elements forward NETGEN_INLINE void RemoveElement (size_t i) { + NETGEN_CHECK_RANGE(i, BASE, BASE+size); for(size_t j = i; j < this->size-1; j++) this->data[j] = this->data[j+1]; this->size--;