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 <jwellbelove@users.noreply.github.com>
This commit is contained in:
Roland Reichwein 2026-02-25 10:42:56 +01:00 committed by GitHub
parent 37aba58965
commit e439963258
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 130 additions and 20 deletions

View File

@ -410,6 +410,12 @@ namespace etl
template <typename T, typename... VTypes>
friend ETL_CONSTEXPR14 const T&& get(const etl::variant<VTypes...>&& v);
template< class T, typename... VTypes >
friend ETL_CONSTEXPR14 etl::add_pointer_t<T> get_if(etl::variant<VTypes...>* pv) ETL_NOEXCEPT;
template< class T, typename... VTypes >
friend ETL_CONSTEXPR14 etl::add_pointer_t<const T> get_if(const etl::variant<VTypes...>* 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 T, typename T0, typename T1, typename... Ts>
typename etl::enable_if_t<etl::is_same<T, T0>::value, bool>
ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT;
template<typename T, typename T0>
typename etl::enable_if_t<etl::is_same<T, T0>::value, bool>
ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT;
template<typename T, typename T0, typename T1, typename... Ts>
typename etl::enable_if_t<!etl::is_same<T, T0>::value, bool>
ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT;
template<typename T, typename T0>
typename etl::enable_if_t<!etl::is_same<T, T0>::value, bool>
ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT;
template<typename T, typename T0, typename T1, typename... Ts>
typename etl::enable_if_t<etl::is_same<T, T0>::value, bool>
ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT
{
if (index == 0)
{
return true;
}
else
{
return is_same_type_in<T, T1, Ts...>(index - 1);
}
}
template<typename T, typename T0>
typename etl::enable_if_t<etl::is_same<T, T0>::value, bool>
ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT
{
return index == 0;
}
template<typename T, typename T0, typename T1, typename... Ts>
typename etl::enable_if_t<!etl::is_same<T, T0>::value, bool>
ETL_CONSTEXPR14 is_same_type_in(size_t index) ETL_NOEXCEPT
{
if (index == 0)
{
return false;
}
else
{
return is_same_type_in<T, T1, Ts...>(index - 1);
}
}
template<typename T, typename T0>
typename etl::enable_if_t<!etl::is_same<T, T0>::value, bool>
ETL_CONSTEXPR14 is_same_type_in(size_t) ETL_NOEXCEPT
{
return false;
}
}
//***************************************************************************
/// Checks if the variant v holds the alternative T.
//***************************************************************************
template <typename T, typename... TTypes>
ETL_CONSTEXPR14 bool holds_alternative(const etl::variant<TTypes...>& v) ETL_NOEXCEPT
{
constexpr size_t Index = etl::type_list_index_of_type<etl::type_list<TTypes...>, T>::value;
return (Index == variant_npos) ? false : (v.index() == Index);
{
return private_variant::is_same_type_in<T, TTypes...>(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<Index, etl::variant<TTypes...>>;
return etl::move(*static_cast<type*>(v.data));
@ -1315,36 +1386,36 @@ namespace etl
template <typename T, typename... TTypes>
ETL_CONSTEXPR14 T& get(etl::variant<TTypes...>& v)
{
constexpr size_t Index = etl::type_list_index_of_type<etl::type_list<TTypes...>, T>::value;
ETL_ASSERT((private_variant::is_same_type_in<T, TTypes...>(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception));
return get<Index>(v);
return *static_cast<T*>(v.data);
}
//***********************************
template <typename T, typename... TTypes>
ETL_CONSTEXPR14 T&& get(etl::variant<TTypes...>&& v)
{
constexpr size_t Index = etl::type_list_index_of_type<etl::type_list<TTypes...>, T>::value;
ETL_ASSERT((private_variant::is_same_type_in<T, TTypes...>(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception));
return get<Index>(etl::move(v));
return etl::move(*static_cast<T*>(v.data));
}
//***********************************
template <typename T, typename... TTypes>
ETL_CONSTEXPR14 const T& get(const etl::variant<TTypes...>& v)
{
constexpr size_t Index = etl::type_list_index_of_type<etl::type_list<TTypes...>, T>::value;
ETL_ASSERT((private_variant::is_same_type_in<T, TTypes...>(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception));
return get<Index>(v);
return *static_cast<const T*>(v.data);
}
//***********************************
template <typename T, typename... TTypes>
ETL_CONSTEXPR14 const T&& get(const etl::variant<TTypes...>&& v)
{
constexpr size_t Index = etl::type_list_index_of_type<etl::type_list<TTypes...>, T>::value;
ETL_ASSERT((private_variant::is_same_type_in<T, TTypes...>(v.index())), ETL_ERROR(etl::variant_incorrect_type_exception));
return get<Index>(etl::move(v));
return etl::move(*static_cast<const T*>(v.data));
}
//***************************************************************************
@ -1383,11 +1454,9 @@ namespace etl
template< class T, typename... TTypes >
ETL_CONSTEXPR14 etl::add_pointer_t<T> get_if(etl::variant<TTypes...>* pv) ETL_NOEXCEPT
{
constexpr size_t Index = etl::type_list_index_of_type<etl::type_list<TTypes...>, T>::value;
if ((pv != nullptr) && (pv->index() == Index))
if ((pv != nullptr) && (private_variant::is_same_type_in<T, TTypes...>(pv->index())))
{
return &etl::get<Index>(*pv);
return static_cast<T*>(pv->data);
}
else
{
@ -1399,11 +1468,9 @@ namespace etl
template< typename T, typename... TTypes >
ETL_CONSTEXPR14 etl::add_pointer_t<const T> get_if(const etl::variant<TTypes...>* pv) ETL_NOEXCEPT
{
constexpr size_t Index = etl::type_list_index_of_type<etl::type_list<TTypes...>, T>::value;
if ((pv != nullptr) && (pv->index() == Index))
if ((pv != nullptr) && (private_variant::is_same_type_in<T, TTypes...>(pv->index())))
{
return &etl::get<Index>(*pv);
return static_cast<const T*>(pv->data);
}
else
{

View File

@ -2182,6 +2182,49 @@ namespace
CHECK_TRUE(variant1.is_supported_type<std::string>());
CHECK_FALSE(variant1.is_supported_type<double>());
}
//*************************************************************************
TEST(test_variant_same_types_get)
{
etl::variant<int, int> v1;
etl::variant<int, int> 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<int>(v1));
CHECK(etl::get_if<int>(&v1) != nullptr);
}
}
}