diff options
Diffstat (limited to 'source/blender/blenlib/BLI_memory_utils.hh')
-rw-r--r-- | source/blender/blenlib/BLI_memory_utils.hh | 199 |
1 files changed, 173 insertions, 26 deletions
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index 44d25340778..b73e0e95312 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -19,62 +19,82 @@ /** \file * \ingroup bli + * Some of the functions below have very similar alternatives in the standard library. However, it + * is rather annoying to use those when debugging. Therefore, some more specialized and easier to + * debug functions are provided here. */ #include <memory> #include <new> +#include <type_traits> #include "BLI_utildefines.h" namespace blender { /** - * Call the default constructor on n consecutive elements. For trivially constructible types, this - * does nothing. + * Call the destructor on n consecutive values. For trivially destructible types, this does + * nothing. + * + * Exception Safety: Destructors shouldn't throw exceptions. * * Before: - * ptr: uninitialized - * After: * ptr: initialized + * After: + * ptr: uninitialized */ -template<typename T> void default_construct_n(T *ptr, uint n) +template<typename T> void destruct_n(T *ptr, uint n) { + static_assert(std::is_nothrow_destructible_v<T>, + "This should be true for all types. Destructors are noexcept by default."); + /* This is not strictly necessary, because the loop below will be optimized away anyway. It is * nice to make behavior this explicitly, though. */ - if (std::is_trivially_constructible<T>::value) { + if (std::is_trivially_destructible_v<T>) { return; } for (uint i = 0; i < n; i++) { - new ((void *)(ptr + i)) T; + ptr[i].~T(); } } /** - * Call the destructor on n consecutive values. For trivially destructible types, this does - * nothing. + * Call the default constructor on n consecutive elements. For trivially constructible types, this + * does nothing. + * + * Exception Safety: Strong. * * Before: - * ptr: initialized - * After: * ptr: uninitialized + * After: + * ptr: initialized */ -template<typename T> void destruct_n(T *ptr, uint n) +template<typename T> void default_construct_n(T *ptr, uint n) { /* This is not strictly necessary, because the loop below will be optimized away anyway. It is * nice to make behavior this explicitly, though. */ - if (std::is_trivially_destructible<T>::value) { + if (std::is_trivially_constructible_v<T>) { return; } - for (uint i = 0; i < n; i++) { - ptr[i].~T(); + uint current = 0; + try { + for (; current < n; current++) { + new ((void *)(ptr + current)) T; + } + } + catch (...) { + destruct_n(ptr, current); + throw; } } /** * Copy n values from src to dst. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: initialized @@ -92,6 +112,8 @@ template<typename T> void initialized_copy_n(const T *src, uint n, T *dst) /** * Copy n values from src to dst. * + * Exception Safety: Strong. + * * Before: * src: initialized * dst: uninitialized @@ -101,14 +123,49 @@ template<typename T> void initialized_copy_n(const T *src, uint n, T *dst) */ template<typename T> void uninitialized_copy_n(const T *src, uint n, T *dst) { - for (uint i = 0; i < n; i++) { - new ((void *)(dst + i)) T(src[i]); + uint current = 0; + try { + for (; current < n; current++) { + new ((void *)(dst + current)) T(src[current]); + } + } + catch (...) { + destruct_n(dst, current); + throw; + } +} + +/** + * 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. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: initialized @@ -126,6 +183,8 @@ template<typename T> void initialized_move_n(T *src, uint n, T *dst) /** * Move n values from src to dst. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: uninitialized @@ -135,8 +194,15 @@ template<typename T> void initialized_move_n(T *src, uint n, T *dst) */ template<typename T> void uninitialized_move_n(T *src, uint n, T *dst) { - for (uint i = 0; i < n; i++) { - new ((void *)(dst + i)) T(std::move(src[i])); + uint current = 0; + try { + for (; current < n; current++) { + new ((void *)(dst + current)) T(std::move(src[current])); + } + } + catch (...) { + destruct_n(dst, current); + throw; } } @@ -144,6 +210,8 @@ template<typename T> void uninitialized_move_n(T *src, uint n, T *dst) * Relocate n values from src to dst. Relocation is a move followed by destruction of the src * value. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: initialized @@ -161,6 +229,8 @@ template<typename T> void initialized_relocate_n(T *src, uint n, T *dst) * Relocate n values from src to dst. Relocation is a move followed by destruction of the src * value. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: uninitialized @@ -177,6 +247,8 @@ template<typename T> void uninitialized_relocate_n(T *src, uint n, T *dst) /** * Copy the value to n consecutive elements. * + * Exception Safety: Basic. + * * Before: * dst: initialized * After: @@ -192,6 +264,8 @@ template<typename T> void initialized_fill_n(T *dst, uint n, const T &value) /** * Copy the value to n consecutive elements. * + * Exception Safety: Strong. + * * Before: * dst: uninitialized * After: @@ -199,8 +273,15 @@ template<typename T> void initialized_fill_n(T *dst, uint n, const T &value) */ template<typename T> void uninitialized_fill_n(T *dst, uint n, const T &value) { - for (uint i = 0; i < n; i++) { - new ((void *)(dst + i)) T(value); + uint current = 0; + try { + for (; current < n; current++) { + new ((void *)(dst + current)) T(value); + } + } + catch (...) { + destruct_n(dst, current); + throw; } } @@ -218,12 +299,8 @@ template<typename T> struct DestructValueAtAddress { template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddress<T>>; /** - * An `AlignedBuffer` is simply a byte array with the given size and alignment. The buffer will + * An `AlignedBuffer` is a byte array with at least the given size and alignment. The buffer will * not be initialized by the default constructor. - * - * This can be used to reserve memory for C++ objects whose lifetime is different from the - * lifetime of the object they are embedded in. It's used by containers with small buffer - * optimization and hash table implementations. */ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { private: @@ -231,6 +308,16 @@ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { char buffer_[(Size > 0) ? Size : 1]; public: + operator void *() + { + return (void *)buffer_; + } + + operator const void *() const + { + return (void *)buffer_; + } + void *ptr() { return (void *)buffer_; @@ -243,12 +330,72 @@ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { }; /** + * This can be used to reserve memory for C++ objects whose lifetime is different from the + * lifetime of the object they are embedded in. It's used by containers with small buffer + * optimization and hash table implementations. + */ +template<typename T, size_t Size = 1> class TypedBuffer { + private: + AlignedBuffer<sizeof(T) * Size, alignof(T)> buffer_; + + public: + operator T *() + { + return (T *)&buffer_; + } + + operator const T *() const + { + return (const T *)&buffer_; + } + + T &operator*() + { + return *(T *)&buffer_; + } + + const T &operator*() const + { + return *(const T *)&buffer_; + } + + T *ptr() + { + return (T *)&buffer_; + } + + const T *ptr() const + { + return (const T *)&buffer_; + } + + T &ref() + { + return *(T *)&buffer_; + } + + const T &ref() const + { + return *(const T *)&buffer_; + } +}; + +/** * This can be used by container constructors. A parameter of this type should be used to indicate * that the constructor does not construct the elements. */ 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__ */ |