Added etl::typed_storage_ext and swap for same

This commit is contained in:
John Wellbelove 2025-08-28 10:30:24 +01:00
parent aea6e7da8b
commit 33302790ba
2 changed files with 299 additions and 13 deletions

View File

@ -37,6 +37,7 @@ SOFTWARE.
#include "error_handler.h"
#include "exception.h"
#include "utility.h"
#include "algorithm.h"
#include <stdint.h>
@ -245,11 +246,6 @@ namespace etl
{
struct type
{
//type()
// : data()
//{
//}
/// Convert to T reference.
template <typename T>
operator T& ()
@ -382,6 +378,7 @@ namespace etl
//***************************************************************************
template <typename... TArgs>
typed_storage(TArgs&&... args) ETL_NOEXCEPT_IF_NO_THROW
: valid(false)
{
create(etl::forward<TArgs>(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 <typename T>
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 <typename U>
friend ETL_CONSTEXPR14 void swap(typed_storage_ext<U>& lhs, typed_storage_ext<U>& rhs) ETL_NOEXCEPT;
//***************************************************************************
/// Constructor.
//***************************************************************************
template <typename... TArgs>
typed_storage_ext(void* pbuffer_) ETL_NOEXCEPT_IF_NO_THROW
: pbuffer(reinterpret_cast<T*>(pbuffer_)),
valid(false)
{
ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of<T>::value), ETL_ERROR(etl::alignment_error));
}
#if ETL_USING_CPP11
//***************************************************************************
/// Constructs the instance of T forwarding the given \p args to its constructor.
//***************************************************************************
template <typename... TArgs>
typed_storage_ext(void* pbuffer_, TArgs&&... args) ETL_NOEXCEPT_IF_NO_THROW
: pbuffer(reinterpret_cast<T*>(pbuffer_))
, valid(false)
{
ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of<T>::value), ETL_ERROR(etl::alignment_error));
create(etl::forward<TArgs>(args)...);
}
#else
//***************************************************************************
/// Constructs the instance of T with type T1
//***************************************************************************
typed_storage_ext(void* pbuffer_, const T1& t1)
: pbuffer(reinterpret_cast<T*>(pbuffer_))
, valid(false)
{
ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of<T>::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<T*>(pbuffer_))
, valid(false)
{
ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of<T>::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<T*>(pbuffer_))
, valid(false)
{
ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of<T>::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<T*>(pbuffer_))
, valid(false)
{
ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of<T>::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<T*>(pbuffer_))
, valid(false)
{
ETL_ASSERT(etl::is_aligned(pbuffer_, etl::alignment_of<T>::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 <b>true</b> if object has been constructed using create().
/// \returns <b>false</b> 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 <typename... TArgs>
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<TArgs>(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 <typename T1>
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 <typename T1, typename T2>
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 <typename T1, typename T2, typename T3>
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 <typename T1, typename T2, typename T3, typename T4>
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<T>&) ETL_DELETE;
typed_storage_ext& operator =(etl::typed_storage_ext<T>&) ETL_DELETE;
T* pbuffer;
bool valid;
};
//***************************************************************************
/// Swap two etl::typed_storage_ext
//***************************************************************************
template <typename T>
ETL_CONSTEXPR14 void swap(etl::typed_storage_ext<T>& lhs, etl::typed_storage_ext<T>& rhs) ETL_NOEXCEPT
{
using ETL_OR_STD::swap;
swap(lhs.pbuffer, rhs.pbuffer);
swap(lhs.valid, rhs.valid);
}
}
#endif

View File

@ -196,7 +196,7 @@ namespace
etl::typed_storage<A_t> a;
CHECK_FALSE(a.has_value());
etl::typed_storage<A_t> b(789, 10); // Construct in place.
etl::typed_storage<A_t> 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_t> a(buffer1);
CHECK_FALSE(a.has_value());
etl::typed_storage_ext<A_t> 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());