diff --git a/include/etl/experimental/mem_cast.h b/include/etl/experimental/mem_cast.h index 395899fc..539beeaf 100644 --- a/include/etl/experimental/mem_cast.h +++ b/include/etl/experimental/mem_cast.h @@ -32,14 +32,59 @@ SOFTWARE. #define ETL_MEM_CAST_INCLUDED #include +#include #include "../platform.h" #include "../memory.h" #include "../static_assert.h" #include "../largest.h" +#include "../utility.h" +#include "../placement_new.h" +#include "../exception.h" +#include "../error_handler.h" +#include "../file_error_numbers.h" namespace etl { + //*************************************************************************** + /// The base class for array_wrapper exceptions. + //*************************************************************************** + class mem_cast_exception : public exception + { + public: + + mem_cast_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// The exception thrown when the buffer pointer alignment is not compatible. + //*************************************************************************** + class mem_cast_alignment_exception : public mem_cast_exception + { + public: + + mem_cast_alignment_exception(string_type file_name_, numeric_type line_number_) + : mem_cast_exception(ETL_ERROR_TEXT("mem_cast:alignment", ETL_MEM_CAST_FILE_ID"A"), file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// The exception thrown when the pointer is null. + //*************************************************************************** + class mem_cast_nullptr_exception : public mem_cast_exception + { + public: + + mem_cast_nullptr_exception(string_type file_name_, numeric_type line_number_) + : mem_cast_exception(ETL_ERROR_TEXT("mem_cast:null pointer", ETL_MEM_CAST_FILE_ID"B"), file_name_, line_number_) + { + } + }; + //***************************************************************************** /// mem_cast //***************************************************************************** @@ -51,14 +96,20 @@ namespace etl static ETL_CONSTANT size_t Size = Size_; static ETL_CONSTANT size_t Alignment = Alignment_; + //*********************************** + /// Default constructor //*********************************** ETL_CONSTEXPR mem_cast() + : buffer() { } + //*********************************** + /// Copy constructor //*********************************** template ETL_CONSTEXPR mem_cast(const mem_cast& other) + : type_size(other.type_size) { ETL_STATIC_ASSERT(Size >= Other_Size, "Other size is too large"); ETL_STATIC_ASSERT(Alignment >= Other_Alignment, "Other alignment incompatible"); @@ -66,9 +117,22 @@ namespace etl memcpy(buffer, other.buffer, Size_); } +#if ETL_CPP11_SUPPORTED + //*********************************** + /// Emplace from parameters + //*********************************** + template + void emplace(TArgs... args) + { + ::new (static_cast(buffer)) T(etl::forward(args)...); + } +#endif + + //*********************************** + /// Get a reference to T //*********************************** template - ETL_CONSTEXPR T& get() + ETL_CONSTEXPR T& ref() { ETL_STATIC_ASSERT(sizeof(T) <= Size, "Size of T is too large"); ETL_STATIC_ASSERT(Alignment >= etl::alignment_of::value, "Alignment of T is incompatible"); @@ -76,26 +140,20 @@ namespace etl return *static_cast(buffer); } + //*********************************** + /// Get a const reference to T //*********************************** template - ETL_CONSTEXPR const T& get() const + ETL_CONSTEXPR const T& ref() const { ETL_STATIC_ASSERT(sizeof(T) <= Size, "Size of T is too large"); ETL_STATIC_ASSERT(Alignment >= etl::alignment_of::value, "Alignment of T is incompatible"); - return *static_cast(buffer); + return *static_cast(buffer); } //*********************************** - template - ETL_CONSTEXPR operator T() const - { - ETL_STATIC_ASSERT(sizeof(T) <= Size, "Size of T is too large"); - ETL_STATIC_ASSERT(Alignment >= etl::alignment_of::value, "Alignment of T is incompatible"); - - return *static_cast(buffer); - } - + /// Assignment operator //*********************************** template ETL_CONSTEXPR mem_cast& operator =(const mem_cast& rhs) @@ -108,24 +166,32 @@ namespace etl return *this; } + //*********************************** + /// Get the size of the buffer //*********************************** ETL_CONSTEXPR size_t size() const { return Size; } + //*********************************** + /// Get the alignment of the buffer //*********************************** ETL_CONSTEXPR size_t alignment() const { return Alignment; } + //*********************************** + /// Get a pointer to the internal buffer //*********************************** ETL_CONSTEXPR char* data() { return buffer; } + //*********************************** + /// Get a const pointer to the internal buffer //*********************************** ETL_CONSTEXPR const char* data() const { @@ -134,6 +200,7 @@ namespace etl private: + /// The internal buffer etl::uninitialized_buffer buffer; }; @@ -147,65 +214,78 @@ namespace etl static ETL_CONSTANT size_t Size = Size_; + //*********************************** + /// Default constructor //*********************************** ETL_CONSTEXPR mem_cast_ptr() : pbuffer(ETL_NULLPTR) { } + //*********************************** + /// Construct with pointer to buffer //*********************************** ETL_CONSTEXPR mem_cast_ptr(char* pbuffer_) : pbuffer(pbuffer_) { } + //*********************************** + /// Copy construct with pointer to buffer //*********************************** template ETL_CONSTEXPR mem_cast_ptr(const mem_cast_ptr& other, char* pbuffer_) : pbuffer(pbuffer_) { + ETL_ASSERT((pbuffer != ETL_NULLPTR), ETL_ERROR(etl::mem_cast_nullptr_exception)); ETL_STATIC_ASSERT(Size >= Other_Size, "Other size is too large"); - memcpy(buffer, other.buffer, Size_); + memcpy(pbuffer, other.pbuffer, Size_); } +#if ETL_CPP11_SUPPORTED + //*********************************** + /// Emplace from parameters + //*********************************** + template + void emplace(TArgs... args) + { + ETL_ASSERT((pbuffer != ETL_NULLPTR), ETL_ERROR(etl::mem_cast_nullptr_exception)); + ETL_ASSERT((uintptr_t(pbuffer) % etl::alignment_of::value) == 0, ETL_ERROR(etl::mem_cast_alignment_exception)); + + ::new (pbuffer) T(etl::forward(args)...); + } +#endif + //*********************************** - void set(char* pbuffer_) - { - pbuffer = pbuffer_; - } - + /// Get a reference to T //*********************************** template - ETL_CONSTEXPR T& get() + ETL_CONSTEXPR T& ref() { - ETL_STATIC_ASSERT((uintptr_t(pbuffer) % etl::alignment_of::value) == 0, "Alignment of T is incompatible"); + ETL_ASSERT((pbuffer != ETL_NULLPTR), ETL_ERROR(etl::mem_cast_nullptr_exception)); + ETL_ASSERT((uintptr_t(pbuffer) % etl::alignment_of::value) == 0, ETL_ERROR(etl::mem_cast_alignment_exception)); return *reinterpret_cast(pbuffer); } //*********************************** - template - ETL_CONSTEXPR const T& get() const - { - ETL_STATIC_ASSERT((uintptr_t(pbuffer) % etl::alignment_of::value) == 0, "Alignment of T is incompatible"); - - return *reinterpret_cast(pbuffer); - } - + /// Get a const reference to T //*********************************** template - ETL_CONSTEXPR operator T() const + ETL_CONSTEXPR const T& ref() const { - ETL_STATIC_ASSERT((uintptr_t(pbuffer) % etl::alignment_of::value) == 0, "Alignment of T is incompatible"); + ETL_ASSERT((pbuffer != ETL_NULLPTR), ETL_ERROR(etl::mem_cast_nullptr_exception)); + ETL_ASSERT((uintptr_t(pbuffer) % etl::alignment_of::value) == 0, ETL_ERROR(etl::mem_cast_alignment_exception)); - return *reinterpret_cast(pbuffer); + return *reinterpret_cast(pbuffer); } //*********************************** template mem_cast_ptr& operator =(const mem_cast_ptr& rhs) { + ETL_ASSERT((pbuffer != ETL_NULLPTR), ETL_ERROR(etl::mem_cast_nullptr_exception)); ETL_STATIC_ASSERT(Size >= Other_Size, "RHS size is too large"); memcpy(pbuffer, rhs.pbuffer, Size_); @@ -213,12 +293,24 @@ namespace etl return *this; } + //*********************************** + /// Assignment operator //*********************************** ETL_CONSTEXPR size_t size() const { return Size; } + //*********************************** + /// Get a pointer to the internal buffer + //*********************************** + void data(char* pbuffer_) + { + pbuffer = pbuffer_; + } + + //*********************************** + /// Get a const pointer to the internal buffer //*********************************** ETL_CONSTEXPR char* data() const { @@ -227,18 +319,17 @@ namespace etl private: + /// Pointer to the buffer char* pbuffer; }; #if ETL_CPP11_SUPPORTED //***************************************************************************** /// mem_cast_var + /// mem_cast from a variadic list of types //***************************************************************************** template - class mem_cast_types : public etl::mem_cast::size, - etl::largest::alignment> - { - }; + using mem_cast_types = etl::mem_cast::size, etl::largest::alignment>; #endif } diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index a72f34de..86a31fbb 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -92,5 +92,6 @@ SOFTWARE. #define ETL_QUEUE_SPSC_LOCKABLE_FILE_ID "59" #define ETL_MESSAGE_ROUTER_REGISTRY_FILE_ID "60" #define ETL_ARRAY_WRAPPER_FILE_ID "61" +#define ETL_MEM_CAST_FILE_ID "62" #endif diff --git a/test/test_mem_cast.cpp b/test/test_mem_cast.cpp index 56c95841..11d10af4 100644 --- a/test/test_mem_cast.cpp +++ b/test/test_mem_cast.cpp @@ -40,6 +40,20 @@ namespace { struct Data { + Data() + : c(0) + , d(0) + , a() + { + } + + Data(char c_, double d_, std::array a_) + : c(c_) + , d(d_) + , a(a_) + { + } + char c; double d; std::array a; @@ -120,22 +134,76 @@ namespace CHECK(alignof(Data) <= memCastTypes.alignment()); } + //************************************************************************* + TEST(test_mem_cast_emplace_type) + { + MemCast memCast; + + memCast.emplace(123); + CHECK_EQUAL(123, memCast.ref()); + + memCast.emplace(1.23); + CHECK_EQUAL(1.23, memCast.ref()); + + memCast.emplace(123, 1.23, std::array{ 1, 2, 3 }); + CHECK(123 == memCast.ref().c); + CHECK(1.23 == memCast.ref().d); + CHECK((std::array { 1, 2, 3 }) == memCast.ref().a); + } + //************************************************************************* TEST(test_mem_cast_to_type) { - MemCast memCast; + MemCast memCast; char* pbuffer = memCast.data(); *pbuffer = 123; - CHECK_EQUAL(123, memCast.get()); + CHECK_EQUAL(123, memCast.ref()); *reinterpret_cast(pbuffer) = 1.23; - CHECK_EQUAL(1.23, memCast.get()); + CHECK_EQUAL(1.23, memCast.ref()); *reinterpret_cast(pbuffer) = { 123, 1.23, { 1, 2, 3 } }; - CHECK(123 == memCast.get().c); - CHECK(1.23 == memCast.get().d); - CHECK((std::array { 1, 2, 3 }) == memCast.get().a); + CHECK(123 == memCast.ref().c); + CHECK(1.23 == memCast.ref().d); + CHECK((std::array { 1, 2, 3 }) == memCast.ref().a); + } + + //************************************************************************* + TEST(test_const_mem_cast_to_type) + { + MemCast memCast; + const MemCast& memCastRef = memCast; + + char* pbuffer = memCast.data(); + *pbuffer = 123; + CHECK_EQUAL(123, memCastRef.ref()); + + *reinterpret_cast(pbuffer) = 1.23; + CHECK_EQUAL(1.23, memCastRef.ref()); + + *reinterpret_cast(pbuffer) = { 123, 1.23, { 1, 2, 3 } }; + CHECK(123 == memCastRef.ref().c); + CHECK(1.23 == memCastRef.ref().d); + CHECK((std::array { 1, 2, 3 }) == memCastRef.ref().a); + } + + //************************************************************************* + TEST(test_mem_cast_types_to_type) + { + MemCastTypes memCastTypes; + + char* pbuffer = memCastTypes.data(); + *pbuffer = 123; + CHECK_EQUAL(123, memCastTypes.ref()); + + *reinterpret_cast(pbuffer) = 1.23; + CHECK_EQUAL(1.23, memCastTypes.ref()); + + *reinterpret_cast(pbuffer) = { 123, 1.23, { 1, 2, 3 } }; + CHECK(123 == memCastTypes.ref().c); + CHECK(1.23 == memCastTypes.ref().d); + CHECK((std::array { 1, 2, 3 }) == memCastTypes.ref().a); } }; } diff --git a/test/test_mem_cast_ptr.cpp b/test/test_mem_cast_ptr.cpp new file mode 100644 index 00000000..08f8cebc --- /dev/null +++ b/test/test_mem_cast_ptr.cpp @@ -0,0 +1,189 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2021 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/experimental/mem_cast.h" +#include "etl/largest.h" + +#include +#include +#include +#include + +namespace +{ + struct Data + { + Data() + : c(0) + , d(0) + , a() + { + } + + Data(char c_, double d_, std::array a_) + : c(c_) + , d(d_) + , a(a_) + { + } + + char c; + double d; + std::array a; + }; + + constexpr size_t Size = etl::largest::size; + constexpr size_t Alignment = etl::largest::alignment; + + // Test variant types. + using MemCast = etl::mem_cast_ptr; + + char c; + double d; + Data data; + + std::aligned_storage_t buffer; + + SUITE(test_mem_cast) + { + //************************************************************************* + TEST(test_size) + { + MemCast memCast; + + CHECK(sizeof(char) <= MemCast::Size); + CHECK(sizeof(short) <= MemCast::Size); + CHECK(sizeof(Data) <= MemCast::Size); + + CHECK(sizeof(char) <= memCast.Size); + CHECK(sizeof(short) <= memCast.Size); + CHECK(sizeof(Data) <= memCast.Size); + + CHECK(sizeof(char) <= memCast.size()); + CHECK(sizeof(short) <= memCast.size()); + CHECK(sizeof(Data) <= memCast.size()); + } + + //************************************************************************* + TEST(test_mem_cast_emplace_type) + { + char* pbuffer = reinterpret_cast(&buffer); + + MemCast memCast(pbuffer); + + memCast.emplace(123); + CHECK_EQUAL(123, memCast.ref()); + + memCast.emplace(1.23); + CHECK_EQUAL(1.23, memCast.ref()); + + memCast.emplace(123, 1.23, std::array{ 1, 2, 3 }); + CHECK(123 == memCast.ref().c); + CHECK(1.23 == memCast.ref().d); + CHECK((std::array { 1, 2, 3 }) == memCast.ref().a); + } + + //************************************************************************* + TEST(test_mem_cast_to_type) + { + char* pbuffer = reinterpret_cast(&buffer); + + MemCast memCast(pbuffer); + + pbuffer = memCast.data(); + *pbuffer = 123; + CHECK_EQUAL(123, memCast.ref()); + + *reinterpret_cast(pbuffer) = 1.23; + CHECK_EQUAL(1.23, memCast.ref()); + + *reinterpret_cast(pbuffer) = { 123, 1.23, { 1, 2, 3 } }; + CHECK(123 == memCast.ref().c); + CHECK(1.23 == memCast.ref().d); + CHECK((std::array { 1, 2, 3 }) == memCast.ref().a); + } + + //************************************************************************* + TEST(test_const_mem_cast_to_type) + { + char* pbuffer = reinterpret_cast(&buffer); + + const MemCast memCast(pbuffer); + + pbuffer = memCast.data(); + *pbuffer = 123; + CHECK_EQUAL(123, memCast.ref()); + + *reinterpret_cast(pbuffer) = 1.23; + CHECK_EQUAL(1.23, memCast.ref()); + + *reinterpret_cast(pbuffer) = { 123, 1.23, { 1, 2, 3 } }; + CHECK(123 == memCast.ref().c); + CHECK(1.23 == memCast.ref().d); + CHECK((std::array { 1, 2, 3 }) == memCast.ref().a); + } + + //************************************************************************* + TEST(test_mem_cast_to_type_no_buffer) + { + MemCast memCast; + + CHECK_THROW(memCast.ref(), etl::mem_cast_nullptr_exception); + } + + //************************************************************************* + TEST(test_const_mem_cast_to_type_no_buffer) + { + const MemCast memCast; + + CHECK_THROW(memCast.ref(), etl::mem_cast_nullptr_exception); + } + + //************************************************************************* + TEST(test_mem_cast_to_type_misaligned_buffer) + { + double d; + char* pbuffer = reinterpret_cast(&d); + MemCast memCast(pbuffer + 1); + + CHECK_THROW(memCast.ref(), etl::mem_cast_alignment_exception); + } + + //************************************************************************* + TEST(test_const_mem_cast_to_type_misaligned_buffer) + { + double d; + char* pbuffer = reinterpret_cast(&d); + const MemCast memCast(pbuffer + 1); + + CHECK_THROW(memCast.ref(), etl::mem_cast_alignment_exception); + } + }; +} diff --git a/test/vs2019/etl.vcxproj b/test/vs2019/etl.vcxproj index 81b374c5..37c264bc 100644 --- a/test/vs2019/etl.vcxproj +++ b/test/vs2019/etl.vcxproj @@ -4496,6 +4496,7 @@ + diff --git a/test/vs2019/etl.vcxproj.filters b/test/vs2019/etl.vcxproj.filters index 3c1d4a64..b66174cf 100644 --- a/test/vs2019/etl.vcxproj.filters +++ b/test/vs2019/etl.vcxproj.filters @@ -2537,6 +2537,9 @@ Source Files + + Source Files +