///\file /****************************************************************************** The MIT License(MIT) Embedded Template Library. https://github.com/ETLCPP/etl https://www.etlcpp.com Copyright(c) 2021 jwellbelove, Robin S�derholm 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 "../platform.h" #include "../utility.h" #include "../largest.h" #include "../exception.h" #include "../type_traits.h" #include "../integral_limits.h" #include "../static_assert.h" #include "../alignment.h" #include "../error_handler.h" #include "../parameter_pack.h" #include "../placement_new.h" #include "../visitor.h" #include "../memory.h" #include "../compare.h" #include "../initializer_list.h" #include #if defined(ETL_COMPILER_KEIL) #pragma diag_suppress 940 #pragma diag_suppress 111 #endif #if ETL_CPP11_NOT_SUPPORTED #if !defined(ETL_IN_UNIT_TEST) #error NOT SUPPORTED FOR C++03 OR BELOW #endif #else //***************************************************************************** ///\defgroup variant variant /// A class that can contain one a several specified types in a type safe manner. ///\ingroup containers //***************************************************************************** namespace etl { namespace private_variant { //*************************************************************************** // This is a copy of the normal etl::parameter_pack, but without the static_assert // so that the C++11 versions of do_visitor() & do_operator() do not throw a compile time error. //*************************************************************************** template class parameter_pack { public: static constexpr size_t size = sizeof...(TTypes); //*************************************************************************** /// index_of_type //*************************************************************************** template class index_of_type { private: using type = etl::remove_cvref_t; //*********************************** template struct index_of_type_helper { static constexpr size_t value = etl::is_same::value ? 1 : 1 + index_of_type_helper::value; }; //*********************************** template struct index_of_type_helper { static constexpr size_t value = 1UL; }; public: static_assert(etl::is_one_of::value, "T is not in parameter pack"); /// The index value. static constexpr size_t value = index_of_type_helper::value - 1; }; //*************************************************************************** /// type_from_index //*************************************************************************** template class type_from_index { private: //*********************************** template struct type_from_index_helper { using type = typename etl::conditional::type>::type; }; //*********************************** template struct type_from_index_helper { using type = T1; }; public: /// Template alias using type = typename type_from_index_helper::type; }; //*********************************** template using type_from_index_t = typename type_from_index::type; }; //******************************************* // The traits an object may have. //******************************************* static constexpr bool Copyable = true; static constexpr bool Non_Copyable = false; static constexpr bool Moveable = true; static constexpr bool Non_Moveable = false; //******************************************* // The types of operations we can perform. //******************************************* static constexpr int Copy = 0; static constexpr int Move = 1; static constexpr int Destroy = 2; //******************************************* // operation_type //******************************************* template struct operation_type; //******************************************* // Specialisation for null operation. template <> struct operation_type { static void do_operation(int , char* , const char* ) { // 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; } } } }; //******************************************* // Specialisation for no-copyable & moveable types. template struct operation_type { 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; } 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 { static void do_operation(int operation, char* pstorage, const char* pvalue) { switch (operation) { case Copy: { ::new (pstorage) T(*reinterpret_cast(pvalue)); break; } 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 { static void do_operation(int operation, char* pstorage, const char* pvalue) { switch (operation) { case Copy: { ::new (pstorage) T(*reinterpret_cast(pvalue)); break; } case Move: { ::new (pstorage) T(etl::move(*reinterpret_cast(const_cast(pvalue)))); break; } case Destroy: { reinterpret_cast(pstorage)->~T(); break; } default: { // This should never occur. #if defined(ETL_IN_UNIT_TEST) assert(false); #endif break; } } } }; } /// Definition of variant_npos. constexpr size_t variant_npos = etl::integral_limits::max; //*********************************** // variant. Forward declaration template class variant; //*************************************************************************** /// variant_alternative //*************************************************************************** template struct variant_alternative; template struct variant_alternative> { using type = typename etl::private_variant::parameter_pack::template type_from_index::type; }; template struct variant_alternative { using type = typename variant_alternative::type; }; template using variant_alternative_t = typename variant_alternative::type; //*********************************** // holds_alternative. Forward declaration template ETL_CONSTEXPR14 bool holds_alternative(const etl::variant& v) noexcept; //*********************************** // get. Forward declarations template ETL_CONSTEXPR14 etl::variant_alternative_t>& get(etl::variant& v); template ETL_CONSTEXPR14 etl::variant_alternative_t>&& get(etl::variant&& v); template ETL_CONSTEXPR14 const etl::variant_alternative_t>& get(const etl::variant& v); template ETL_CONSTEXPR14 const etl::variant_alternative_t>&& get(const etl::variant&& v); template ETL_CONSTEXPR14 T& get(etl::variant& v); template ETL_CONSTEXPR14 T&& get(etl::variant&& v); template ETL_CONSTEXPR14 const T& get(const etl::variant& v); template ETL_CONSTEXPR14 const T&& get(const etl::variant&& v); //*************************************************************************** /// Monostate for variants. ///\ingroup variant //*************************************************************************** struct monostate { }; constexpr bool operator >(etl::monostate, etl::monostate) noexcept { return false; } constexpr bool operator <(etl::monostate, etl::monostate) noexcept { return false; } constexpr bool operator !=(etl::monostate, etl::monostate) noexcept { return false; } constexpr bool operator <=(etl::monostate, etl::monostate) noexcept { return true; } constexpr bool operator >=(etl::monostate, etl::monostate) noexcept { return true; } constexpr bool operator ==(etl::monostate, etl::monostate) noexcept { return true; } #if ETL_USING_CPP20 && ETL_USING_STL && !(defined(ETL_DEVELOPMENT_OS_APPLE) && defined(ETL_COMPILER_CLANG)) constexpr std::strong_ordering operator<=>(monostate, monostate) noexcept { return std::strong_ordering::equal; } #endif #if ETL_NOT_USING_STL && !defined(ETL_USE_TYPE_TRAITS_BUILTINS) template <> struct is_copy_constructible : public etl::true_type { }; template <> struct is_move_constructible : public etl::true_type { }; #endif //*************************************************************************** /// Base exception for the variant class. ///\ingroup variant //*************************************************************************** class variant_exception : public exception { public: variant_exception(string_type reason_, string_type file_name_, numeric_type line_number_) : exception(reason_, file_name_, line_number_) { } }; //*************************************************************************** /// 'Unsupported type' exception for the variant class. ///\ingroup variant //*************************************************************************** class variant_incorrect_type_exception : public variant_exception { public: variant_incorrect_type_exception(string_type file_name_, numeric_type line_number_) : variant_exception(ETL_ERROR_TEXT("variant:unsupported type", ETL_VARIANT_FILE_ID"A"), file_name_, line_number_) { } }; //*************************************************************************** /// 'Bad variant access' exception for the variant class. ///\ingroup variant //*************************************************************************** class bad_variant_access : public variant_exception { public: bad_variant_access(string_type file_name_, numeric_type line_number_) : variant_exception(ETL_ERROR_TEXT("variant:bad variant access", ETL_VARIANT_FILE_ID"B"), file_name_, line_number_) {} }; //*************************************************************************** /// A template class that can store any of the types defined in the template parameter list. /// Supports up to 8 types. ///\ingroup variant //*************************************************************************** template class variant { public: //*************************************************************************** /// The type used for ids. //*************************************************************************** using type_id_t = uint_least8_t ; //*************************************************************************** /// get() is a friend function. //*************************************************************************** template friend ETL_CONSTEXPR14 etl::variant_alternative_t>& get(etl::variant& v); template friend ETL_CONSTEXPR14 etl::variant_alternative_t>&& get(etl::variant&& v); template friend ETL_CONSTEXPR14 const etl::variant_alternative_t>& get(const etl::variant& v); template friend ETL_CONSTEXPR14 const etl::variant_alternative_t>&& get(const etl::variant&& v); template friend ETL_CONSTEXPR14 T& get(etl::variant& v); template friend ETL_CONSTEXPR14 T&& get(etl::variant&& v); template friend ETL_CONSTEXPR14 const T& get(const etl::variant& v); template friend ETL_CONSTEXPR14 const T&& get(const etl::variant&& v); 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. //******************************************* template using index_of_type = typename etl::private_variant::parameter_pack::template index_of_type>; //******************************************* // Get the type from the index. //******************************************* template using type_from_index = typename etl::private_variant::parameter_pack::template type_from_index::type; public: //*************************************************************************** /// Default constructor. /// Sets the state of the instance to containing no valid data. //*************************************************************************** #include "diagnostic_uninitialized_push.h" ETL_CONSTEXPR14 variant() { using type = typename etl::private_variant::parameter_pack::template type_from_index<0U>::type; default_construct_in_place(data); operation = operation_type::value, etl::is_move_constructible::value>::do_operation; type_id = 0U; } #include "diagnostic_pop.h" //*************************************************************************** /// Construct from a value. //*************************************************************************** #include "diagnostic_uninitialized_push.h" template , variant>::value, int> = 0> ETL_CONSTEXPR14 variant(T&& value) : operation(operation_type, etl::is_copy_constructible>::value, etl::is_move_constructible>::value>::do_operation) , type_id(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) : operation(operation_type, etl::is_copy_constructible>::value, etl::is_move_constructible>::value>::do_operation) , type_id(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" //*************************************************************************** /// Construct from arguments. //*************************************************************************** #include "diagnostic_uninitialized_push.h" template ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t, TArgs&&... args) : type_id(Index) { using type = typename private_variant::parameter_pack:: template type_from_index_t; 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; } #include "diagnostic_pop.h" #if ETL_HAS_INITIALIZER_LIST //*************************************************************************** /// Construct from type, initializer_list and arguments. //*************************************************************************** #include "diagnostic_uninitialized_push.h" template ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, std::initializer_list init, TArgs&&... args) : operation(operation_type, etl::is_copy_constructible>::value, etl::is_move_constructible>::value>::do_operation) , type_id(index_of_type::value) { static_assert(etl::is_one_of, TTypes...> ::value, "Unsupported type"); construct_in_place_args>(data, init, etl::forward(args)...); } #include "diagnostic_pop.h" //*************************************************************************** /// Construct from index, initializer_list and arguments. //*************************************************************************** #include "diagnostic_uninitialized_push.h" template ETL_CONSTEXPR14 explicit variant(etl::in_place_index_t, std::initializer_list init, TArgs&&... args) : type_id(Index) { using type = typename private_variant::parameter_pack:: template type_from_index_t; 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 //*************************************************************************** /// Copy constructor. ///\param other The other variant object to copy. //*************************************************************************** #include "diagnostic_uninitialized_push.h" ETL_CONSTEXPR14 variant(const variant& other) : operation(other.operation) , type_id(other.type_id) { if (this != &other) { if (other.index() == variant_npos) { type_id = variant_npos; } else { operation(private_variant::Copy, data, other.data); } } } #include "diagnostic_pop.h" //*************************************************************************** /// Move constructor. ///\param other The other variant object to copy. //*************************************************************************** #include "diagnostic_uninitialized_push.h" ETL_CONSTEXPR14 variant(variant&& other) : operation(other.operation) , type_id(other.type_id) { if (this != &other) { if (other.index() == variant_npos) { type_id = variant_npos; } else { operation(private_variant::Move, data, other.data); } } else { type_id = variant_npos; } } #include "diagnostic_pop.h" //*************************************************************************** /// Destructor. //*************************************************************************** ~variant() { if (index() != variant_npos) { operation(private_variant::Destroy, data, nullptr); } operation = operation_type::do_operation; // Null operation. type_id = variant_npos; } //*************************************************************************** /// Emplace by type with variadic constructor parameters. //*************************************************************************** template T& emplace(TArgs&&... args) { static_assert(etl::is_one_of::value, "Unsupported type"); using type = etl::remove_cvref_t; 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; type_id = etl::private_variant::parameter_pack::template index_of_type::value; return *static_cast(data); } #if ETL_HAS_INITIALIZER_LIST //*************************************************************************** /// Emplace by type with variadic constructor parameters. //*************************************************************************** template T& emplace(std::initializer_list il, TArgs&&... args) { static_assert(etl::is_one_of::value, "Unsupported type"); using type = etl::remove_cvref_t; 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; type_id = etl::private_variant::parameter_pack::template index_of_type::value; return *static_cast(data); } #endif //*************************************************************************** /// Emplace by index with variadic constructor parameters. //*************************************************************************** template typename etl::variant_alternative>::type& emplace(TArgs&&... args) { static_assert(Index < etl::private_variant::parameter_pack::size, "Index out of range"); using type = type_from_index; 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; type_id = Index; return *static_cast(data); } #if ETL_HAS_INITIALIZER_LIST //*************************************************************************** /// Emplace by index with variadic constructor parameters. //*************************************************************************** template typename etl::variant_alternative>::type& emplace(std::initializer_list il, TArgs&&... args) { static_assert(Index < etl::private_variant::parameter_pack::size, "Index out of range"); using type = type_from_index; 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; type_id = Index; return *static_cast(data); } #endif //*************************************************************************** /// Move assignment operator for type. ///\param value The value to assign. //*************************************************************************** template , variant>::value, int> = 0> variant& operator =(T&& value) { using type = etl::remove_cvref_t; static_assert(etl::is_one_of::value, "Unsupported type"); operation(private_variant::Destroy, data, nullptr); construct_in_place(data, etl::forward(value)); operation = operation_type::value, etl::is_move_constructible::value>::do_operation; type_id = etl::private_variant::parameter_pack::template index_of_type::value; return *this; } //*************************************************************************** /// Assignment operator for variant type. ///\param other The variant to assign. //*************************************************************************** variant& operator =(const variant& other) { if (this != &other) { if (other.index() == variant_npos) { type_id = variant_npos; } else { operation(Destroy, data, nullptr); operation = other.operation; operation(Copy, data, other.data); type_id = other.type_id; } } return *this; } //*************************************************************************** /// Assignment operator for variant type. ///\param other The variant to assign. //*************************************************************************** variant& operator =(variant&& other) { if (this != &other) { if (other.index() == variant_npos) { type_id = variant_npos; } else { operation(Destroy, data, nullptr); operation = other.operation; operation(Move, data, other.data); type_id = other.type_id; } } return *this; } //*************************************************************************** /// Checks whether a valid value is currently stored. ///\return true if the value is valid, otherwise false. //*************************************************************************** constexpr bool valueless_by_exception() const noexcept { return type_id == variant_npos; } //*************************************************************************** /// Gets the index of the type currently stored or variant_npos //*************************************************************************** constexpr size_t index() const noexcept { return type_id; } //*************************************************************************** /// Checks to see if the type T is one of the variant's supported types. /// For compatibility with legacy variant API. ///\return true if it is, otherwise false. //*************************************************************************** template static constexpr bool is_supported_type() { return etl::is_one_of, TTypes...>::value; } //*************************************************************************** /// 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> constexpr bool is_type() const noexcept { return (type_id == etl::private_variant::parameter_pack::template index_of_type::value); } //*************************************************************************** template (), int> = 0> constexpr bool is_type() const noexcept { return false; } //*************************************************************************** /// 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 { return type_id == other.type_id; } //*************************************************************************** /// Swaps this variant with another. //*************************************************************************** void swap(variant& rhs) noexcept { variant temp(etl::move(*this)); *this = etl::move(rhs); rhs = etl::move(temp); } //*************************************************************************** /// Accept an etl::visitor. //*************************************************************************** template etl::enable_if_t::value, void> accept(TVisitor& v) { #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) do_visitor(v, etl::make_index_sequence{}); #else do_visitor(v); #endif } //*************************************************************************** /// Accept an etl::visitor. //*************************************************************************** template etl::enable_if_t::value, void> accept(TVisitor& v) const { #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) do_visitor(v, etl::make_index_sequence{}); #else do_visitor(v); #endif } //*************************************************************************** /// Accept a generic functor. //*************************************************************************** template etl::enable_if_t::value, void> accept(TVisitor& v) { #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) do_operator(v, etl::make_index_sequence{}); #else do_operator(v); #endif } //*************************************************************************** /// Accept a generic functor. //*************************************************************************** template etl::enable_if_t::value, void> accept(TVisitor& v) const { #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) do_operator(v, etl::make_index_sequence{}); #else do_operator(v); #endif } //*************************************************************************** /// Accept an etl::visitor. /// Deprecated. //*************************************************************************** template #if !defined(ETL_IN_UNIT_TEST) ETL_DEPRECATED_REASON("Replace with accept()") #endif void accept_visitor(TVisitor& v) { #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) do_visitor(v, etl::make_index_sequence{}); #else do_visitor(v); #endif } //*************************************************************************** /// Accept an etl::visitor. /// Deprecated. //*************************************************************************** template #if !defined(ETL_IN_UNIT_TEST) ETL_DEPRECATED_REASON("Replace with accept()") #endif void accept_visitor(TVisitor& v) const { #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) do_visitor(v, etl::make_index_sequence{}); #else do_visitor(v); #endif } //*************************************************************************** /// Accept a generic functor. /// Deprecated. //*************************************************************************** template #if !defined(ETL_IN_UNIT_TEST) ETL_DEPRECATED_REASON("Replace with accept()") #endif void accept_functor(TVisitor& v) { #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) do_operator(v, etl::make_index_sequence{}); #else do_operator(v); #endif } //*************************************************************************** /// Accept a generic functor. /// Deprecated. //*************************************************************************** template #if !defined(ETL_IN_UNIT_TEST) ETL_DEPRECATED_REASON("Replace with accept()") #endif void accept_functor(TVisitor& v) const { #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) do_operator(v, etl::make_index_sequence{}); #else do_operator(v); #endif } private: /// The operation function type. using operation_function = void(*)(int, char*, const char*); //*************************************************************************** /// Construct the type in-place. lvalue reference. //*************************************************************************** template static void construct_in_place(char* pstorage, const T& 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)); } //*************************************************************************** /// Construct the type in-place. Variadic args. //*************************************************************************** template static void construct_in_place_args(char* pstorage, TArgs&&... args) { using type = etl::remove_cvref_t; ::new (pstorage) type(etl::forward(args)...); } //*************************************************************************** /// Default construct the type in-place. //*************************************************************************** template static void default_construct_in_place(char* pstorage) { using type = etl::remove_cvref_t; ::new (pstorage) type(); } #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) //*************************************************************************** /// Call the relevant visitor by attempting each one. //*************************************************************************** template void do_visitor(TVisitor& visitor, etl::index_sequence) { (attempt_visitor(visitor) || ...); } //*************************************************************************** /// Call the relevant visitor by attempting each one. //*************************************************************************** template void do_visitor(TVisitor& visitor, etl::index_sequence) const { (attempt_visitor(visitor) || ...); } #else //*************************************************************************** /// /// Call the relevant visitor. //*************************************************************************** template void do_visitor(TVisitor& visitor) { switch (index()) { case 0: { visitor.visit(etl::get<0>(*this)); break; } case 1: { visitor.visit(etl::get<1>(*this)); break; } case 2: { visitor.visit(etl::get<2>(*this)); break; } case 3: { visitor.visit(etl::get<3>(*this)); break; } case 4: { visitor.visit(etl::get<4>(*this)); break; } case 5: { visitor.visit(etl::get<5>(*this)); break; } case 6: { visitor.visit(etl::get<6>(*this)); break; } case 7: { visitor.visit(etl::get<7>(*this)); break; } #if !defined(ETL_VARIANT_CPP11_MAX_8_TYPES) case 8: { visitor.visit(etl::get<8>(*this)); break; } case 9: { visitor.visit(etl::get<9>(*this)); break; } case 10: { visitor.visit(etl::get<10>(*this)); break; } case 11: { visitor.visit(etl::get<11>(*this)); break; } case 12: { visitor.visit(etl::get<12>(*this)); break; } case 13: { visitor.visit(etl::get<13>(*this)); break; } case 14: { visitor.visit(etl::get<14>(*this)); break; } case 15: { visitor.visit(etl::get<15>(*this)); break; } #if !defined(ETL_VARIANT_CPP11_MAX_16_TYPES) case 16: { visitor.visit(etl::get<16>(*this)); break; } case 17: { visitor.visit(etl::get<17>(*this)); break; } case 18: { visitor.visit(etl::get<18>(*this)); break; } case 19: { visitor.visit(etl::get<19>(*this)); break; } case 20: { visitor.visit(etl::get<20>(*this)); break; } case 21: { visitor.visit(etl::get<21>(*this)); break; } case 22: { visitor.visit(etl::get<22>(*this)); break; } case 23: { visitor.visit(etl::get<23>(*this)); break; } #if !defined(ETL_VARIANT_CPP11_MAX_24_TYPES) case 24: { visitor.visit(etl::get<24>(*this)); break; } case 25: { visitor.visit(etl::get<25>(*this)); break; } case 26: { visitor.visit(etl::get<26>(*this)); break; } case 27: { visitor.visit(etl::get<27>(*this)); break; } case 28: { visitor.visit(etl::get<28>(*this)); break; } case 29: { visitor.visit(etl::get<29>(*this)); break; } case 30: { visitor.visit(etl::get<30>(*this)); break; } case 31: { visitor.visit(etl::get<31>(*this)); break; } #endif #endif #endif default: break; } } //*************************************************************************** /// /// Call the relevant visitor. //*************************************************************************** template void do_visitor(TVisitor& visitor) const { switch (index()) { case 0: { visitor.visit(etl::get<0>(*this)); break; } case 1: { visitor.visit(etl::get<1>(*this)); break; } case 2: { visitor.visit(etl::get<2>(*this)); break; } case 3: { visitor.visit(etl::get<3>(*this)); break; } case 4: { visitor.visit(etl::get<4>(*this)); break; } case 5: { visitor.visit(etl::get<5>(*this)); break; } case 6: { visitor.visit(etl::get<6>(*this)); break; } case 7: { visitor.visit(etl::get<7>(*this)); break; } #if !defined(ETL_VARIANT_CPP11_MAX_8_TYPES) case 8: { visitor.visit(etl::get<8>(*this)); break; } case 9: { visitor.visit(etl::get<9>(*this)); break; } case 10: { visitor.visit(etl::get<10>(*this)); break; } case 11: { visitor.visit(etl::get<11>(*this)); break; } case 12: { visitor.visit(etl::get<12>(*this)); break; } case 13: { visitor.visit(etl::get<13>(*this)); break; } case 14: { visitor.visit(etl::get<14>(*this)); break; } case 15: { visitor.visit(etl::get<15>(*this)); break; } #if !defined(ETL_VARIANT_CPP11_MAX_16_TYPES) case 16: { visitor.visit(etl::get<16>(*this)); break; } case 17: { visitor.visit(etl::get<17>(*this)); break; } case 18: { visitor.visit(etl::get<18>(*this)); break; } case 19: { visitor.visit(etl::get<19>(*this)); break; } case 20: { visitor.visit(etl::get<20>(*this)); break; } case 21: { visitor.visit(etl::get<21>(*this)); break; } case 22: { visitor.visit(etl::get<22>(*this)); break; } case 23: { visitor.visit(etl::get<23>(*this)); break; } #if !defined(ETL_VARIANT_CPP11_MAX_24_TYPES) case 24: { visitor.visit(etl::get<24>(*this)); break; } case 25: { visitor.visit(etl::get<25>(*this)); break; } case 26: { visitor.visit(etl::get<26>(*this)); break; } case 27: { visitor.visit(etl::get<27>(*this)); break; } case 28: { visitor.visit(etl::get<28>(*this)); break; } case 29: { visitor.visit(etl::get<29>(*this)); break; } case 30: { visitor.visit(etl::get<30>(*this)); break; } case 31: { visitor.visit(etl::get<31>(*this)); break; } #endif #endif #endif default: break; } } #endif //*************************************************************************** /// Attempt to call a visitor. //*************************************************************************** template bool attempt_visitor(TVisitor& visitor) { 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. auto& v = etl::get(*this); visitor.visit(v); return true; } else { return false; } } //*************************************************************************** /// Attempt to call a visitor. //*************************************************************************** template bool attempt_visitor(TVisitor& visitor) const { 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. auto& v = etl::get(*this); visitor.visit(v); return true; } else { return false; } } #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) //*************************************************************************** /// Call the relevant visitor by attempting each one. //*************************************************************************** template void do_operator(TVisitor& visitor, etl::index_sequence) { (attempt_operator(visitor) || ...); } //*************************************************************************** /// Call the relevant visitor by attempting each one. //*************************************************************************** template void do_operator(TVisitor& visitor, etl::index_sequence) const { (attempt_operator(visitor) || ...); } #else //*************************************************************************** /// Call the relevant visitor. //*************************************************************************** template void do_operator(TVisitor& visitor) { #if defined(ETL_VARIANT_CPP11_MAX_8_TYPES) ETL_STATIC_ASSERT(sizeof...(TTypes) <= 8U, "ETL_VARIANT_CPP11_MAX_8_TYPES - Only a maximum of 8 types are allowed in this variant"); #endif #if defined(ETL_VARIANT_CPP11_MAX_16_TYPES) ETL_STATIC_ASSERT(sizeof...(TTypes) <= 16U, "ETL_VARIANT_CPP11_MAX_16_TYPES - Only a maximum of 16 types are allowed in this variant"); #endif #if defined(ETL_VARIANT_CPP11_MAX_24_TYPES) ETL_STATIC_ASSERT(sizeof...(TTypes) <= 24U, "ETL_VARIANT_CPP11_MAX_24_TYPES - Only a maximum of 24 types are allowed in this variant"); #endif ETL_STATIC_ASSERT(sizeof...(TTypes) <= 32U, "A maximum of 32 types are allowed in this variant"); switch (index()) { case 0: visitor(etl::get<0>(*this)); break; case 1: visitor(etl::get<1>(*this)); break; case 2: visitor(etl::get<2>(*this)); break; case 3: visitor(etl::get<3>(*this)); break; case 4: visitor(etl::get<4>(*this)); break; case 5: visitor(etl::get<5>(*this)); break; case 6: visitor(etl::get<6>(*this)); break; case 7: visitor(etl::get<7>(*this)); break; #if !defined(ETL_VARIANT_CPP11_MAX_8_TYPES) case 8: visitor(etl::get<8>(*this)); break; case 9: visitor(etl::get<9>(*this)); break; case 10: visitor(etl::get<10>(*this)); break; case 11: visitor(etl::get<11>(*this)); break; case 12: visitor(etl::get<12>(*this)); break; case 13: visitor(etl::get<13>(*this)); break; case 14: visitor(etl::get<14>(*this)); break; case 15: visitor(etl::get<15>(*this)); break; #if !defined(ETL_VARIANT_CPP11_MAX_16_TYPES) case 16: visitor(etl::get<16>(*this)); break; case 17: visitor(etl::get<17>(*this)); break; case 18: visitor(etl::get<18>(*this)); break; case 19: visitor(etl::get<19>(*this)); break; case 20: visitor(etl::get<20>(*this)); break; case 21: visitor(etl::get<21>(*this)); break; case 22: visitor(etl::get<22>(*this)); break; case 23: visitor(etl::get<23>(*this)); break; #if !defined(ETL_VARIANT_CPP11_MAX_24_TYPES) case 24: visitor(etl::get<24>(*this)); break; case 25: visitor(etl::get<25>(*this)); break; case 26: visitor(etl::get<26>(*this)); break; case 27: visitor(etl::get<27>(*this)); break; case 28: visitor(etl::get<28>(*this)); break; case 29: visitor(etl::get<29>(*this)); break; case 30: visitor(etl::get<30>(*this)); break; case 31: visitor(etl::get<31>(*this)); break; #endif #endif #endif default: break; } } //*************************************************************************** /// Call the relevant visitor. //*************************************************************************** template void do_operator(TVisitor& visitor) const { #if defined(ETL_VARIANT_CPP11_MAX_8_TYPES) ETL_STATIC_ASSERT(sizeof...(TTypes) <= 8U, "ETL_VARIANT_CPP11_MAX_8_TYPES - Only a maximum of 8 types are allowed in this variant"); #endif #if defined(ETL_VARIANT_CPP11_MAX_16_TYPES) ETL_STATIC_ASSERT(sizeof...(TTypes) <= 16U, "ETL_VARIANT_CPP11_MAX_16_TYPES - Only a maximum of 16 types are allowed in this variant"); #endif #if defined(ETL_VARIANT_CPP11_MAX_24_TYPES) ETL_STATIC_ASSERT(sizeof...(TTypes) <= 24U, "ETL_VARIANT_CPP11_MAX_24_TYPES - Only a maximum of 24 types are allowed in this variant"); #endif ETL_STATIC_ASSERT(sizeof...(TTypes) <= 32U, "A maximum of 32 types are allowed in this variant"); switch (index()) { case 0: visitor(etl::get<0>(*this)); break; case 1: visitor(etl::get<1>(*this)); break; case 2: visitor(etl::get<2>(*this)); break; case 3: visitor(etl::get<3>(*this)); break; case 4: visitor(etl::get<4>(*this)); break; case 5: visitor(etl::get<5>(*this)); break; case 6: visitor(etl::get<6>(*this)); break; case 7: visitor(etl::get<7>(*this)); break; #if !defined(ETL_VARIANT_CPP11_MAX_8_TYPES) case 8: visitor(etl::get<8>(*this)); break; case 9: visitor(etl::get<9>(*this)); break; case 10: visitor(etl::get<10>(*this)); break; case 11: visitor(etl::get<11>(*this)); break; case 12: visitor(etl::get<12>(*this)); break; case 13: visitor(etl::get<13>(*this)); break; case 14: visitor(etl::get<14>(*this)); break; case 15: visitor(etl::get<15>(*this)); break; #if !defined(ETL_VARIANT_CPP11_MAX_16_TYPES) case 16: visitor(etl::get<16>(*this)); break; case 17: visitor(etl::get<17>(*this)); break; case 18: visitor(etl::get<18>(*this)); break; case 19: visitor(etl::get<19>(*this)); break; case 20: visitor(etl::get<20>(*this)); break; case 21: visitor(etl::get<21>(*this)); break; case 22: visitor(etl::get<22>(*this)); break; case 23: visitor(etl::get<23>(*this)); break; #if !defined(ETL_VARIANT_CPP11_MAX_24_TYPES) case 24: visitor(etl::get<24>(*this)); break; case 25: visitor(etl::get<25>(*this)); break; case 26: visitor(etl::get<26>(*this)); break; case 27: visitor(etl::get<27>(*this)); break; case 28: visitor(etl::get<28>(*this)); break; case 29: visitor(etl::get<29>(*this)); break; case 30: visitor(etl::get<30>(*this)); break; case 31: visitor(etl::get<31>(*this)); break; #endif #endif #endif default: break; } } #endif //*************************************************************************** /// Attempt to call a visitor. //*************************************************************************** template bool attempt_operator(TVisitor& visitor) { if (Index == index()) { auto& v = etl::get(*this); visitor(v); return true; } else { return false; } } //*************************************************************************** /// Attempt to call a visitor. //*************************************************************************** template bool attempt_operator(TVisitor& visitor) const { if (Index == index()) { auto& v = etl::get(*this); visitor(v); return true; } else { return false; } } //*************************************************************************** /// The internal storage. /// Aligned on a suitable boundary, which should be good for all types. //*************************************************************************** etl::uninitialized_buffer data; //*************************************************************************** /// The operation function. //*************************************************************************** operation_function operation; //*************************************************************************** /// The id of the current stored type. //*************************************************************************** size_t type_id; }; //*************************************************************************** /// Checks if the variant v holds the alternative T. //*************************************************************************** template ETL_CONSTEXPR14 bool holds_alternative(const etl::variant& v) noexcept { constexpr size_t Index = etl::private_variant::parameter_pack::template index_of_type::value; return (Index == variant_npos) ? false : (v.index() == Index); } //*************************************************************************** /// Checks if the variant v holds the alternative Index. //*************************************************************************** template ETL_CONSTEXPR14 bool holds_alternative(const etl::variant& v) noexcept { return (Index == v.index()); } //*************************************************************************** /// Checks if the variant v holds the alternative Index. (Runtime) //*************************************************************************** template ETL_CONSTEXPR14 bool holds_alternative(size_t index, const etl::variant& v) noexcept { return (index == v.index()); } //*************************************************************************** /// get //*************************************************************************** template ETL_CONSTEXPR14 etl::variant_alternative_t>& get(etl::variant& v) { #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 *static_cast(v.data); } //*********************************** template ETL_CONSTEXPR14 etl::variant_alternative_t>&& get(etl::variant&& v) { #if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11) static_assert(Index < sizeof...(TTypes), "Index out of range"); #endif using type = etl::variant_alternative_t>; return etl::move(*static_cast(v.data)); } //*********************************** template ETL_CONSTEXPR14 const etl::variant_alternative_t>& get(const etl::variant& v) { #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 *static_cast(v.data); } //*********************************** template ETL_CONSTEXPR14 const etl::variant_alternative_t>&& get(const etl::variant&& v) { #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)); } //*********************************** template ETL_CONSTEXPR14 T& get(etl::variant& v) { constexpr size_t Index = etl::private_variant::parameter_pack::template index_of_type::value; return get(v); } //*********************************** template ETL_CONSTEXPR14 T&& get(etl::variant&& v) { constexpr size_t Index = etl::private_variant::parameter_pack::template index_of_type::value; return get(etl::move(v)); } //*********************************** template ETL_CONSTEXPR14 const T& get(const etl::variant& v) { constexpr size_t Index = etl::private_variant::parameter_pack::template index_of_type::value; return get(v); } //*********************************** template ETL_CONSTEXPR14 const T&& get(const etl::variant&& v) { constexpr size_t Index = etl::private_variant::parameter_pack::template index_of_type::value; return get(etl::move(v)); } //*************************************************************************** /// get_if //*************************************************************************** template< size_t Index, typename... TTypes > ETL_CONSTEXPR14 etl::add_pointer_t>> get_if(etl::variant* pv) noexcept { if ((pv != nullptr) && (pv->index() == Index)) { return &etl::get(*pv); } else { return nullptr; } } //*********************************** template< size_t Index, typename... TTypes > ETL_CONSTEXPR14 etl::add_pointer_t>> get_if(const etl::variant* pv) noexcept { if ((pv != nullptr) && (pv->index() == Index)) { return &etl::get(*pv); } else { return nullptr; } } //*********************************** template< class T, typename... TTypes > ETL_CONSTEXPR14 etl::add_pointer_t get_if(etl::variant* pv) noexcept { constexpr size_t Index = etl::private_variant::parameter_pack::template index_of_type::value; if ((pv != nullptr) && (pv->index() == Index)) { return &etl::get(*pv); } else { return nullptr; } } //*********************************** template< typename T, typename... TTypes > ETL_CONSTEXPR14 etl::add_pointer_t get_if(const etl::variant* pv) noexcept { constexpr size_t Index = etl::private_variant::parameter_pack::template index_of_type::value; if ((pv != nullptr) && (pv->index() == Index)) { return &etl::get(*pv); } else { return nullptr; } } //*************************************************************************** /// swap //*************************************************************************** template void swap(etl::variant& lhs, etl::variant& rhs) { lhs.swap(rhs); } //*************************************************************************** /// variant_size //*************************************************************************** template struct variant_size; template struct variant_size> : etl::integral_constant { }; template struct variant_size : etl::integral_constant::value> { }; #if ETL_USING_CPP17 template inline constexpr size_t variant_size_v = variant_size::value; #endif //*************************************************************************** /// visit //*************************************************************************** namespace private_variant { template 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. //*************************************************************************** struct visit_auto_return { }; //*************************************************************************** /// Deduces return type of a call to TCallable with arguments Ts. /// A lite version of std::invoke_result. //*************************************************************************** template struct single_visit_result_type { using type = decltype(declval()(declval()...)); }; template using single_visit_result_type_t = typename single_visit_result_type::type; //*************************************************************************** /// Used to copy r/l value reference qualifier from a variant type to an /// element. //*************************************************************************** template 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`. //*************************************************************************** template