diff --git a/include/etl/alignment.h b/include/etl/alignment.h index c6a4de06..47ae7744 100644 --- a/include/etl/alignment.h +++ b/include/etl/alignment.h @@ -36,6 +36,7 @@ SOFTWARE. #include "static_assert.h" #include "error_handler.h" #include "exception.h" +#include "utility.h" #include @@ -71,6 +72,19 @@ namespace etl } }; + //*************************************************************************** + /// Typed storage exception. + //*************************************************************************** + class typed_storage_error : public alignment_exception + { + public: + + typed_storage_error(string_type file_name_, numeric_type line_number_) + : alignment_exception(ETL_ERROR_TEXT("typed_storage:error", ETL_ALIGNMENT_FILE_ID"B"), file_name_, line_number_) + { + } + }; + //***************************************************************************** /// Check that 'p' has 'required_alignment'. //***************************************************************************** @@ -333,6 +347,109 @@ namespace etl #if ETL_USING_CPP11 template using aligned_storage_as_t = typename aligned_storage_as::type; + + //*************************************************************************** + /// Wrapper class that provides a memory area and lets the user emplace and + /// 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 + { + public: + + using value_type = T; + using reference = T&; + using const_reference = T const&; + using pointer = T*; + using const_pointer = T const*; + + // Constructor + typed_storage() + : valid(false) + { + } + + //*************************************************************************** + /// Default destructor which will NOT call the destructor of the object which + /// was created by calling emplace(). + //*************************************************************************** + ~typed_storage() = default; + + //*************************************************************************** + /// Calls the destructor of the wrapped object and asserts if has_value() is false. + //*************************************************************************** + void destroy() + { + ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); + data.template get_reference().~T(); + valid = false; + } + + //*************************************************************************** + /// \returns true if object has been constructed using emplace(). + /// \returns false otherwise. + //*************************************************************************** + bool has_value() const + { + return valid; + } + + //*************************************************************************** + /// Constructs the instance of T forwarding the given \p args to its constructor and + /// asserts if has_value() is true before calling emplace(). + /// + /// \returns the instance of T which has been constructed in the internal byte array. + //*************************************************************************** + template + reference create(Args&&... args) + { + ETL_ASSERT(!has_value(), ETL_ERROR(etl::typed_storage_error)); + valid = true; + return *::new (data.template get_address()) value_type(etl::forward(args)...); + } + + //*************************************************************************** + /// \returns a pointer of type T and asserts if has_value() is false. + //*************************************************************************** + pointer operator->() + { + ETL_ASSERT(has_value(), ETL_ERROR(etl::typed_storage_error)); + return data.template get_address(); + } + + //*************************************************************************** + /// \returns a const pointer of type T and asserts if has_value() is false. + //*************************************************************************** + const_pointer operator->() const + { + return operator->(); + } + + //*************************************************************************** + /// \returns reference of type T and asserts if has_value() is false. + //*************************************************************************** + reference operator*() + { + return *operator->(); + } + + //*************************************************************************** + /// \returns const reference of type T and asserts if has_value() is false. + //*************************************************************************** + const_reference operator*() const + { + return *operator->(); + } + + private: + + typename aligned_storage_as::type data; + bool valid; + }; #endif } diff --git a/test/test_alignment.cpp b/test/test_alignment.cpp index 29de827a..7563bbbc 100644 --- a/test/test_alignment.cpp +++ b/test/test_alignment.cpp @@ -48,6 +48,23 @@ void f(int) { } +struct A_t +{ + A_t(uint32_t v_x, uint8_t v_y) + : x(v_x) + , y(v_y) + { + } + + bool operator==(A_t& other) + { + return other.x == x && other.y == y; + } + + uint32_t x; + uint8_t y; +}; + namespace { SUITE(test_alignment) @@ -155,5 +172,31 @@ namespace CHECK_EQUAL(32, alignof(etl::type_with_alignment_t<32>)); CHECK_EQUAL(64, alignof(etl::type_with_alignment_t<64>)); } + + //************************************************************************* +#if ETL_USING_CPP11 + TEST(test_typed_storage) + { + etl::typed_storage a; + + CHECK_EQUAL(false, a.has_value()); + + auto& b = a.create(123, 4); + + CHECK_EQUAL(true, a.has_value()); + + CHECK_EQUAL(a->x, 123); + CHECK_EQUAL(a->y, 4); + + CHECK_EQUAL(b.x, 123); + CHECK_EQUAL(b.y, 4); + + CHECK_TRUE(*a == b); + + CHECK_EQUAL(true, a.has_value()); + a.destroy(); + CHECK_EQUAL(false, a.has_value()); + } +#endif }; }