From e439963258f56d8956417a44a5d677eaf1ec0c20 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 25 Feb 2026 10:42:56 +0100 Subject: [PATCH] Fix etl::variant get() for ambiguous types (#1304) When a type occured multiple times in a variant, comparison operator asserted before. Co-authored-by: John Wellbelove --- include/etl/private/variant_variadic.h | 107 ++++++++++++++++++++----- test/test_variant_variadic.cpp | 43 ++++++++++ 2 files changed, 130 insertions(+), 20 deletions(-) diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 4363ebfa..b1f00142 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -410,6 +410,12 @@ namespace etl template friend ETL_CONSTEXPR14 const T&& get(const etl::variant&& v); + template< class T, typename... VTypes > + friend ETL_CONSTEXPR14 etl::add_pointer_t get_if(etl::variant* pv) ETL_NOEXCEPT; + + template< class T, typename... VTypes > + friend ETL_CONSTEXPR14 etl::add_pointer_t get_if(const etl::variant* pv) ETL_NOEXCEPT; + private: // All types of variant are friends. @@ -1218,15 +1224,78 @@ namespace etl size_t type_id; }; + namespace private_variant + { + //*************************************************************************** + /// is_same_type_in. + /// Checks if specified type T is at specified index in given type list + //*************************************************************************** + template + typename etl::enable_if_t::value, bool> + ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT; + + template + typename etl::enable_if_t::value, bool> + ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT; + + template + typename etl::enable_if_t::value, bool> + ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT; + + template + typename etl::enable_if_t::value, bool> + ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT; + + template + typename etl::enable_if_t::value, bool> + ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT + { + if (index == 0) + { + return true; + } + else + { + return is_same_type_in(index - 1); + } + } + + template + typename etl::enable_if_t::value, bool> + ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT + { + return index == 0; + } + + template + typename etl::enable_if_t::value, bool> + ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT + { + if (index == 0) + { + return false; + } + else + { + return is_same_type_in(index - 1); + } + } + + template + typename etl::enable_if_t::value, bool> + ETL_CONSTEXPR14 is_same_type_in(size_t) ETL_NOEXCEPT + { + return false; + } + } + //*************************************************************************** /// Checks if the variant v holds the alternative T. //*************************************************************************** template ETL_CONSTEXPR14 bool holds_alternative(const etl::variant& v) ETL_NOEXCEPT - { - constexpr size_t Index = etl::type_list_index_of_type, T>::value; - - return (Index == variant_npos) ? false : (v.index() == Index); + { + return private_variant::is_same_type_in(v.index()); } //*************************************************************************** @@ -1274,6 +1343,8 @@ namespace etl 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)); @@ -1315,36 +1386,36 @@ namespace etl template ETL_CONSTEXPR14 T& get(etl::variant& v) { - constexpr size_t Index = etl::type_list_index_of_type, T>::value; + ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return get(v); + return *static_cast(v.data); } //*********************************** template ETL_CONSTEXPR14 T&& get(etl::variant&& v) { - constexpr size_t Index = etl::type_list_index_of_type, T>::value; + ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return get(etl::move(v)); + return etl::move(*static_cast(v.data)); } //*********************************** template ETL_CONSTEXPR14 const T& get(const etl::variant& v) { - constexpr size_t Index = etl::type_list_index_of_type, T>::value; + ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return get(v); + return *static_cast(v.data); } //*********************************** template ETL_CONSTEXPR14 const T&& get(const etl::variant&& v) { - constexpr size_t Index = etl::type_list_index_of_type, T>::value; + ETL_ASSERT((private_variant::is_same_type_in(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception)); - return get(etl::move(v)); + return etl::move(*static_cast(v.data)); } //*************************************************************************** @@ -1383,11 +1454,9 @@ namespace etl template< class T, typename... TTypes > ETL_CONSTEXPR14 etl::add_pointer_t get_if(etl::variant* pv) ETL_NOEXCEPT { - constexpr size_t Index = etl::type_list_index_of_type, T>::value; - - if ((pv != nullptr) && (pv->index() == Index)) + if ((pv != nullptr) && (private_variant::is_same_type_in(pv->index()))) { - return &etl::get(*pv); + return static_cast(pv->data); } else { @@ -1399,11 +1468,9 @@ namespace etl template< typename T, typename... TTypes > ETL_CONSTEXPR14 etl::add_pointer_t get_if(const etl::variant* pv) ETL_NOEXCEPT { - constexpr size_t Index = etl::type_list_index_of_type, T>::value; - - if ((pv != nullptr) && (pv->index() == Index)) + if ((pv != nullptr) && (private_variant::is_same_type_in(pv->index()))) { - return &etl::get(*pv); + return static_cast(pv->data); } else { diff --git a/test/test_variant_variadic.cpp b/test/test_variant_variadic.cpp index bf684bcd..e1c86f93 100644 --- a/test/test_variant_variadic.cpp +++ b/test/test_variant_variadic.cpp @@ -2182,6 +2182,49 @@ namespace CHECK_TRUE(variant1.is_supported_type()); CHECK_FALSE(variant1.is_supported_type()); } + + //************************************************************************* + TEST(test_variant_same_types_get) + { + etl::variant v1; + etl::variant v2; + + v1.emplace<0>(1); + v2.emplace<0>(1); + + CHECK(v1 == v2); + + v1.emplace<0>(1); + v2.emplace<1>(1); + + CHECK(v1 != v2); + + v1.emplace<0>(0); + v2.emplace<0>(1); + + CHECK(v1 != v2); + + v1.emplace<1>(0); + v2.emplace<1>(1); + + CHECK(v1 != v2); + + v1.emplace<1>(1); + v2.emplace<1>(1); + + CHECK(v1 == v2); + + v1.emplace<0>(42); + CHECK_EQUAL(42, etl::get<0>(v1)); + CHECK_EQUAL(0U, v1.index()); + + v1.emplace<1>(99); + CHECK_EQUAL(99, etl::get<1>(v1)); + CHECK_EQUAL(1U, v1.index()); + + CHECK(etl::holds_alternative(v1)); + CHECK(etl::get_if(&v1) != nullptr); + } } }