From 33302790baf0a4546e943d10c875c48dceaaa69c Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 28 Aug 2025 10:30:24 +0100 Subject: [PATCH] Added etl::typed_storage_ext and swap for same --- include/etl/alignment.h | 269 ++++++++++++++++++++++++++++++++++++++-- test/test_alignment.cpp | 43 ++++++- 2 files changed, 299 insertions(+), 13 deletions(-) diff --git a/include/etl/alignment.h b/include/etl/alignment.h index ff469ccd..75439280 100644 --- a/include/etl/alignment.h +++ b/include/etl/alignment.h @@ -37,6 +37,7 @@ SOFTWARE. #include "error_handler.h" #include "exception.h" #include "utility.h" +#include "algorithm.h" #include @@ -245,11 +246,6 @@ namespace etl { struct type { - //type() - // : data() - //{ - //} - /// Convert to T reference. template operator T& () @@ -382,6 +378,7 @@ namespace etl //*************************************************************************** template typed_storage(TArgs&&... args) ETL_NOEXCEPT_IF_NO_THROW + : valid(false) { create(etl::forward(args)...); } @@ -390,6 +387,7 @@ namespace etl /// Constructs the instance of T with type T1 //*************************************************************************** typed_storage(const T1& t1) + : valid(false) { create(t1); } @@ -398,6 +396,7 @@ namespace etl /// Constructs the instance of T with types T1, T2 //*************************************************************************** typed_storage(const T1& t1, const T2& t2) + : valid(false) { create(t1, t2); } @@ -406,6 +405,7 @@ namespace etl /// Constructs the instance of T with types T1, T2, T3 //*************************************************************************** typed_storage(const T1& t1, const T2& t2, const T3& t3) + : valid(false) { create(t1, t2, t3); } @@ -414,6 +414,7 @@ namespace etl /// Constructs the instance of T with types T1, T2, T3, T4 //*************************************************************************** typed_storage(const T1& t1, const T2& t2, const T3& t3, const T4& t4) + : valid(false) { create(t1, t2, t3, t4); } @@ -554,7 +555,7 @@ namespace etl //*************************************************************************** /// \returns a pointer of type T and asserts if has_value() is false. //*************************************************************************** - pointer operator->() + pointer operator->() ETL_NOEXCEPT_IF_NO_THROW { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -564,7 +565,7 @@ namespace etl //*************************************************************************** /// \returns a const pointer of type T and asserts if has_value() is false. //*************************************************************************** - const_pointer operator->() const + const_pointer operator->() const ETL_NOEXCEPT_IF_NO_THROW { ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); @@ -574,7 +575,7 @@ namespace etl //*************************************************************************** /// \returns reference of type T and asserts if has_value() is false. //*************************************************************************** - reference operator*() + reference operator*() ETL_NOEXCEPT_IF_NO_THROW { return *operator->(); } @@ -582,7 +583,7 @@ namespace etl //*************************************************************************** /// \returns const_reference of type T and asserts if has_value() is false. //*************************************************************************** - const_reference operator*() const + const_reference operator*() const ETL_NOEXCEPT_IF_NO_THROW { return *operator->(); } @@ -612,6 +613,256 @@ namespace etl bool valid; }; + + //*************************************************************************** + /// Wrapper class that provides a memory area and lets the user create an + /// instance of T in this memory at runtime. This class also erases the + /// destructor call of T, i.e. if typed_storage goes out of scope, the + /// destructor if the wrapped type will not be called. This can be done + /// explicitly by calling destroy(). + /// \tparam T Type of element stored in this instance of typed_storage. + //*************************************************************************** + template + class typed_storage_ext + { + public: + + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; + typedef T* pointer; + typedef const T* const_pointer; + + template + friend ETL_CONSTEXPR14 void swap(typed_storage_ext& lhs, typed_storage_ext& rhs) ETL_NOEXCEPT; + + //*************************************************************************** + /// Constructor. + //*************************************************************************** + template + typed_storage_ext(void* pbuffer_) ETL_NOEXCEPT_IF_NO_THROW + : pbuffer(reinterpret_cast(pbuffer_)), + valid(false) + { + ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of::value), ETL_ERROR(etl::alignment_error)); + } + +#if ETL_USING_CPP11 + //*************************************************************************** + /// Constructs the instance of T forwarding the given \p args to its constructor. + //*************************************************************************** + template + typed_storage_ext(void* pbuffer_, TArgs&&... args) ETL_NOEXCEPT_IF_NO_THROW + : pbuffer(reinterpret_cast(pbuffer_)) + , valid(false) + { + ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of::value), ETL_ERROR(etl::alignment_error)); + create(etl::forward(args)...); + } +#else + //*************************************************************************** + /// Constructs the instance of T with type T1 + //*************************************************************************** + typed_storage_ext(void* pbuffer_, const T1& t1) + : pbuffer(reinterpret_cast(pbuffer_)) + , valid(false) + { + ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of::value), ETL_ERROR(etl::alignment_error)); + } + + //*************************************************************************** + /// Constructs the instance of T with type T1 + //*************************************************************************** + typed_storage_ext(void* pbuffer_, const T1& t1) + : pbuffer(reinterpret_cast(pbuffer_)) + , valid(false) + { + ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of::value), ETL_ERROR(etl::alignment_error)); + create(t1); + } + + //*************************************************************************** + /// Constructs the instance of T with types T1, T2 + //*************************************************************************** + typed_storage_ext(void* pbuffer_, const T1& t1, const T2& t2) + : pbuffer(reinterpret_cast(pbuffer_)) + , valid(false) + { + ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of::value), ETL_ERROR(etl::alignment_error)); + create(t1, t2); + } + + //*************************************************************************** + /// Constructs the instance of T with types T1, T2, T3 + //*************************************************************************** + typed_storage_ext(void* pbuffer_, const T1& t1, const T2& t2, const T3& t3) + : pbuffer(reinterpret_cast(pbuffer_)) + , valid(false) + { + ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of::value), ETL_ERROR(etl::alignment_error)); + create(t1, t2, t3); + } + + //*************************************************************************** + /// Constructs the instance of T with types T1, T2, T3, T4 + //*************************************************************************** + typed_storage_ext(void* pbuffer_, const T1& t1, const T2& t2, const T3& t3, const T4& t4) + : pbuffer(reinterpret_cast(pbuffer_)) + , valid(false) + { + ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of::value), ETL_ERROR(etl::alignment_error)); + create(t1, t2, t3, t4); + } +#endif + + //*************************************************************************** + /// Default destructor which will NOT call the destructor of the object which + /// was created by calling create(). + //*************************************************************************** + ~typed_storage_ext() ETL_NOEXCEPT + { + // Intentionally empty. + } + + //*************************************************************************** + /// \returns true if object has been constructed using create(). + /// \returns false otherwise. + //*************************************************************************** + bool has_value() const ETL_NOEXCEPT + { + return valid; + } + +#if ETL_USING_CPP11 + //*************************************************************************** + /// Constructs the instance of T forwarding the given \p args to its constructor. + /// \returns the instance of T which has been constructed in the internal byte array. + //*************************************************************************** + template + reference create(TArgs&&... args) ETL_NOEXCEPT_IF_NO_THROW + { + ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); + valid = true; + return *::new (pbuffer) value_type(etl::forward(args)...); + } +#else + //*************************************************************************** + /// Constructs the instance of T with type T1 + /// \returns the instance of T which has been constructed in the internal byte array. + //*************************************************************************** + template + reference create(const T1& t1) + { + ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); + valid = true; + return *::new (pbuffer) value_type(t1); + } + + //*************************************************************************** + /// Constructs the instance of T with types T1, T2 + /// \returns the instance of T which has been constructed in the internal byte array. + //*************************************************************************** + template + reference create(const T1& t1, const T2& t2) + { + ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); + valid = true; + return *::new (pbuffer) value_type(t1, t2); + } + + //*************************************************************************** + /// Constructs the instance of T with types T1, T2, T3 + /// \returns the instance of T which has been constructed in the internal byte array. + //*************************************************************************** + template + reference create(const T1& t1, const T2& t2, const T3& t3) + { + ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); + valid = true; + return *::new (pbuffer) value_type(t1, t2, t3); + } + + //*************************************************************************** + /// Constructs the instance of T with types T1, T2, T3, T4 + /// \returns the instance of T which has been constructed in the internal byte array. + //*************************************************************************** + template + reference create(const T1& t1, const T2& t2, const T3& t3, const T4& t4) + { + ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); + valid = true; + return *::new (pbuffer) value_type(t1, t2, t3, t4); + } +#endif + + //*************************************************************************** + /// Calls the destructor of the stored object, if created. + //*************************************************************************** + void destroy() ETL_NOEXCEPT + { + if (has_value()) + { + pbuffer->~T(); + valid = false; + } + } + + //*************************************************************************** + /// \returns a pointer of type T and asserts if has_value() is false. + //*************************************************************************** + pointer operator->() ETL_NOEXCEPT_IF_NO_THROW + { + ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); + + return pbuffer; + } + + //*************************************************************************** + /// \returns a const pointer of type T and asserts if has_value() is false. + //*************************************************************************** + const_pointer operator->() const ETL_NOEXCEPT_IF_NO_THROW + { + ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); + + return pbuffer; + } + + //*************************************************************************** + /// \returns reference of type T and asserts if has_value() is false. + //*************************************************************************** + reference operator*() ETL_NOEXCEPT_IF_NO_THROW + { + return *operator->(); + } + + //*************************************************************************** + /// \returns const_reference of type T and asserts if has_value() is false. + //*************************************************************************** + const_reference operator*() const ETL_NOEXCEPT_IF_NO_THROW + { + return *operator->(); + } + + private: + + typed_storage_ext(etl::typed_storage_ext&) ETL_DELETE; + typed_storage_ext& operator =(etl::typed_storage_ext&) ETL_DELETE; + + T* pbuffer; + bool valid; + }; + + //*************************************************************************** + /// Swap two etl::typed_storage_ext + //*************************************************************************** + template + ETL_CONSTEXPR14 void swap(etl::typed_storage_ext& lhs, etl::typed_storage_ext& rhs) ETL_NOEXCEPT + { + using ETL_OR_STD::swap; + + swap(lhs.pbuffer, rhs.pbuffer); + swap(lhs.valid, rhs.valid); + } } #endif diff --git a/test/test_alignment.cpp b/test/test_alignment.cpp index ee4b589e..b9d3b5d2 100644 --- a/test/test_alignment.cpp +++ b/test/test_alignment.cpp @@ -196,7 +196,7 @@ namespace etl::typed_storage a; CHECK_FALSE(a.has_value()); - etl::typed_storage b(789, 10); // Construct in place. + etl::typed_storage b(789, 10); // Construct in place. CHECK_TRUE(b.has_value()); CHECK_EQUAL(b->x, 789); CHECK_EQUAL(b->y, 10); @@ -212,10 +212,45 @@ namespace CHECK_TRUE(*a == ref); - a.create(456, 7); // Calling create again is not an error. - CHECK_EQUAL(a->x, 456); - CHECK_EQUAL(a->y, 7); + CHECK_TRUE(a.has_value()); + a.destroy(); + CHECK_FALSE(a.has_value()); + } + //************************************************************************* + TEST(test_typed_storage_ext) + { + alignas(A_t) char buffer1[sizeof(A_t)] = {0}; + alignas(A_t) char buffer2[sizeof(A_t)] = {0}; + + etl::typed_storage_ext a(buffer1); + CHECK_FALSE(a.has_value()); + + etl::typed_storage_ext b(buffer2, 789, 10); // Construct in place. + CHECK_TRUE(b.has_value()); + CHECK_EQUAL(b->x, 789); + CHECK_EQUAL(b->y, 10); + + auto& ref = a.create(123, 4); // Create in place. + CHECK_EQUAL(ref.x, 123); + CHECK_EQUAL(ref.y, 4); + + CHECK_TRUE(a.has_value()); + CHECK_EQUAL(a->x, 123); + CHECK_EQUAL(a->y, 4); + CHECK_TRUE(*a == ref); + + // Swap + etl::swap(a, b); + CHECK_TRUE(a.has_value()); + CHECK_EQUAL(a->x, 789); + CHECK_EQUAL(a->y, 10); + + CHECK_TRUE(b.has_value()); + CHECK_EQUAL(b->x, 123); + CHECK_EQUAL(b->y, 4); + + // Destroy CHECK_TRUE(a.has_value()); a.destroy(); CHECK_FALSE(a.has_value());