From 89be7e62cf69798267fcaec2db5de242ecd34ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20S=C3=B6derholm?= Date: Thu, 9 Jun 2022 09:56:55 +0200 Subject: [PATCH] Feature/variant visit (#552) * add variant visit to c++11 and above * visit legacy added * update with multiple variants visit for variadic --- include/etl/private/variant_legacy.h | 105 +++++++++++++ include/etl/private/variant_variadic.h | 195 +++++++++++++++++++++++++ test/test_variant_legacy.cpp | 74 ++++++++++ test/test_variant_variadic.cpp | 158 ++++++++++++++++++++ 4 files changed, 532 insertions(+) diff --git a/include/etl/private/variant_legacy.h b/include/etl/private/variant_legacy.h index a224d48e..8d6ba1a3 100644 --- a/include/etl/private/variant_legacy.h +++ b/include/etl/private/variant_legacy.h @@ -94,6 +94,17 @@ namespace etl } }; + //*************************************************************************** + /// '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"A"), 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. @@ -1057,4 +1068,98 @@ namespace etl //*************************************************************************** type_id_t type_id; }; + + namespace private_variant + { + template + struct variant_alternative_helper; +#define ETL_VARIANT_HELPER(INDEX, TYPE) \ + template \ + struct variant_alternative_helper> \ + { \ + typedef TYPE type; \ + }; + ETL_VARIANT_HELPER(0, T1) + ETL_VARIANT_HELPER(1, T2) + ETL_VARIANT_HELPER(2, T3) + ETL_VARIANT_HELPER(3, T4) + ETL_VARIANT_HELPER(4, T5) + ETL_VARIANT_HELPER(5, T6) + ETL_VARIANT_HELPER(6, T7) + ETL_VARIANT_HELPER(7, T8) +#undef ETL_VARIANT_HELPER + } // namespace private_variant + template + struct variant_alternative + { + typedef typename private_variant::variant_alternative_helper::type type; + }; + template + struct variant_alternative + { + typedef typename private_variant::variant_alternative_helper::type const type; + }; + template + struct variant_alternative + { + typedef typename private_variant::variant_alternative_helper::type volatile type; + }; + template + struct variant_alternative + { + typedef typename private_variant::variant_alternative_helper::type const volatile type; + }; + template + inline T& get(TVariant& variant) + { + return variant.template get(); + } + template + inline T const& get(TVariant const& variant) + { + return variant.template get(); + } + template + inline typename variant_alternative::type& get(TVariant& variant) + { + return get::type>(variant); + } + template + inline typename variant_alternative::type& get(TVariant const& variant) + { + return get::type>(variant); + } + +#define ETL_GEN_LEGACY_VISIT(VISITQUAL, VARIANTQUAL) \ + template \ + static TRet visit(TVisitor VISITQUAL visitor, TVariant VARIANTQUAL variant) \ + { \ + switch (variant.index()) \ + { \ + case 0: return static_cast(visitor(get<0>(variant))); \ + case 1: return static_cast(visitor(get<1>(variant))); \ + case 2: return static_cast(visitor(get<2>(variant))); \ + case 3: return static_cast(visitor(get<3>(variant))); \ + case 4: return static_cast(visitor(get<4>(variant))); \ + case 5: return static_cast(visitor(get<5>(variant))); \ + case 6: return static_cast(visitor(get<6>(variant))); \ + case 7: return static_cast(visitor(get<7>(variant))); \ + default: ETL_ASSERT(false, ETL_ERROR(bad_variant_access)); \ + } \ + } + + ETL_GEN_LEGACY_VISIT(&, &) + ETL_GEN_LEGACY_VISIT(const&, &) + ETL_GEN_LEGACY_VISIT(&, const&) + ETL_GEN_LEGACY_VISIT(const&, const&) + +#undef ETL_GEN_LEGACY_VISIT + } diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 9c4b6bba..e85e8045 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -426,6 +426,17 @@ namespace etl } }; + //*************************************************************************** + /// '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"A"), 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. @@ -1265,5 +1276,189 @@ namespace etl template inline constexpr size_t variant_size_v = variant_size::value; #endif + + //*************************************************************************** + /// visit + //*************************************************************************** + namespace private_variant + { + //*************************************************************************** + /// 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(std::declval()(std::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