From 7316c07045c7885ad3fd95b3ef49f591c6494756 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 19 Feb 2023 10:43:13 +0000 Subject: [PATCH] Added etl::contruct_object_at, etl::get_object_at and etl::destroy_object_at --- include/etl/file_error_numbers.h | 1 + include/etl/memory.h | 97 +++++++++++++++++ test/test_memory.cpp | 174 +++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+) diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 7d6a0e1a..55aaf14b 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -100,5 +100,6 @@ SOFTWARE. #define ETL_BIP_BUFFER_SPSC_ATOMIC_FILE_ID "67" #define ETL_REFERENCE_COUNTED_OBJECT_FILE_ID "68" #define ETL_TO_ARITHMETIC_FILE_ID "69" +#define ETL_ALIGNMENT_FILE_ID "70" #endif diff --git a/include/etl/memory.h b/include/etl/memory.h index 0725d2e6..f81da1db 100644 --- a/include/etl/memory.h +++ b/include/etl/memory.h @@ -2446,6 +2446,103 @@ namespace etl return (result == 0U) ? reinterpret_cast(sb + n) : reinterpret_cast(result); } + +#if ETL_USING_CPP11 + //***************************************************************************** + /// Move construct the container at 'p'. + //***************************************************************************** + template + TObject& construct_object_at(void* p, TObject&& other) + { +#if ETL_IS_DEBUG_BUILD + ETL_ASSERT(is_aligned(p), ETL_ERROR(alignment_error)); +#endif + + return *etl::construct_at(reinterpret_cast::type*>(p), etl::forward(other)); + } + + //***************************************************************************** + /// Construct the container at 'p' from arguments. + //***************************************************************************** + template + TObject& construct_object_at(void* p, TArgs&&... args) + { +#if ETL_IS_DEBUG_BUILD + ETL_ASSERT(is_aligned(p), ETL_ERROR(alignment_error)); +#endif + + return *etl::construct_at(reinterpret_cast(p), etl::forward(args)...); + } +#else + //***************************************************************************** + /// Default construct the container at 'p'. + //***************************************************************************** + template + TObject& construct_object_at(void* p) + { +#if ETL_IS_DEBUG_BUILD + ETL_ASSERT(is_aligned(p), ETL_ERROR(alignment_error)); +#endif + + return *etl::construct_at(reinterpret_cast(p)); + } + + //***************************************************************************** + /// Copy construct the container at 'p'. + //***************************************************************************** + template + TObject& construct_object_at(void* p, const TObject& other) + { +#if ETL_IS_DEBUG_BUILD + ETL_ASSERT(is_aligned(p), ETL_ERROR(alignment_error)); +#endif + + return *etl::construct_at(reinterpret_cast(p), other); + } + + //***************************************************************************** + /// Construct the container at 'p' from argument. + //***************************************************************************** + template + TObject& construct_object_at(void* p, const TArg& arg) + { +#if ETL_IS_DEBUG_BUILD + ETL_ASSERT(is_aligned(p), ETL_ERROR(alignment_error)); +#endif + + return *etl::construct_at(reinterpret_cast(p), arg); + } +#endif + + //***************************************************************************** + /// Get the container at 'p'. + //***************************************************************************** + template + TObject& get_object_at(void* p) + { +#if ETL_IS_DEBUG_BUILD + ETL_ASSERT(is_aligned(p), ETL_ERROR(alignment_error)); +#endif + + TObject& v = *reinterpret_cast(p); + + return v; + } + + //***************************************************************************** + /// Destroy the container at 'p'. + /// For a container that contains a type that is not trivially destructible. + //***************************************************************************** + template + void destroy_object_at(void* p) + { +#if ETL_IS_DEBUG_BUILD + ETL_ASSERT(is_aligned(p), ETL_ERROR(alignment_error)); +#endif + + TObject& v = get_object_at(p); + v.~TObject(); + } } #endif diff --git a/test/test_memory.cpp b/test/test_memory.cpp index 9854f172..fabd6153 100644 --- a/test/test_memory.cpp +++ b/test/test_memory.cpp @@ -1344,5 +1344,179 @@ namespace CHECK(function_was_called); CHECK(ptr.get() == ETL_NULLPTR); } + + //************************************************************************* + struct Flags + { + Flags() + : constructed(false) + , destructed(false) + { + } + + void Clear() + { + constructed = false; + destructed = false; + } + + bool constructed; + bool destructed; + }; + + static Flags flags; + + TEST(test_construct_get_destroy_object_aligned) + { + struct Data + { + Data() + : a(1) + , b(2) + { + flags.constructed = true; + } + + Data(int a_, int b_) + : a(a_) + , b(b_) + { + flags.constructed = true; + } + + ~Data() + { + flags.destructed = true; + } + + int a; + int b; + }; + + alignas(Data) char buffer1[sizeof(Data)]; + char* pbuffer1 = buffer1; + + alignas(Data) char buffer1b[sizeof(Data)]; + char* pbuffer1b = buffer1b; + + alignas(Data) char buffer2[sizeof(Data)]; + char* pbuffer2 = buffer2; + + alignas(Data) char buffer2b[sizeof(Data)]; + char* pbuffer2b = buffer2b; + + alignas(Data) char buffer3[sizeof(Data)]; + char* pbuffer3 = buffer3; + + alignas(Data) char buffer3b[sizeof(Data)]; + char* pbuffer3b = buffer3b; + + flags.Clear(); + Data& rdata1 = etl::construct_object_at(pbuffer1); + CHECK_TRUE(flags.constructed); + CHECK_FALSE(flags.destructed); + CHECK_EQUAL(1, rdata1.a); + CHECK_EQUAL(2, rdata1.b); + + flags.Clear(); + Data data2(3, 4); + Data& rdata2 = etl::construct_object_at(pbuffer2, data2); + CHECK_TRUE(flags.constructed); + CHECK_FALSE(flags.destructed); + CHECK_EQUAL(data2.a, rdata2.a); + CHECK_EQUAL(data2.b, rdata2.b); + + flags.Clear(); + Data& rdata3 = etl::construct_object_at(pbuffer3, 5, 6); + CHECK_TRUE(flags.constructed); + CHECK_FALSE(flags.destructed); + CHECK_EQUAL(5, rdata3.a); + CHECK_EQUAL(6, rdata3.b); + + memcpy(buffer1b, buffer1, sizeof(Data)); + memcpy(buffer2b, buffer2, sizeof(Data)); + memcpy(buffer3b, buffer3, sizeof(Data)); + + flags.Clear(); + Data& rdata1b = etl::get_object_at(pbuffer1b); + CHECK_FALSE(flags.constructed); + CHECK_FALSE(flags.destructed); + CHECK_EQUAL(1, rdata1.a); + CHECK_EQUAL(2, rdata1.b); + + flags.Clear(); + Data& rdata2b = etl::get_object_at(pbuffer2b); + CHECK_FALSE(flags.constructed); + CHECK_FALSE(flags.destructed); + CHECK_EQUAL(data2.a, rdata2.a); + CHECK_EQUAL(data2.b, rdata2.b); + + flags.Clear(); + Data& rdata3b = etl::get_object_at(pbuffer3b); + CHECK_FALSE(flags.constructed); + CHECK_FALSE(flags.destructed); + CHECK_EQUAL(5, rdata3.a); + CHECK_EQUAL(6, rdata3.b); + + flags.Clear(); + etl::destroy_object_at(pbuffer1b); + CHECK_FALSE(flags.constructed); + CHECK_TRUE(flags.destructed); + + flags.Clear(); + etl::destroy_object_at(pbuffer2b); + CHECK_FALSE(flags.constructed); + CHECK_TRUE(flags.destructed); + + flags.Clear(); + etl::destroy_object_at(pbuffer3b); + CHECK_FALSE(flags.constructed); + CHECK_TRUE(flags.destructed); + } + + TEST(test_construct_get_destroy_object_misaligned) + { + struct Data + { + Data() + : a(1) + , b(2) + { + } + + Data(int a_, int b_) + : a(a_) + , b(b_) + { + } + + ~Data() + { + } + + int a; + int b; + }; + + alignas(Data) char buffer1[sizeof(Data)]; + char* pbuffer1 = buffer1 + 1; + + alignas(Data) char buffer2[sizeof(Data)]; + char* pbuffer2 = buffer2 + 1; + + alignas(Data) char buffer3[sizeof(Data)]; + char* pbuffer3 = buffer3 + 1; + + CHECK_THROW(etl::construct_object_at(pbuffer1), etl::alignment_error); + + Data data2(3, 4); + CHECK_THROW(etl::construct_object_at(pbuffer2, data2), etl::alignment_error); + + CHECK_THROW(etl::construct_object_at(pbuffer3, 5, 6), etl::alignment_error); + + CHECK_THROW(etl::get_object_at(pbuffer1), etl::alignment_error); + + CHECK_THROW(etl::destroy_object_at(pbuffer1), etl::alignment_error); + } }; }