Updates to mem_cast

This commit is contained in:
John Wellbelove 2021-05-30 12:08:03 +01:00
parent abf8d7bb14
commit 8af219c0b3
6 changed files with 394 additions and 41 deletions

View File

@ -32,14 +32,59 @@ SOFTWARE.
#define ETL_MEM_CAST_INCLUDED
#include <stdint.h>
#include <string.h>
#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 <size_t Other_Size, size_t Other_Alignment>
ETL_CONSTEXPR mem_cast(const mem_cast<Other_Size, Other_Alignment>& 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 <typename T, typename... TArgs>
void emplace(TArgs... args)
{
::new (static_cast<void*>(buffer)) T(etl::forward<TArgs>(args)...);
}
#endif
//***********************************
/// Get a reference to T
//***********************************
template <typename T>
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<T>::value, "Alignment of T is incompatible");
@ -76,26 +140,20 @@ namespace etl
return *static_cast<T*>(buffer);
}
//***********************************
/// Get a const reference to T
//***********************************
template <typename T>
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<T>::value, "Alignment of T is incompatible");
return *static_cast<T*>(buffer);
return *static_cast<const T*>(buffer);
}
//***********************************
template <typename T>
ETL_CONSTEXPR operator T() const
{
ETL_STATIC_ASSERT(sizeof(T) <= Size, "Size of T is too large");
ETL_STATIC_ASSERT(Alignment >= etl::alignment_of<T>::value, "Alignment of T is incompatible");
return *static_cast<T*>(buffer);
}
/// Assignment operator
//***********************************
template <size_t Other_Size, size_t Other_Alignment>
ETL_CONSTEXPR mem_cast& operator =(const mem_cast<Other_Size, Other_Alignment>& 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<Size, 1U, Alignment> 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 <size_t Other_Size>
ETL_CONSTEXPR mem_cast_ptr(const mem_cast_ptr<Other_Size>& 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 <typename T, typename... TArgs>
void emplace(TArgs... args)
{
ETL_ASSERT((pbuffer != ETL_NULLPTR), ETL_ERROR(etl::mem_cast_nullptr_exception));
ETL_ASSERT((uintptr_t(pbuffer) % etl::alignment_of<T>::value) == 0, ETL_ERROR(etl::mem_cast_alignment_exception));
::new (pbuffer) T(etl::forward<TArgs>(args)...);
}
#endif
//***********************************
void set(char* pbuffer_)
{
pbuffer = pbuffer_;
}
/// Get a reference to T
//***********************************
template <typename T>
ETL_CONSTEXPR T& get()
ETL_CONSTEXPR T& ref()
{
ETL_STATIC_ASSERT((uintptr_t(pbuffer) % etl::alignment_of<T>::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<T>::value) == 0, ETL_ERROR(etl::mem_cast_alignment_exception));
return *reinterpret_cast<T*>(pbuffer);
}
//***********************************
template <typename T>
ETL_CONSTEXPR const T& get() const
{
ETL_STATIC_ASSERT((uintptr_t(pbuffer) % etl::alignment_of<T>::value) == 0, "Alignment of T is incompatible");
return *reinterpret_cast<T*>(pbuffer);
}
/// Get a const reference to T
//***********************************
template <typename T>
ETL_CONSTEXPR operator T() const
ETL_CONSTEXPR const T& ref() const
{
ETL_STATIC_ASSERT((uintptr_t(pbuffer) % etl::alignment_of<T>::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<T>::value) == 0, ETL_ERROR(etl::mem_cast_alignment_exception));
return *reinterpret_cast<T*>(pbuffer);
return *reinterpret_cast<const T*>(pbuffer);
}
//***********************************
template <size_t Other_Size>
mem_cast_ptr& operator =(const mem_cast_ptr<Other_Size>& 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 <typename... TTypes>
class mem_cast_types : public etl::mem_cast<etl::largest<TTypes...>::size,
etl::largest<TTypes...>::alignment>
{
};
using mem_cast_types = etl::mem_cast<etl::largest<TTypes...>::size, etl::largest<TTypes...>::alignment>;
#endif
}

View File

@ -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

View File

@ -40,6 +40,20 @@ namespace
{
struct Data
{
Data()
: c(0)
, d(0)
, a()
{
}
Data(char c_, double d_, std::array<int, 3> a_)
: c(c_)
, d(d_)
, a(a_)
{
}
char c;
double d;
std::array<int, 3> a;
@ -120,22 +134,76 @@ namespace
CHECK(alignof(Data) <= memCastTypes.alignment());
}
//*************************************************************************
TEST(test_mem_cast_emplace_type)
{
MemCast memCast;
memCast.emplace<char>(123);
CHECK_EQUAL(123, memCast.ref<char>());
memCast.emplace<double>(1.23);
CHECK_EQUAL(1.23, memCast.ref<double>());
memCast.emplace<Data>(123, 1.23, std::array<int, 3>{ 1, 2, 3 });
CHECK(123 == memCast.ref<Data>().c);
CHECK(1.23 == memCast.ref<Data>().d);
CHECK((std::array { 1, 2, 3 }) == memCast.ref<Data>().a);
}
//*************************************************************************
TEST(test_mem_cast_to_type)
{
MemCast memCast;
MemCast memCast;
char* pbuffer = memCast.data();
*pbuffer = 123;
CHECK_EQUAL(123, memCast.get<char>());
CHECK_EQUAL(123, memCast.ref<char>());
*reinterpret_cast<double*>(pbuffer) = 1.23;
CHECK_EQUAL(1.23, memCast.get<double>());
CHECK_EQUAL(1.23, memCast.ref<double>());
*reinterpret_cast<Data*>(pbuffer) = { 123, 1.23, { 1, 2, 3 } };
CHECK(123 == memCast.get<Data>().c);
CHECK(1.23 == memCast.get<Data>().d);
CHECK((std::array { 1, 2, 3 }) == memCast.get<Data>().a);
CHECK(123 == memCast.ref<Data>().c);
CHECK(1.23 == memCast.ref<Data>().d);
CHECK((std::array { 1, 2, 3 }) == memCast.ref<Data>().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<char>());
*reinterpret_cast<double*>(pbuffer) = 1.23;
CHECK_EQUAL(1.23, memCastRef.ref<double>());
*reinterpret_cast<Data*>(pbuffer) = { 123, 1.23, { 1, 2, 3 } };
CHECK(123 == memCastRef.ref<Data>().c);
CHECK(1.23 == memCastRef.ref<Data>().d);
CHECK((std::array { 1, 2, 3 }) == memCastRef.ref<Data>().a);
}
//*************************************************************************
TEST(test_mem_cast_types_to_type)
{
MemCastTypes memCastTypes;
char* pbuffer = memCastTypes.data();
*pbuffer = 123;
CHECK_EQUAL(123, memCastTypes.ref<char>());
*reinterpret_cast<double*>(pbuffer) = 1.23;
CHECK_EQUAL(1.23, memCastTypes.ref<double>());
*reinterpret_cast<Data*>(pbuffer) = { 123, 1.23, { 1, 2, 3 } };
CHECK(123 == memCastTypes.ref<Data>().c);
CHECK(1.23 == memCastTypes.ref<Data>().d);
CHECK((std::array { 1, 2, 3 }) == memCastTypes.ref<Data>().a);
}
};
}

189
test/test_mem_cast_ptr.cpp Normal file
View File

@ -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 <array>
#include <vector>
#include <algorithm>
#include <string>
namespace
{
struct Data
{
Data()
: c(0)
, d(0)
, a()
{
}
Data(char c_, double d_, std::array<int, 3> a_)
: c(c_)
, d(d_)
, a(a_)
{
}
char c;
double d;
std::array<int, 3> a;
};
constexpr size_t Size = etl::largest<char, double, Data>::size;
constexpr size_t Alignment = etl::largest<char, double, Data>::alignment;
// Test variant types.
using MemCast = etl::mem_cast_ptr<Size>;
char c;
double d;
Data data;
std::aligned_storage_t<Size, Alignment> 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<char*>(&buffer);
MemCast memCast(pbuffer);
memCast.emplace<char>(123);
CHECK_EQUAL(123, memCast.ref<char>());
memCast.emplace<double>(1.23);
CHECK_EQUAL(1.23, memCast.ref<double>());
memCast.emplace<Data>(123, 1.23, std::array<int, 3>{ 1, 2, 3 });
CHECK(123 == memCast.ref<Data>().c);
CHECK(1.23 == memCast.ref<Data>().d);
CHECK((std::array { 1, 2, 3 }) == memCast.ref<Data>().a);
}
//*************************************************************************
TEST(test_mem_cast_to_type)
{
char* pbuffer = reinterpret_cast<char*>(&buffer);
MemCast memCast(pbuffer);
pbuffer = memCast.data();
*pbuffer = 123;
CHECK_EQUAL(123, memCast.ref<char>());
*reinterpret_cast<double*>(pbuffer) = 1.23;
CHECK_EQUAL(1.23, memCast.ref<double>());
*reinterpret_cast<Data*>(pbuffer) = { 123, 1.23, { 1, 2, 3 } };
CHECK(123 == memCast.ref<Data>().c);
CHECK(1.23 == memCast.ref<Data>().d);
CHECK((std::array { 1, 2, 3 }) == memCast.ref<Data>().a);
}
//*************************************************************************
TEST(test_const_mem_cast_to_type)
{
char* pbuffer = reinterpret_cast<char*>(&buffer);
const MemCast memCast(pbuffer);
pbuffer = memCast.data();
*pbuffer = 123;
CHECK_EQUAL(123, memCast.ref<char>());
*reinterpret_cast<double*>(pbuffer) = 1.23;
CHECK_EQUAL(1.23, memCast.ref<double>());
*reinterpret_cast<Data*>(pbuffer) = { 123, 1.23, { 1, 2, 3 } };
CHECK(123 == memCast.ref<Data>().c);
CHECK(1.23 == memCast.ref<Data>().d);
CHECK((std::array { 1, 2, 3 }) == memCast.ref<Data>().a);
}
//*************************************************************************
TEST(test_mem_cast_to_type_no_buffer)
{
MemCast memCast;
CHECK_THROW(memCast.ref<char>(), etl::mem_cast_nullptr_exception);
}
//*************************************************************************
TEST(test_const_mem_cast_to_type_no_buffer)
{
const MemCast memCast;
CHECK_THROW(memCast.ref<char>(), etl::mem_cast_nullptr_exception);
}
//*************************************************************************
TEST(test_mem_cast_to_type_misaligned_buffer)
{
double d;
char* pbuffer = reinterpret_cast<char*>(&d);
MemCast memCast(pbuffer + 1);
CHECK_THROW(memCast.ref<double>(), etl::mem_cast_alignment_exception);
}
//*************************************************************************
TEST(test_const_mem_cast_to_type_misaligned_buffer)
{
double d;
char* pbuffer = reinterpret_cast<char*>(&d);
const MemCast memCast(pbuffer + 1);
CHECK_THROW(memCast.ref<double>(), etl::mem_cast_alignment_exception);
}
};
}

View File

@ -4496,6 +4496,7 @@
<ClCompile Include="..\test_make_string.cpp" />
<ClCompile Include="..\test_mean.cpp" />
<ClCompile Include="..\test_mem_cast.cpp" />
<ClCompile Include="..\test_mem_cast_ptr.cpp" />
<ClCompile Include="..\test_message_packet.cpp" />
<ClCompile Include="..\test_message_router_registry.cpp" />
<ClCompile Include="..\test_multi_array.cpp" />

View File

@ -2537,6 +2537,9 @@
<ClCompile Include="..\test_mem_cast.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\test_mem_cast_ptr.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\library.properties">