diff options
author | Ali Mohammad Pur <ali.mpfard@gmail.com> | 2022-11-10 12:42:19 +0330 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2022-11-10 16:02:42 +0330 |
commit | 40b07901ac3cdc4e8942bff62f4784370543003b (patch) | |
tree | 1fbc8773c48520df6451c829723dba7118b9f3ea | |
parent | 4a9218451dbf6bd5b58addd37f7e334669882ece (diff) | |
download | serenity-40b07901ac3cdc4e8942bff62f4784370543003b.zip |
AK: Allow Variant::downcast<OtherVariantType>()
We usually give type aliases to variants, so their variant types are not
always available, so make it possible to downcast to another variant
type.
-rw-r--r-- | AK/Variant.h | 54 | ||||
-rw-r--r-- | Tests/AK/TestVariant.cpp | 7 |
2 files changed, 44 insertions, 17 deletions
diff --git a/AK/Variant.h b/AK/Variant.h index 8962b7ee0a..eb6500bc44 100644 --- a/AK/Variant.h +++ b/AK/Variant.h @@ -406,30 +406,50 @@ public: } template<typename... NewTs> - Variant<NewTs...> downcast() && + decltype(auto) downcast() && + { + if constexpr (sizeof...(NewTs) == 1 && (IsSpecializationOf<NewTs, Variant> && ...)) { + return move(*this).template downcast_variant<NewTs...>(); + } else { + Variant<NewTs...> instance { Variant<NewTs...>::invalid_index, Detail::VariantConstructTag {} }; + visit([&](auto& value) { + if constexpr (Variant<NewTs...>::template can_contain<RemoveCVReference<decltype(value)>>()) + instance.set(move(value), Detail::VariantNoClearTag {}); + }); + VERIFY(instance.m_index != instance.invalid_index); + return instance; + } + } + + template<typename... NewTs> + decltype(auto) downcast() const& + { + if constexpr (sizeof...(NewTs) == 1 && (IsSpecializationOf<NewTs, Variant> && ...)) { + return (*this).template downcast_variant(TypeWrapper<NewTs...> {}); + } else { + Variant<NewTs...> instance { Variant<NewTs...>::invalid_index, Detail::VariantConstructTag {} }; + visit([&](auto const& value) { + if constexpr (Variant<NewTs...>::template can_contain<RemoveCVReference<decltype(value)>>()) + instance.set(value, Detail::VariantNoClearTag {}); + }); + VERIFY(instance.m_index != instance.invalid_index); + return instance; + } + } + +private: + template<typename... NewTs> + Variant<NewTs...> downcast_variant(TypeWrapper<Variant<NewTs...>>) && { - Variant<NewTs...> instance { Variant<NewTs...>::invalid_index, Detail::VariantConstructTag {} }; - visit([&](auto& value) { - if constexpr (Variant<NewTs...>::template can_contain<RemoveCVReference<decltype(value)>>()) - instance.set(move(value), Detail::VariantNoClearTag {}); - }); - VERIFY(instance.m_index != instance.invalid_index); - return instance; + return move(*this).template downcast<NewTs...>(); } template<typename... NewTs> - Variant<NewTs...> downcast() const& + Variant<NewTs...> downcast_variant(TypeWrapper<Variant<NewTs...>>) const& { - Variant<NewTs...> instance { Variant<NewTs...>::invalid_index, Detail::VariantConstructTag {} }; - visit([&](auto const& value) { - if constexpr (Variant<NewTs...>::template can_contain<RemoveCVReference<decltype(value)>>()) - instance.set(value, Detail::VariantNoClearTag {}); - }); - VERIFY(instance.m_index != instance.invalid_index); - return instance; + return (*this).template downcast<NewTs...>(); } -private: static constexpr auto data_size = Detail::integer_sequence_generate_array<size_t>(0, IntegerSequence<size_t, sizeof(Ts)...>()).max(); static constexpr auto data_alignment = Detail::integer_sequence_generate_array<size_t>(0, IntegerSequence<size_t, alignof(Ts)...>()).max(); using Helper = Detail::Variant<IndexType, 0, Ts...>; diff --git a/Tests/AK/TestVariant.cpp b/Tests/AK/TestVariant.cpp index f55d176537..ea7998a099 100644 --- a/Tests/AK/TestVariant.cpp +++ b/Tests/AK/TestVariant.cpp @@ -125,6 +125,13 @@ TEST_CASE(verify_cast) EXPECT(one_integer_to_rule_them_all.has<i8>()); EXPECT_EQ(fake_integer.get<i8>(), 60); EXPECT_EQ(one_integer_to_rule_them_all.get<i8>(), 60); + + using SomeFancyType = Variant<i8, i16>; + one_integer_to_rule_them_all = fake_integer.downcast<SomeFancyType>(); + EXPECT(fake_integer.has<i8>()); + EXPECT(one_integer_to_rule_them_all.has<i8>()); + EXPECT_EQ(fake_integer.get<i8>(), 60); + EXPECT_EQ(one_integer_to_rule_them_all.get<i8>(), 60); } TEST_CASE(moved_from_state) |