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.
This commit is contained in:
Jacques Lucke 2020-07-08 22:27:25 +02:00
parent 4b85ed819d
commit 403384998a
Notes: blender-bot 2023-02-13 11:55:39 +01:00
Referenced by commit a19584a471, BLI: fix constructor regression for Vector and Array
7 changed files with 145 additions and 21 deletions

View File

@ -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;

View File

@ -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__ */

View File

@ -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())
{
}

View File

@ -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))
{
}
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))
{
}
/**
* Create a vector from an array ref. The values in the vector are copy constructed.
*/
Vector(Span<T> values, Allocator allocator = {}) : Vector(allocator)
{
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;

View File

@ -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

View File

@ -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;

View File

@ -65,7 +65,8 @@ TEST(mutable_attributes_ref, ComplexTest)
Array<float> sizes(amount);
Array<std::string> names(amount);
Array<void *> buffers = {positions.data(), ids.data(), sizes.data(), names.data()};
Array<void *> buffers = {
(void *)positions.data(), (void *)ids.data(), (void *)sizes.data(), (void *)names.data()};
MutableAttributesRef attributes{info, buffers, IndexRange(1, 3)};
EXPECT_EQ(attributes.size(), 3);
EXPECT_EQ(attributes.info().size(), 4);