From 373247e5c15e4342502848fd2175decbafc58895 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 18 May 2026 08:14:26 +0200 Subject: [PATCH] Make etl::variant capable for ROM placement and optimize runtime size (#1441) Via an variadic_union, the internal storage is adjusted to be able to be constexpr. --- include/etl/private/variant_variadic.h | 936 ++++++++++++++----------- test/test_variant_variadic.cpp | 169 ++++- 2 files changed, 705 insertions(+), 400 deletions(-) diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 3a95502a..3b621582 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -47,6 +47,7 @@ SOFTWARE. #include "../visitor.h" #include +#include #if defined(ETL_COMPILER_KEIL) #pragma diag_suppress 940 @@ -60,8 +61,7 @@ SOFTWARE. #else //***************************************************************************** ///\defgroup variant variant -/// A class that can contain one a several specified types in a type safe -/// manner. +/// A class that can contain one a several specified types in a type safe manner. ///\ingroup containers //***************************************************************************** @@ -70,171 +70,188 @@ namespace etl namespace private_variant { //******************************************* - // The traits an object may have. + /// Switch-based dispatch for destroy/copy/move. + /// Replaces the per-instance function pointer with + /// an inline if-else chain the compiler can optimise + /// into a switch / jump-table, enabling inlining and + /// dead-code elimination. //******************************************* - static constexpr bool Copyable = true; - static constexpr bool Non_Copyable = false; - static constexpr bool Moveable = true; - static constexpr bool Non_Moveable = false; + template + struct variant_operations; + + // Base case: no types left. + template + struct variant_operations + { + static void destroy(char*, size_t) {} + static void copy(char*, const char*, size_t) {} + static void move(char*, const char*, size_t) {} + }; + + // Recursive case. + template + struct variant_operations + { + static void destroy(char* data, size_t type_id) + { + if (type_id == Index) + { + reinterpret_cast(data)->~THead(); + } + else + { + variant_operations::destroy(data, type_id); + } + } + + static void copy(char* dst, const char* src, size_t type_id) + { + if (type_id == Index) + { + copy_impl(dst, src, etl::integral_constant::value>{}); + } + else + { + variant_operations::copy(dst, src, type_id); + } + } + + static void move(char* dst, const char* src, size_t type_id) + { + if (type_id == Index) + { + move_impl(dst, src, etl::integral_constant::value>{}); + } + else + { + variant_operations::move(dst, src, type_id); + } + } + + private: + + static void copy_impl(char* dst, const char* src, etl::true_type) + { + ::new (dst) THead(*reinterpret_cast(src)); + } + + static void copy_impl(char*, const char*, etl::false_type) {} + + static void move_impl(char* dst, const char* src, etl::true_type) + { + ::new (dst) THead(etl::move(*reinterpret_cast(const_cast(src)))); + } + + static void move_impl(char*, const char*, etl::false_type) {} + }; //******************************************* - // The types of operations we can perform. + /// Trait: are all types trivially destructible? //******************************************* - static constexpr int Copy = 0; - static constexpr int Move = 1; - static constexpr int Destroy = 2; + template + struct are_all_trivially_destructible : etl::conjunction...> + { + }; //******************************************* - // operation_type + /// Recursive variadic union for constexpr-friendly storage. + /// Used when all variant types are trivially destructible. //******************************************* - template - struct operation_type; + template + union variadic_union; - //******************************************* - // Specialisation for null operation. + /// Base case: empty union. template <> - struct operation_type + union variadic_union<> { - static void do_operation(int, char*, const char*) + constexpr variadic_union() ETL_NOEXCEPT {} + }; + + /// Recursive case: union of head type and tail union. + template + union variadic_union + { + THead head; + variadic_union tail; + + constexpr variadic_union() ETL_NOEXCEPT + : tail() + { + } + + // Constructor for head element (index 0). + template + constexpr variadic_union(etl::in_place_index_t<0>, T&& value) + : head(etl::forward(value)) + { + } + + // Constructor for tail elements (index > 0). + template + constexpr variadic_union(etl::in_place_index_t, T&& value) + : tail(etl::in_place_index_t{}, etl::forward(value)) { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif } }; //******************************************* - // Specialisation for no-copyable & non-moveable types. - template - struct operation_type - { - static void do_operation(int operation, char* pstorage, const char* /*pvalue*/) - { - switch (operation) - { - case Destroy: - { - reinterpret_cast(pstorage)->~T(); - break; - } - - default: - { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif - break; - } - } - } - }; - + /// Constexpr get by index from variadic_union. //******************************************* - // Specialisation for no-copyable & moveable types. - template - struct operation_type + // Non-const lvalue reference + template + ETL_CONSTEXPR14 typename etl::enable_if_t<(Index == 0), THead&> variadic_union_get(variadic_union& u) ETL_NOEXCEPT { - static void do_operation(int operation, char* pstorage, const char* pvalue) - { - switch (operation) - { - case Move: - { - ::new (pstorage) T(etl::move(*reinterpret_cast(const_cast(pvalue)))); - break; - } + return u.head; + } - case Destroy: - { - reinterpret_cast(pstorage)->~T(); - break; - } - - default: - { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif - break; - } - } - } - }; - - //******************************************* - // Specialisation for copyable & non-moveable types. - template - struct operation_type + template + ETL_CONSTEXPR14 typename etl::enable_if_t<(Index != 0), etl::nth_type_t&> + variadic_union_get(variadic_union& u) ETL_NOEXCEPT { - static void do_operation(int operation, char* pstorage, const char* pvalue) - { - switch (operation) - { - case Copy: - { - ::new (pstorage) T(*reinterpret_cast(pvalue)); - break; - } + return variadic_union_get(u.tail); + } - case Destroy: - { - reinterpret_cast(pstorage)->~T(); - break; - } - - default: - { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif - break; - } - } - } - }; - - //******************************************* - // Specialisation for copyable & moveable types. - template - struct operation_type + // Const lvalue reference + template + constexpr typename etl::enable_if_t<(Index == 0), const THead&> variadic_union_get(const variadic_union& u) ETL_NOEXCEPT { - static void do_operation(int operation, char* pstorage, const char* pvalue) - { - switch (operation) - { - case Copy: - { - ::new (pstorage) T(*reinterpret_cast(pvalue)); - break; - } + return u.head; + } - case Move: - { - ::new (pstorage) T(etl::move(*reinterpret_cast(const_cast(pvalue)))); - break; - } + template + constexpr typename etl::enable_if_t<(Index != 0), const etl::nth_type_t&> + variadic_union_get(const variadic_union& u) ETL_NOEXCEPT + { + return variadic_union_get(u.tail); + } - case Destroy: - { - reinterpret_cast(pstorage)->~T(); - break; - } + // Rvalue reference + template + ETL_CONSTEXPR14 typename etl::enable_if_t<(Index == 0), THead&&> variadic_union_get(variadic_union&& u) ETL_NOEXCEPT + { + return etl::move(u.head); + } - default: - { - // This should never occur. - #if defined(ETL_IN_UNIT_TEST) - assert(false); - #endif - break; - } - } - } - }; + template + ETL_CONSTEXPR14 typename etl::enable_if_t<(Index != 0), etl::nth_type_t&&> + variadic_union_get(variadic_union&& u) ETL_NOEXCEPT + { + return variadic_union_get(etl::move(u.tail)); + } + + // Const rvalue reference + template + constexpr typename etl::enable_if_t<(Index == 0), const THead&&> variadic_union_get(const variadic_union&& u) ETL_NOEXCEPT + { + return etl::move(u.head); + } + + template + constexpr typename etl::enable_if_t<(Index != 0), const etl::nth_type_t&&> + variadic_union_get(const variadic_union&& u) ETL_NOEXCEPT + { + return variadic_union_get(etl::move(u.tail)); + } } // namespace private_variant /// Definition of variant_npos. @@ -387,14 +404,91 @@ namespace etl } }; + namespace private_variant + { + //*************************************************************************** + /// variant_base for non-trivially destructible types. + /// Uses uninitialized_buffer (char array) storage and switch-based + /// dispatch for copy/move/destroy (no per-instance function pointer). + //*************************************************************************** + template + struct variant_base + { + using largest_t = typename largest_type::type; + static const size_t Size = sizeof(largest_t); + static const size_t Alignment = etl::largest_alignment::value; + + etl::uninitialized_buffer data; + size_t type_id; + + ETL_CONSTEXPR14 variant_base() noexcept + : type_id(variant_npos) + { + } + + ETL_CONSTEXPR14 variant_base(size_t id) noexcept + : type_id(id) + { + } + + ~variant_base() + { + if (type_id != variant_npos) + { + variant_operations<0, TTypes...>::destroy(data, type_id); + } + type_id = variant_npos; + } + }; + + //*************************************************************************** + /// variant_base specialisation for trivially destructible types. + /// Uses variadic_union storage. Destructor is trivial (defaulted), making + /// the variant a literal type eligible for constexpr / ROM placement. + /// No operation function pointer is needed since destroy/copy/move are + /// all handled without indirection for trivially destructible types. + //*************************************************************************** + template + struct variant_base + { + variadic_union data; + size_t type_id; + + constexpr variant_base() noexcept + : data() + , type_id(variant_npos) + { + } + + constexpr variant_base(size_t id) noexcept + : data() + , type_id(id) + { + } + + template + constexpr variant_base(etl::in_place_index_t, T&& value, + size_t id) noexcept(etl::is_nothrow_constructible, T>::value) + : data(etl::in_place_index_t{}, etl::forward(value)) + , type_id(id) + { + } + + ~variant_base() = default; + }; + } // namespace private_variant + //*************************************************************************** - /// A template class that can store any of the types defined in the template - /// parameter list. + /// A template class that can store any of the types defined in the template parameter list. ///\ingroup variant //*************************************************************************** template - class variant + class variant : private private_variant::variant_base::value, TTypes...> { + using base_type = private_variant::variant_base::value, TTypes...>; + + static constexpr bool Is_Trivially_Destructible_Suite = private_variant::are_all_trivially_destructible::value; + public: using type_list = etl::type_list; @@ -409,10 +503,10 @@ namespace etl friend ETL_CONSTEXPR14 etl::variant_alternative_t >&& get(etl::variant&& v); template - friend ETL_CONSTEXPR14 const etl::variant_alternative_t< Index, const etl::variant >& get(const etl::variant& v); + friend ETL_CONSTEXPR14 const etl::variant_alternative_t >& get(const etl::variant& v); template - friend ETL_CONSTEXPR14 const etl::variant_alternative_t< Index, const etl::variant >&& get(const etl::variant&& v); + friend ETL_CONSTEXPR14 const etl::variant_alternative_t >&& get(const etl::variant&& v); template friend ETL_CONSTEXPR14 T& get(etl::variant& v); @@ -434,38 +528,6 @@ namespace etl private: - // All types of variant are friends. - template - friend class variant; - - //*************************************************************************** - /// The largest type. - //*************************************************************************** - using largest_t = typename largest_type::type; - - //*************************************************************************** - /// The largest size. - //*************************************************************************** - static const size_t Size = sizeof(largest_t); - - //*************************************************************************** - /// The largest alignment. - //*************************************************************************** - static const size_t Alignment = etl::largest_alignment::value; - - //*************************************************************************** - /// The operation templates. - //*************************************************************************** - template - using operation_type = private_variant::operation_type; - - //******************************************* - // The types of operations we can perform. - //******************************************* - static constexpr int Copy = private_variant::Copy; - static constexpr int Move = private_variant::Move; - static constexpr int Destroy = private_variant::Destroy; - //******************************************* // Get the index of a type. //******************************************* @@ -478,21 +540,32 @@ namespace etl template using type_from_index = typename etl::type_list_type_at_index, Index>::type; + //******************************************* + // Bring base members into scope. + //******************************************* + using base_type::data; + using base_type::type_id; + public: //*************************************************************************** /// Default constructor. - /// Constructs a variant holding the value-initialized value of the first - /// alternative (index() is zero). + /// Constructs a variant holding the value-initialized value of the first alternative (index() is zero). //*************************************************************************** #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant() ETL_NOEXCEPT_IF(etl::is_nothrow_default_constructible >::value) + template = 0> + ETL_CONSTEXPR14 variant() noexcept(etl::is_nothrow_default_constructible >::value) { using type = type_from_index<0U>; default_construct_in_place(data); - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; - type_id = 0U; + type_id = 0U; + } + + template = 0> + constexpr variant() noexcept(etl::is_nothrow_default_constructible >::value) + : base_type(etl::in_place_index_t<0>{}, type_from_index<0U>{}, 0U) + { } #include "diagnostic_pop.h" @@ -500,32 +573,22 @@ namespace etl /// Construct from a value. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - template , variant>::value, int> = 0> - ETL_CONSTEXPR14 variant(T&& value) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, T&&>::value)) - : operation(operation_type< etl::remove_cvref_t, etl::is_copy_constructible >::value, - etl::is_move_constructible >::value>::do_operation) - , type_id(index_of_type::value) + template , variant>::value && !Trivial_, int> = 0> + ETL_CONSTEXPR14 variant(T&& value) + : base_type(index_of_type::value) { static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); construct_in_place >(data, etl::forward(value)); } - #include "diagnostic_pop.h" - //*************************************************************************** - /// Construct from arguments. - //*************************************************************************** - #include "diagnostic_uninitialized_push.h" - template - ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, TArgs&&... args) - ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, TArgs...>::value)) - : operation(operation_type< etl::remove_cvref_t, etl::is_copy_constructible >::value, - etl::is_move_constructible >::value>::do_operation) - , type_id(index_of_type::value) + template , variant>::value && Trivial_, int> = 0> + constexpr variant(T&& value) + : base_type(etl::in_place_index_t::value>{}, etl::forward(value), index_of_type::value) { static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); - - construct_in_place_args >(data, etl::forward(args)...); } #include "diagnostic_pop.h" @@ -533,17 +596,43 @@ namespace etl /// Construct from arguments. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - template + template = 0> + ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, TArgs&&... args) + : base_type(index_of_type::value) + { + static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); + + construct_in_place_args >(data, etl::forward(args)...); + } + + template = 0> + constexpr explicit variant(etl::in_place_type_t, TArgs&&... args) + : base_type(etl::in_place_index_t::value>{}, etl::remove_cvref_t(etl::forward(args)...), index_of_type::value) + { + static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); + } + #include "diagnostic_pop.h" + + //*************************************************************************** + /// Construct from arguments. + //*************************************************************************** + #include "diagnostic_uninitialized_push.h" + template = 0> ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t, TArgs&&... args) - ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, TArgs...>::value)) - : type_id(Index) + : base_type(Index) { using type = type_from_index; static_assert(etl::is_one_of::value, "Unsupported type"); construct_in_place_args(data, etl::forward(args)...); + } - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + template = 0> + constexpr explicit variant(etl::in_place_index_t, TArgs&&... args) + : base_type(etl::in_place_index_t{}, type_from_index(etl::forward(args)...), Index) + { + using type = type_from_index; + static_assert(etl::is_one_of::value, "Unsupported type"); } #include "diagnostic_pop.h" @@ -554,10 +643,7 @@ namespace etl #include "diagnostic_uninitialized_push.h" template ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, std::initializer_list init, TArgs&&... args) - ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, std::initializer_list, TArgs...>::value)) - : operation(operation_type< etl::remove_cvref_t, etl::is_copy_constructible >::value, - etl::is_move_constructible >::value>::do_operation) - , type_id(index_of_type::value) + : base_type(index_of_type::value) { static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); @@ -571,15 +657,12 @@ namespace etl #include "diagnostic_uninitialized_push.h" template ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t, std::initializer_list init, TArgs&&... args) - ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, std::initializer_list, TArgs...>::value)) - : type_id(Index) + : base_type(Index) { using type = type_from_index; static_assert(etl::is_one_of::value, "Unsupported type"); construct_in_place_args(data, init, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; } #include "diagnostic_pop.h" #endif @@ -589,17 +672,12 @@ namespace etl ///\param other The other variant object to copy. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant(const variant& other) ETL_NOEXCEPT_IF((etl::conjunction...>::value)) - : operation(other.operation) - , type_id(other.type_id) + ETL_CONSTEXPR14 variant(const variant& other) noexcept(etl::conjunction...>::value) + : base_type(other.type_id) { - if (other.valueless_by_exception()) + if (other.index() != variant_npos) { - type_id = variant_npos; - } - else - { - operation(private_variant::Copy, data, other.data); + do_copy_from(other, etl::integral_constant{}); } } #include "diagnostic_pop.h" @@ -609,34 +687,22 @@ namespace etl ///\param other The other variant object to copy. //*************************************************************************** #include "diagnostic_uninitialized_push.h" - ETL_CONSTEXPR14 variant(variant&& other) ETL_NOEXCEPT_IF((etl::conjunction...>::value)) - : operation(other.operation) - , type_id(other.type_id) + ETL_CONSTEXPR14 variant(variant&& other) noexcept(etl::conjunction...>::value) + : base_type(other.type_id) { - if (other.valueless_by_exception()) + if (other.index() != variant_npos) { - type_id = variant_npos; - } - else - { - operation(private_variant::Move, data, other.data); + do_move_from(other, etl::integral_constant{}); } } #include "diagnostic_pop.h" //*************************************************************************** /// Destructor. + /// Handled by variant_base (trivial for trivially destructible types, + /// non-trivial otherwise). //*************************************************************************** - ~variant() ETL_NOEXCEPT - { - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - operation = operation_type::do_operation; // Null operation. - type_id = variant_npos; - } + // ~variant() is provided by base_type //*************************************************************************** /// Emplace by type with variadic constructor parameters. @@ -648,18 +714,12 @@ namespace etl using type = etl::remove_cvref_t; - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - construct_in_place_args(data, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + do_destroy(); + do_construct(type(etl::forward(args)...)); type_id = index_of_type::value; - return *static_cast(data); + return get_value::value>(); } #if ETL_HAS_INITIALIZER_LIST @@ -674,18 +734,12 @@ namespace etl using type = etl::remove_cvref_t; - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - construct_in_place_args(data, il, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + do_destroy(); + do_construct(type(il, etl::forward(args)...)); type_id = index_of_type::value; - return *static_cast(data); + return get_value::value>(); } #endif @@ -700,18 +754,12 @@ namespace etl using type = type_from_index; - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - construct_in_place_args(data, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + do_destroy(); + do_construct(type(etl::forward(args)...)); type_id = Index; - return *static_cast(data); + return get_value(); } #if ETL_HAS_INITIALIZER_LIST @@ -726,18 +774,12 @@ namespace etl using type = type_from_index; - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } - - construct_in_place_args(data, il, etl::forward(args)...); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; + do_destroy(); + do_construct(type(il, etl::forward(args)...)); type_id = Index; - return *static_cast(data); + return get_value(); } #endif @@ -745,22 +787,17 @@ namespace etl /// Move assignment operator for type. ///\param value The value to assign. //*************************************************************************** - template , variant>::value, int> = 0> - variant& operator=(T&& value) ETL_NOEXCEPT_IF((etl::is_nothrow_constructible, T&&>::value)) + template , variant>::value, int> = 0> + variant& operator=(T&& value) { using type = etl::remove_cvref_t; static_assert(etl::is_one_of::value, "Unsupported type"); - if (!valueless_by_exception()) - { - operation(private_variant::Destroy, data, nullptr); - } + do_destroy(); + do_construct(etl::forward(value)); - construct_in_place(data, etl::forward(value)); - - operation = operation_type::value, etl::is_move_constructible::value>::do_operation; - type_id = index_of_type::value; + type_id = index_of_type::value; return *this; } @@ -773,19 +810,15 @@ namespace etl { if (this != &other) { - if (!valueless_by_exception()) - { - operation(Destroy, data, nullptr); - } - - if (other.valueless_by_exception()) + if (other.index() == variant_npos) { type_id = variant_npos; } else { - operation = other.operation; - operation(Copy, data, other.data); + do_destroy(); + + do_copy_from(other, etl::integral_constant{}); type_id = other.type_id; } @@ -802,19 +835,15 @@ namespace etl { if (this != &other) { - if (!valueless_by_exception()) - { - operation(Destroy, data, nullptr); - } - - if (other.valueless_by_exception()) + if (other.index() == variant_npos) { type_id = variant_npos; } else { - operation = other.operation; - operation(Move, data, other.data); + do_destroy(); + + do_move_from(other, etl::integral_constant{}); type_id = other.type_id; } @@ -852,8 +881,8 @@ namespace etl } //*************************************************************************** - /// Checks to see if the type currently stored is the same as that specified - /// in the template parameter. For compatibility with legacy variant API. + /// Checks to see if the type currently stored is the same as that specified in the template parameter. + /// For compatibility with legacy variant API. ///\return true if it is the specified type, otherwise false. //*************************************************************************** template (), int> = 0> @@ -870,9 +899,9 @@ namespace etl } //*************************************************************************** - /// Checks if the other variant holds the same type as the current stored - /// type. For variants with the same type declarations. For compatibility - /// with legacy variant API. + /// Checks if the other variant holds the same type as the current stored type. + /// For variants with the same type declarations. + /// For compatibility with legacy variant API. ///\return true if the types are the same, otherwise false. //*************************************************************************** constexpr bool is_same_type(const variant& other) const @@ -1016,29 +1045,15 @@ namespace etl private: - /// The operation function type. - using operation_function = void (*)(int, char*, const char*); - //*************************************************************************** - /// Construct the type in-place. lvalue reference. + /// Construct the type in-place via perfect forwarding. //*************************************************************************** - template - static void construct_in_place(char* pstorage, const T& value) + template + static void construct_in_place(char* pstorage, U&& value) { using type = etl::remove_cvref_t; - ::new (pstorage) type(value); - } - - //*************************************************************************** - /// Construct the type in-place. rvalue reference. - //*************************************************************************** - template - static void construct_in_place(char* pstorage, T&& value) - { - using type = etl::remove_cvref_t; - - ::new (pstorage) type(etl::move(value)); + ::new (pstorage) type(etl::forward(value)); } //*************************************************************************** @@ -1056,13 +1071,84 @@ namespace etl /// Default construct the type in-place. //*************************************************************************** template - static void default_construct_in_place(char* pstorage) + static void default_construct_in_place(char* pstorage) ETL_NOEXCEPT_IF((etl::is_nothrow_default_constructible >::value)) { using type = etl::remove_cvref_t; ::new (pstorage) type(); } + //*************************************************************************** + /// Destroy the currently held value (only for non-trivially destructible). + //*************************************************************************** + void do_destroy() + { + do_destroy_impl(etl::integral_constant{}); + } + + void do_destroy_impl(etl::integral_constant) + { + // Trivially destructible: no-op. + } + + void do_destroy_impl(etl::integral_constant) + { + private_variant::variant_operations<0, TTypes...>::destroy(data, type_id); + } + + //*************************************************************************** + /// Construct a value in the union or buffer storage. + //*************************************************************************** + template + void do_construct(U&& value) + { + do_construct_impl(etl::forward(value), etl::integral_constant{}); + } + + template + void do_construct_impl(U&& value, etl::integral_constant) + { + // Trivially destructible: assign directly into the variadic_union. + private_variant::variadic_union_get::value>(data) = etl::forward(value); + } + + template + void do_construct_impl(U&& value, etl::integral_constant) + { + // Non-trivially destructible: use placement new. + ::new (static_cast(data)) T(etl::forward(value)); + } + + //*************************************************************************** + /// Copy from another variant. + //*************************************************************************** + void do_copy_from(const variant& other, etl::integral_constant) + { + // Trivially destructible: copy via memcpy to avoid issues with non-trivial copy assignment in union. + memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); + } + + void do_copy_from(const variant& other, etl::integral_constant) + { + // Non-trivially destructible: use switch-based dispatch. + private_variant::variant_operations<0, TTypes...>::copy(data, other.data, other.type_id); + } + + //*************************************************************************** + /// Move from another variant. + //*************************************************************************** + void do_move_from(variant& other, etl::integral_constant) + { + // Trivially destructible: copy via memcpy (trivial move == copy). + memcpy(static_cast(&data), static_cast(&other.data), sizeof(data)); + } + + void do_move_from(variant& other, etl::integral_constant) + { + // Non-trivially destructible: use switch-based dispatch. + private_variant::variant_operations<0, TTypes...>::move(data, other.data, other.type_id); + } + #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) //*************************************************************************** /// Call the relevant visitor by attempting each one. @@ -1110,8 +1196,8 @@ namespace etl if (Index == index()) { // Workaround for MSVC (2023/05/13) - // It doesn't compile 'visitor.visit(etl::get(*this))' correctly - // for C++17 & C++20. Changed all of the instances for consistency. + // It doesn't compile 'visitor.visit(etl::get(*this))' correctly for C++17 & C++20. + // Changed all of the instances for consistency. auto& v = etl::get(*this); visitor.visit(v); return true; @@ -1131,8 +1217,8 @@ namespace etl if (Index == index()) { // Workaround for MSVC (2023/05/13) - // It doesn't compile 'visitor.visit(etl::get(*this))' correctly - // for C++17 & C++20. Changed all of the instances for consistency. + // It doesn't compile 'visitor.visit(etl::get(*this))' correctly for C++17 & C++20. + // Changed all of the instances for consistency. auto& v = etl::get(*this); visitor.visit(v); return true; @@ -1246,20 +1332,91 @@ namespace etl } //*************************************************************************** - /// The internal storage. - /// Aligned on a suitable boundary, which should be good for all types. + /// Get a reference to the stored value by index. + /// For trivially destructible types, accesses the variadic_union directly. + /// For non-trivially destructible types, uses pointer cast on uninitialized_buffer. //*************************************************************************** - etl::uninitialized_buffer data; + template + ETL_CONSTEXPR14 type_from_index& get_value() ETL_NOEXCEPT + { + return get_value_impl(etl::integral_constant{}); + } + + template + constexpr const type_from_index& get_value() const ETL_NOEXCEPT + { + return get_value_impl(etl::integral_constant{}); + } + + // Trivially destructible: use variadic_union accessor + template + ETL_CONSTEXPR14 type_from_index& get_value_impl(etl::integral_constant) ETL_NOEXCEPT + { + return private_variant::variadic_union_get(data); + } + + template + constexpr const type_from_index& get_value_impl(etl::integral_constant) const ETL_NOEXCEPT + { + return private_variant::variadic_union_get(data); + } + + // Non-trivially destructible: use pointer cast on uninitialized_buffer + template + ETL_CONSTEXPR14 type_from_index& get_value_impl(etl::integral_constant) ETL_NOEXCEPT + { + return *static_cast*>(data); + } + + template + ETL_CONSTEXPR14 const type_from_index& get_value_impl(etl::integral_constant) const ETL_NOEXCEPT + { + return *static_cast*>(data); + } //*************************************************************************** - /// The operation function. + /// Get a pointer to the stored value by type. //*************************************************************************** - operation_function operation; + template + ETL_CONSTEXPR14 T* get_value_ptr() ETL_NOEXCEPT + { + return get_value_ptr_impl(etl::integral_constant{}); + } - //*************************************************************************** - /// The id of the current stored type. - //*************************************************************************** - size_t type_id; + template + constexpr const T* get_value_ptr() const ETL_NOEXCEPT + { + return get_value_ptr_impl(etl::integral_constant{}); + } + + // Trivially destructible: use variadic_union accessor + template + ETL_CONSTEXPR14 T* get_value_ptr_impl(etl::integral_constant) ETL_NOEXCEPT + { + return &private_variant::variadic_union_get::value>(data); + } + + template + constexpr const T* get_value_ptr_impl(etl::integral_constant) const ETL_NOEXCEPT + { + return &private_variant::variadic_union_get::value>(data); + } + + // Non-trivially destructible: use pointer cast on uninitialized_buffer + template + ETL_CONSTEXPR14 T* get_value_ptr_impl(etl::integral_constant) ETL_NOEXCEPT + { + return static_cast(data); + } + + template + ETL_CONSTEXPR14 const T* get_value_ptr_impl(etl::integral_constant) const ETL_NOEXCEPT + { + return static_cast(data); + } + + // data and type_id are inherited from base_type. + // operation is inherited only for non-trivially destructible variants. }; namespace private_variant @@ -1358,9 +1515,7 @@ namespace etl ETL_ASSERT(Index == v.index(), ETL_ERROR(etl::variant_incorrect_type_exception)); - using type = etl::variant_alternative_t >; - - return *static_cast(v.data); + return v.template get_value(); } //*********************************** @@ -1373,9 +1528,7 @@ namespace etl ETL_ASSERT(Index == v.index(), ETL_ERROR(etl::variant_incorrect_type_exception)); - using type = etl::variant_alternative_t >; - - return etl::move(*static_cast(v.data)); + return etl::move(v.template get_value()); } //*********************************** @@ -1388,24 +1541,20 @@ namespace etl ETL_ASSERT(Index == v.index(), ETL_ERROR(etl::variant_incorrect_type_exception)); - using type = etl::variant_alternative_t >; - - return *static_cast(v.data); + return v.template get_value(); } //*********************************** template ETL_CONSTEXPR14 const etl::variant_alternative_t >&& get(const etl::variant&& v) { - #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) + #if ETL_USING_CPP17 & !defined(ETL_VARIANT_FORCE_CPP11) static_assert(Index < sizeof...(TTypes), "Index out of range"); #endif ETL_ASSERT(Index == v.index(), ETL_ERROR(etl::variant_incorrect_type_exception)); - using type = etl::variant_alternative_t >; - - return etl::move(*static_cast(v.data)); + return etl::move(v.template get_value()); } //*********************************** @@ -1414,7 +1563,7 @@ namespace etl { ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return *static_cast(v.data); + return *v.template get_value_ptr(); } //*********************************** @@ -1423,7 +1572,7 @@ namespace etl { ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return etl::move(*static_cast(v.data)); + return etl::move(*v.template get_value_ptr()); } //*********************************** @@ -1432,7 +1581,7 @@ namespace etl { ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return *static_cast(v.data); + return *v.template get_value_ptr(); } //*********************************** @@ -1441,14 +1590,14 @@ namespace etl { ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return etl::move(*static_cast(v.data)); + return etl::move(*v.template get_value_ptr()); } //*************************************************************************** /// get_if //*************************************************************************** template < size_t Index, typename... TTypes > - ETL_CONSTEXPR14 etl::add_pointer_t< etl::variant_alternative_t > > get_if(etl::variant* pv) ETL_NOEXCEPT + ETL_CONSTEXPR14 etl::add_pointer_t > > get_if(etl::variant* pv) ETL_NOEXCEPT { if ((pv != nullptr) && (pv->index() == Index)) { @@ -1462,7 +1611,7 @@ namespace etl //*********************************** template < size_t Index, typename... TTypes > - ETL_CONSTEXPR14 etl::add_pointer_t< const etl::variant_alternative_t > > get_if(const etl::variant* pv) + ETL_CONSTEXPR14 etl::add_pointer_t > > get_if(const etl::variant* pv) ETL_NOEXCEPT { if ((pv != nullptr) && (pv->index() == Index)) @@ -1481,7 +1630,7 @@ namespace etl { if ((pv != nullptr) && (private_variant::is_same_type_in(pv->index()))) { - return static_cast(pv->data); + return pv->template get_value_ptr(); } else { @@ -1495,7 +1644,7 @@ namespace etl { if ((pv != nullptr) && (private_variant::is_same_type_in(pv->index()))) { - return static_cast(pv->data); + return pv->template get_value_ptr(); } else { @@ -1542,9 +1691,9 @@ namespace etl static ETL_CONSTEXPR14 TRet do_visit_single(TCallable&& f, TVariant&& v, TNext&&, TVariants&&... vs); //*************************************************************************** - /// Dummy-struct used to indicate that the return type should be - /// auto-deduced from the callable object and the alternatives in the - /// variants passed to a visit. Should never explicitly be used by an user. + /// Dummy-struct used to indicate that the return type should be auto-deduced + /// from the callable object and the alternatives in the variants passed to + /// a visit. Should never explicitly be used by an user. //*************************************************************************** struct visit_auto_return { @@ -1571,13 +1720,12 @@ namespace etl using rlref_copy = conditional_t::value, T&, T&&>; //*************************************************************************** - /// Evaluates all permutations of calls to a callable object that can be - /// done based upon the variants input. Need a `index_sequence<...>` as - /// second argument that contains all possible indices of the first - /// following variant. The first argument is essentially a - /// `single_visit_result_type`-prototype in which every recursive - /// instantiation of `visit_result_helper` appends more elements and give it - /// a pass through `common_type_t`. + /// Evaluates all permutations of calls to a callable object that can be done + /// based upon the variants input. Need a `index_sequence<...>` as second + /// argument that contains all possible indices of the first following variant. + /// The first argument is essentially a `single_visit_result_type`-prototype + /// in which every recursive instantiation of `visit_result_helper` appends + /// more elements and give it a pass through `common_type_t`. //*************************************************************************** template