[Bf-blender-cvs] [403384998a6] master: BLI: improve constructors and conversions to span

Jacques Lucke noreply at git.blender.org
Wed Jul 8 22:31:18 CEST 2020


Commit: 403384998a6bb5f428e15ced5503206b45032b25
Author: Jacques Lucke
Date:   Wed Jul 8 22:27:25 2020 +0200
Branches: master
https://developer.blender.org/rB403384998a6bb5f428e15ced5503206b45032b25

BLI: improve constructors and conversions to span

This allows us to avoid many calls to `as_span()` methods. I will
remove those in the next commit. Furthermore, constructors
of Vector and Array can convert from one type to another now.

I tested these changes on Linux with gcc and on Windows.

===================================================================

M	source/blender/blenlib/BLI_array.hh
M	source/blender/blenlib/BLI_memory_utils.hh
M	source/blender/blenlib/BLI_span.hh
M	source/blender/blenlib/BLI_vector.hh
M	tests/gtests/blenlib/BLI_memory_utils_test.cc
M	tests/gtests/blenlib/BLI_vector_test.cc
M	tests/gtests/functions/FN_attributes_ref_test.cc

===================================================================

diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh
index 18f9aad1000..c7a9c49c972 100644
--- a/source/blender/blenlib/BLI_array.hh
+++ b/source/blender/blenlib/BLI_array.hh
@@ -89,17 +89,19 @@ class Array {
   /**
    * Create a new array that contains copies of all values.
    */
-  Array(Span<T> values, Allocator allocator = {}) : allocator_(allocator)
+  template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+  Array(Span<U> values, Allocator allocator = {}) : allocator_(allocator)
   {
     size_ = values.size();
     data_ = this->get_buffer_for_size(values.size());
-    uninitialized_copy_n(values.data(), size_, data_);
+    uninitialized_convert_n<U, T>(values.data(), size_, data_);
   }
 
   /**
    * Create a new array that contains copies of all values.
    */
-  Array(const std::initializer_list<T> &values) : Array(Span<T>(values))
+  template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+  Array(const std::initializer_list<U> &values) : Array(Span<U>(values))
   {
   }
 
@@ -219,6 +221,18 @@ class Array {
     return MutableSpan<T>(data_, size_);
   }
 
+  template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+  operator Span<U>() const
+  {
+    return Span<U>(data_, size_);
+  }
+
+  template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+  operator MutableSpan<U>()
+  {
+    return MutableSpan<U>(data_, size_);
+  }
+
   Span<T> as_span() const
   {
     return *this;
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh
index 16d6227a551..133615f0f31 100644
--- a/source/blender/blenlib/BLI_memory_utils.hh
+++ b/source/blender/blenlib/BLI_memory_utils.hh
@@ -26,6 +26,7 @@
 
 #include <memory>
 #include <new>
+#include <type_traits>
 
 #include "BLI_utildefines.h"
 
@@ -134,6 +135,32 @@ template<typename T> void uninitialized_copy_n(const T *src, uint n, T *dst)
   }
 }
 
+/**
+ * Convert n values from type `From` to type `To`.
+ *
+ * Exception Safety: Strong.
+ *
+ * Before:
+ *  src: initialized
+ *  dst: uninitialized
+ * After:
+ *  src: initialized
+ *  dst: initialized
+ */
+template<typename From, typename To> void uninitialized_convert_n(const From *src, uint n, To *dst)
+{
+  uint current = 0;
+  try {
+    for (; current < n; current++) {
+      new ((void *)(dst + current)) To((To)src[current]);
+    }
+  }
+  catch (...) {
+    destruct_n(dst, current);
+    throw;
+  }
+}
+
 /**
  * Move n values from src to dst.
  *
@@ -364,6 +391,15 @@ template<typename T, size_t Size = 1> class TypedBuffer {
 class NoInitialization {
 };
 
+/**
+ * Helper variable that checks if a pointer type can be converted into another pointer type without
+ * issues. Possible issues are casting away const and casting a pointer to a child class.
+ * Adding const or casting to a parent class is fine.
+ */
+template<typename From, typename To>
+inline constexpr bool is_convertible_pointer_v =
+    std::is_convertible_v<From, To> &&std::is_pointer_v<From> &&std::is_pointer_v<To>;
+
 }  // namespace blender
 
 #endif /* __BLI_MEMORY_UTILS_HH__ */
diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh
index 9688c93ec14..5c841787520 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -100,6 +100,11 @@ template<typename T> class Span {
   {
   }
 
+  template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr>
+  Span(const U *start, uint size) : start_((const T *)start), size_(size)
+  {
+  }
+
   /**
    * Reference an initializer_list. Note that the data in the initializer_list is only valid until
    * the expression containing it is fully computed.
@@ -128,8 +133,8 @@ template<typename T> class Span {
    *   Span<T *> -> Span<const T *>
    *   Span<Derived *> -> Span<Base *>
    */
-  template<typename U, typename std::enable_if_t<std::is_convertible_v<U *, T>> * = nullptr>
-  Span(Span<U *> array) : Span((T *)array.data(), array.size())
+  template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr>
+  Span(Span<U> array) : start_((T *)array.data()), size_(array.size())
   {
   }
 
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index f608290f5df..df885588d9b 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -141,9 +141,19 @@ class Vector {
    */
   Vector(uint size, const T &value) : Vector()
   {
+    this->resize(size, value);
+  }
+
+  /**
+   * Create a vector from an array ref. The values in the vector are copy constructed.
+   */
+  template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+  Vector(Span<U> values, Allocator allocator = {}) : Vector(allocator)
+  {
+    const uint size = values.size();
     this->reserve(size);
     this->increase_size_by_unchecked(size);
-    blender::uninitialized_fill_n(begin_, size, value);
+    uninitialized_convert_n<U, T>(values.data(), size, begin_);
   }
 
   /**
@@ -152,24 +162,21 @@ class Vector {
    * This allows you to write code like:
    * Vector<int> vec = {3, 4, 5};
    */
-  Vector(const std::initializer_list<T> &values) : Vector(Span<T>(values))
+  template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+  Vector(const std::initializer_list<U> &values) : Vector(Span<U>(values))
   {
   }
 
-  /**
-   * Create a vector from an array ref. The values in the vector are copy constructed.
-   */
-  Vector(Span<T> values, Allocator allocator = {}) : Vector(allocator)
+  template<typename U,
+           size_t N,
+           typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
+  Vector(const std::array<U, N> &values) : Vector(Span(values))
   {
-    const uint size = values.size();
-    this->reserve(size);
-    this->increase_size_by_unchecked(size);
-    blender::uninitialized_copy_n(values.data(), size, begin_);
   }
 
   /**
-   * Create a vector from any container. It must be possible to use the container in a range-for
-   * loop.
+   * Create a vector from any container. It must be possible to use the container in a
+   * range-for loop.
    */
   template<typename ContainerT> static Vector FromContainer(const ContainerT &container)
   {
@@ -313,6 +320,18 @@ class Vector {
     return MutableSpan<T>(begin_, this->size());
   }
 
+  template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+  operator Span<U>() const
+  {
+    return Span<U>(begin_, this->size());
+  }
+
+  template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+  operator MutableSpan<U>()
+  {
+    return MutableSpan<U>(begin_, this->size());
+  }
+
   Span<T> as_span() const
   {
     return *this;
diff --git a/tests/gtests/blenlib/BLI_memory_utils_test.cc b/tests/gtests/blenlib/BLI_memory_utils_test.cc
index eb896c0cca6..b99db5c5eca 100644
--- a/tests/gtests/blenlib/BLI_memory_utils_test.cc
+++ b/tests/gtests/blenlib/BLI_memory_utils_test.cc
@@ -1,5 +1,6 @@
 /* Apache License, Version 2.0 */
 
+#include "BLI_float3.hh"
 #include "BLI_memory_utils.hh"
 #include "BLI_strict_flags.h"
 #include "testing/testing.h"
@@ -119,4 +120,40 @@ TEST(memory_utils, UninitializedFillN_StrongExceptionSafety)
   EXPECT_EQ(MyValue::alive, 0);
 }
 
+class TestBaseClass {
+  virtual void mymethod(){};
+};
+
+class TestChildClass : public TestBaseClass {
+  void mymethod() override
+  {
+  }
+};
+
+static_assert(is_convertible_pointer_v<int *, int *>);
+static_assert(is_convertible_pointer_v<int *, const int *>);
+static_assert(is_convertible_pointer_v<int *, int *const>);
+static_assert(is_convertible_pointer_v<int *, const int *const>);
+static_assert(!is_convertible_pointer_v<const int *, int *>);
+static_assert(!is_convertible_pointer_v<int, int *>);
+static_assert(!is_convertible_pointer_v<int *, int>);
+static_assert(is_convertible_pointer_v<TestBaseClass *, const TestBaseClass *>);
+static_assert(!is_convertible_pointer_v<const TestBaseClass *, TestBaseClass *>);
+static_assert(is_convertible_pointer_v<TestChildClass *, TestBaseClass *>);
+static_assert(!is_convertible_pointer_v<TestBaseClass *, TestChildClass *>);
+static_assert(is_convertible_pointer_v<const TestChildClass *, const TestBaseClass *>);
+static_assert(!is_convertible_pointer_v<TestBaseClass, const TestChildClass *>);
+static_assert(!is_convertible_pointer_v<float3, float *>);
+static_assert(!is_convertible_pointer_v<float *, float3>);
+static_assert(!is_convertible_pointer_v<int **, int *>);
+static_assert(!is_convertible_pointer_v<int *, int **>);
+static_assert(is_convertible_pointer_v<int **, int **>);
+static_assert(is_convertible_pointer_v<const int **, const int **>);
+static_assert(!is_convertible_pointer_v<const int **, int **>);
+static_assert(!is_convertible_pointer_v<int *const *, int **>);
+static_assert(!is_convertible_pointer_v<int *const *const, int **>);
+static_assert(is_convertible_pointer_v<int **, int **const>);
+static_assert(is_convertible_pointer_v<int **, int *const *>);
+static_assert(is_convertible_pointer_v<int **, int const *const *>);
+
 }  // namespace blender
diff --git a/tests/gtests/blenlib/BLI_vector_test.cc b/tests/gtests/blenlib/BLI_vector_test.cc
index 1f6bd1ded5b..92fb12fb4e5 100644
--- a/tests/gtests/blenlib/BLI_vector_test.cc
+++ b/tests/gtests/blenlib/BLI_vector_test.cc
@@ -59,6 +59,18 @@ TEST(vector, InitializerListConstructor)
   EXPECT_EQ(vec[3], 6);
 }
 
+TEST(vector, ConvertingConstructor)
+{
+  std::array<float, 5> values = {5.4f, 7.3f, -8.1f, 5.0f, 0.0f};
+  Vector<int> vec = values;
+  EXPECT_EQ(vec.size(), 5u);
+  EXPECT_EQ(vec[0], 5);
+  EXPECT_EQ(vec[1], 7);
+  EXPECT_EQ(vec[2], -8);
+  EXPECT_EQ(vec[3], 5);
+  EXPECT_EQ(vec[4], 0);
+}
+
 struct TestListValue {
   TestListValue *next, *prev;
   int value;
diff --git a/tests/gtests/functions/FN_attributes_ref_test.cc b/tests/gtests/functions/FN_attributes_ref_test.cc
index 1c05bb930db..fee8c5dc058 100644
--- a/tests/gtests/functions/FN_attributes_ref_test.cc
+++ b/tests/gtests/f

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list