forked from bartvdbraak/blender
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:
parent
4b85ed819d
commit
403384998a
@ -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;
|
||||
|
@ -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__ */
|
||||
|
@ -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())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user