SpECTRE Documentation Coverage Report
Current view: top level - DataStructures - TaggedVariant.hpp Hit Total Coverage
Commit: a8efe75339f4781ca06d43fed14c40144d5e8a08 Lines: 22 46 47.8 %
Date: 2024-10-17 21:19:21
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <array>
       7             : #include <cstddef>
       8             : #include <functional>
       9             : #include <type_traits>
      10             : #include <utility>
      11             : #include <variant>
      12             : 
      13             : #include "Options/String.hpp"
      14             : #include "Utilities/ErrorHandling/Error.hpp"
      15             : #include "Utilities/Gsl.hpp"
      16             : #include "Utilities/Requires.hpp"
      17             : #include "Utilities/Serialization/PupStlCpp17.hpp"
      18             : #include "Utilities/TMPL.hpp"
      19             : 
      20             : /// \cond
      21             : namespace Options {
      22             : template <typename... AlternativeLists>
      23             : struct Alternatives;
      24             : }  // namespace Options
      25             : namespace PUP {
      26             : class er;
      27             : }  // namespace PUP
      28             : /// \endcond
      29             : 
      30             : /// \ingroup DataStructuresGroup
      31             : ///
      32             : /// TaggedVariant and related functionality.
      33           1 : namespace variants {
      34             : namespace TaggedVariant_detail {
      35             : template <typename R, typename Variant, typename Tag, typename Visitor>
      36             : struct VisitAlternative;
      37             : }  // namespace TaggedVariant_detail
      38             : 
      39             : /// \cond
      40             : template <typename... Tags>
      41             : class TaggedVariant;
      42             : template <typename... Tags>
      43             : constexpr bool operator==(const TaggedVariant<Tags...>& a,
      44             :                           const TaggedVariant<Tags...>& b);
      45             : template <typename... Tags>
      46             : constexpr bool operator<(const TaggedVariant<Tags...>& a,
      47             :                          const TaggedVariant<Tags...>& b);
      48             : /// \endcond
      49             : 
      50             : /// \ingroup DataStructuresGroup
      51             : ///
      52             : /// A class similar to `std::variant`, but indexed by tag structs.
      53             : ///
      54             : /// \see variants::get, variants::get_if, variants::holds_alternative,
      55             : /// variants::visit
      56             : template <typename... Tags>
      57           1 : class TaggedVariant {
      58             :  private:
      59             :   static_assert(sizeof...(Tags) > 0);
      60             :   static_assert(
      61             :       std::is_same_v<tmpl::remove_duplicates<TaggedVariant>, TaggedVariant>,
      62             :       "TaggedVariant cannot have duplicate tags.");
      63             : 
      64             :   template <typename Tag>
      65           0 :   static constexpr size_t data_index =
      66             :       tmpl::index_of<TaggedVariant, Tag>::value;
      67             : 
      68             :  public:
      69             :   /// A default constructed instance has the first tag active.
      70           1 :   TaggedVariant() = default;
      71           0 :   TaggedVariant(const TaggedVariant&) = default;
      72           0 :   TaggedVariant(TaggedVariant&&) = default;
      73           0 :   TaggedVariant& operator=(const TaggedVariant&) = default;
      74           0 :   TaggedVariant& operator=(TaggedVariant&&) = default;
      75           0 :   ~TaggedVariant() = default;
      76             : 
      77             :   /// Construct with \p Tag active, using \p args to construct the
      78             :   /// contained object.
      79             :   ///
      80             :   /// \snippet DataStructures/Test_TaggedVariant.cpp construct in_place_type
      81             :   template <
      82             :       typename Tag, typename... Args,
      83             :       Requires<(... or std::is_same_v<Tag, Tags>) and
      84             :                std::is_constructible_v<typename Tag::type, Args...>> = nullptr>
      85           1 :   constexpr explicit TaggedVariant(std::in_place_type_t<Tag> /*meta*/,
      86             :                                    Args&&... args)
      87             :       : data_(std::in_place_index<data_index<Tag>>,
      88             :               std::forward<Args>(args)...) {}
      89             : 
      90             :   /// Construct the contained object from \p args.  Only available if
      91             :   /// the TaggedVariant only has one tag.
      92             :   ///
      93             :   /// \snippet DataStructures/Test_TaggedVariant.cpp construct single
      94             :   template <typename... Args,
      95             :             Requires<sizeof...(Tags) == 1 and
      96             :                      std::is_constructible_v<
      97             :                          typename tmpl::front<TaggedVariant>::type, Args...>> =
      98             :                 nullptr>
      99           1 :   constexpr explicit TaggedVariant(Args&&... args)
     100             :       : TaggedVariant(std::in_place_type<tmpl::front<TaggedVariant>>,
     101             :                       std::forward<Args>(args)...) {}
     102             : 
     103             :   /// A TaggedVariant can be implicitly move-converted to another
     104             :   /// variant with a superset of the tags.
     105             :   ///
     106             :   /// \snippet DataStructures/Test_TaggedVariant.cpp convert
     107             :   /// @{
     108             :   template <typename... OtherTags,
     109             :             Requires<tmpl::size<tmpl::list_difference<
     110             :                          TaggedVariant<OtherTags...>, TaggedVariant>>::value ==
     111             :                      0> = nullptr>
     112             :   // NOLINTNEXTLINE(google-explicit-constructor)
     113           1 :   constexpr TaggedVariant(TaggedVariant<OtherTags...>&& other);
     114             : 
     115             :   template <typename... OtherTags,
     116             :             Requires<tmpl::size<tmpl::list_difference<
     117             :                          TaggedVariant<OtherTags...>, TaggedVariant>>::value ==
     118             :                      0> = nullptr>
     119           1 :   constexpr TaggedVariant& operator=(TaggedVariant<OtherTags...>&& other);
     120             :   /// @}
     121             : 
     122             :   /// The index into the `Tags...` of the active object.
     123           1 :   constexpr size_t index() const { return data_.index(); }
     124             : 
     125             :   /// See `std::variant::valueless_by_exception`.
     126           1 :   constexpr bool valueless_by_exception() const {
     127             :     return data_.valueless_by_exception();
     128             :   }
     129             : 
     130             :   /// Destroys the contained object and actives \p Tag, constructing a
     131             :   /// new value from \p args.
     132             :   ///
     133             :   /// \snippet DataStructures/Test_TaggedVariant.cpp emplace
     134             :   template <
     135             :       typename Tag, typename... Args,
     136             :       Requires<(... or std::is_same_v<Tag, Tags>) and
     137             :                std::is_constructible_v<typename Tag::type, Args...>> = nullptr>
     138           1 :   constexpr typename Tag::type& emplace(Args&&... args) {
     139             :     return data_.template emplace<data_index<Tag>>(std::forward<Args>(args)...);
     140             :   }
     141             : 
     142           0 :   constexpr void swap(TaggedVariant& other) noexcept(noexcept(
     143             :       (... and (std::is_nothrow_move_constructible_v<typename Tags::type> and
     144             :                 std::is_nothrow_swappable_v<typename Tags::type>)))) {
     145             :     data_.swap(other.data_);
     146             :   }
     147             : 
     148           0 :   void pup(PUP::er& p) { p | data_; }
     149             : 
     150             :   /// A TaggedVariant over option tags can be parsed as any of them.
     151             :   /// @{
     152           1 :   static constexpr Options::String help = "One of multiple options";
     153           1 :   using options = tmpl::list<Options::Alternatives<tmpl::list<Tags>...>>;
     154             :   template <typename Tag>
     155           1 :   explicit TaggedVariant(tmpl::list<Tag> /*meta*/, typename Tag::type value)
     156             :       : TaggedVariant(std::in_place_type<Tag>, std::move(value)) {}
     157             :   /// @}
     158             : 
     159             :  private:
     160             :   template <typename R, typename Variant, typename Tag, typename Visitor>
     161             :   friend struct TaggedVariant_detail::VisitAlternative;
     162             : 
     163             :   template <typename Tag, typename... Tags2>
     164           0 :   friend constexpr typename Tag::type& get(TaggedVariant<Tags2...>& variant);
     165             :   template <typename Tag, typename... Tags2>
     166           0 :   friend constexpr const typename Tag::type& get(
     167             :       const TaggedVariant<Tags2...>& variant);
     168             :   template <typename Tag, typename... Tags2>
     169           0 :   friend constexpr typename Tag::type&& get(TaggedVariant<Tags2...>&& variant);
     170             :   template <typename Tag, typename... Tags2>
     171           0 :   friend constexpr const typename Tag::type&& get(
     172             :       const TaggedVariant<Tags2...>&& variant);
     173             : 
     174           0 :   friend constexpr bool operator== <Tags...>(const TaggedVariant<Tags...>& a,
     175             :                                              const TaggedVariant<Tags...>& b);
     176           0 :   friend constexpr bool operator< <Tags...>(const TaggedVariant<Tags...>& a,
     177             :                                             const TaggedVariant<Tags...>& b);
     178             : 
     179             :   friend struct std::hash<TaggedVariant>;
     180             : 
     181           0 :   std::variant<typename Tags::type...> data_;
     182             : };
     183             : 
     184             : namespace TaggedVariant_detail {
     185             : template <typename... Tags>
     186             : constexpr bool is_variant_or_derived(const TaggedVariant<Tags...>* /*meta*/) {
     187             :   return true;
     188             : }
     189             : // NOLINTNEXTLINE(cert-dcl50-cpp) - variadic function
     190             : constexpr bool is_variant_or_derived(...) { return false; }
     191             : 
     192             : template <typename Tag, typename Value>
     193             : constexpr std::pair<tmpl::type_<Tag>, Value&&> make_visitor_pair(
     194             :     Value&& value) {
     195             :   return {tmpl::type_<Tag>{}, std::forward<Value>(value)};
     196             : }
     197             : 
     198             : struct DeduceReturn;
     199             : 
     200             : template <typename R, typename Variant, typename Tag, typename Visitor>
     201             : struct VisitAlternative {
     202             :   static constexpr R apply(Variant&& variant, const Visitor& visitor) {
     203             :     return std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(
     204             :         get<std::decay_t<Variant>::template data_index<Tag>>(
     205             :             std::forward<Variant>(variant).data_)));
     206             :   }
     207             : };
     208             : 
     209             : template <typename Variant, typename Tag, typename Visitor>
     210             : struct VisitAlternative<void, Variant, Tag, Visitor> {
     211             :   static constexpr void apply(Variant&& variant, const Visitor& visitor) {
     212             :     std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(
     213             :         get<std::decay_t<Variant>::template data_index<Tag>>(
     214             :             std::forward<Variant>(variant).data_)));
     215             :   }
     216             : };
     217             : 
     218             : template <typename Variant, typename Tag, typename Visitor>
     219             : struct VisitAlternative<DeduceReturn, Variant, Tag, Visitor> {
     220             :   static constexpr decltype(auto) apply(Variant&& variant,
     221             :                                         const Visitor& visitor) {
     222             :     return std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(
     223             :         get<std::decay_t<Variant>::template data_index<Tag>>(
     224             :             std::forward<Variant>(variant).data_)));
     225             :   }
     226             : };
     227             : 
     228             : template <typename... Tags>
     229             : constexpr TaggedVariant<Tags...>& as_variant(TaggedVariant<Tags...>& variant) {
     230             :   return variant;
     231             : }
     232             : template <typename... Tags>
     233             : constexpr const TaggedVariant<Tags...>& as_variant(
     234             :     const TaggedVariant<Tags...>& variant) {
     235             :   return variant;
     236             : }
     237             : template <typename... Tags>
     238             : constexpr TaggedVariant<Tags...>&& as_variant(
     239             :     TaggedVariant<Tags...>&& variant) {
     240             :   return std::move(variant);
     241             : }
     242             : template <typename... Tags>
     243             : constexpr const TaggedVariant<Tags...>&& as_variant(
     244             :     const TaggedVariant<Tags...>&& variant) {
     245             :   return std::move(variant);
     246             : }
     247             : 
     248             : template <typename R, typename Visitor, typename... Variants>
     249             : constexpr decltype(auto) visit_impl(Visitor&& visitor) {
     250             :   return std::forward<Visitor>(visitor)();
     251             : }
     252             : 
     253             : template <typename R, typename Variant, typename Visitor,
     254             :           typename DecayedVariant = std::decay_t<Variant>>
     255             : struct VisitJumpTable;
     256             : 
     257             : template <typename R, typename Variant, typename Visitor, typename... Tags>
     258             : struct VisitJumpTable<R, Variant, Visitor, TaggedVariant<Tags...>> {
     259             :   static constexpr std::array value{
     260             :       VisitAlternative<R, Variant, Tags, Visitor>::apply...};
     261             : };
     262             : 
     263             : template <typename R, typename Visitor, typename FirstVariant,
     264             :           typename... Variants>
     265             : constexpr decltype(auto) visit_impl(Visitor&& visitor,
     266             :                                     FirstVariant&& first_variant,
     267             :                                     Variants&&... variants) {
     268             :   const auto recurse = [&]<typename Arg>(Arg&& first_arg) {
     269             :     return visit_impl<R>(
     270             :         [&]<typename... Rest>(Rest&&... rest) {
     271             :           return std::forward<Visitor>(visitor)(std::forward<Arg>(first_arg),
     272             :                                                 std::forward<Rest>(rest)...);
     273             :         },
     274             :         std::forward<Variants>(variants)...);
     275             :   };
     276             :   if (UNLIKELY(first_variant.valueless_by_exception())) {
     277             :     throw std::bad_variant_access{};
     278             :   }
     279             :   return gsl::at(VisitJumpTable<R, FirstVariant, decltype(recurse)>::value,
     280             :                  first_variant.index())(
     281             :       std::forward<FirstVariant>(first_variant), recurse);
     282             : }
     283             : }  // namespace TaggedVariant_detail
     284             : 
     285             : /// Call \p visitor with the contents of one or more variants.
     286             : ///
     287             : /// Calls \p visitor with the contents of each variant as arguments,
     288             : /// passed as `std::pair<tmpl::type_<Tag>, typename Tag::type ref>`,
     289             : /// where `Tag` is the active tag of the variant and `ref` is a
     290             : /// reference qualifier matching that of the passed variant.
     291             : ///
     292             : /// If the template parameter \p R is supplied, the result is
     293             : /// implicitly converted to that type (which may be `void`).
     294             : /// Otherwise it is deduced from the return type of \p visitor, which
     295             : /// must be the same for all tags in the variant.
     296             : ///
     297             : /// \warning Unlike `visit` for `std::variant`, the types of the
     298             : /// visitor arguments do not allow for implicit conversions between
     299             : /// reference types.  If the visitor expects, for example,
     300             : /// `std::pair<tmpl::type_<Tag>, const typename Tag::type&>`, the caller must
     301             : /// ensure that the passed variant is a const lvalue.
     302             : ///
     303             : /// \snippet DataStructures/Test_TaggedVariant.cpp visit
     304             : /// @{
     305             : template <typename Visitor, typename... Variants,
     306             :           Requires<(... and TaggedVariant_detail::is_variant_or_derived(
     307             :                                 std::add_pointer_t<std::remove_reference_t<
     308             :                                     Variants>>{}))> = nullptr>
     309           1 : constexpr decltype(auto) visit(Visitor&& visitor, Variants&&... variants) {
     310             :   return TaggedVariant_detail::visit_impl<TaggedVariant_detail::DeduceReturn>(
     311             :       visitor,
     312             :       TaggedVariant_detail::as_variant(std::forward<Variants>(variants))...);
     313             : }
     314             : 
     315             : template <typename R, typename Visitor, typename... Variants,
     316             :           Requires<(... and TaggedVariant_detail::is_variant_or_derived(
     317             :                                 std::add_pointer_t<std::remove_reference_t<
     318             :                                     Variants>>{}))> = nullptr>
     319           1 : constexpr R visit(Visitor&& visitor, Variants&&... variants) {
     320             :   return TaggedVariant_detail::visit_impl<R>(
     321             :       visitor,
     322             :       TaggedVariant_detail::as_variant(std::forward<Variants>(variants))...);
     323             : }
     324             : /// @}
     325             : 
     326             : /// Check whether \p Tag is active.
     327             : template <typename Tag, typename... Tags>
     328           1 : constexpr bool holds_alternative(const TaggedVariant<Tags...>& variant) {
     329             :   return variant.index() == tmpl::index_of<TaggedVariant<Tags...>, Tag>::value;
     330             : }
     331             : 
     332             : /// Access the contained object.  Throws `std::bad_variant_access` if
     333             : /// \p Tag is not active.
     334             : /// @{
     335             : template <typename Tag, typename... Tags>
     336           1 : constexpr typename Tag::type& get(TaggedVariant<Tags...>& variant) {
     337             :   return get<TaggedVariant<Tags...>::template data_index<Tag>>(variant.data_);
     338             : }
     339             : template <typename Tag, typename... Tags>
     340           1 : constexpr const typename Tag::type& get(const TaggedVariant<Tags...>& variant) {
     341             :   return get<TaggedVariant<Tags...>::template data_index<Tag>>(variant.data_);
     342             : }
     343             : template <typename Tag, typename... Tags>
     344           1 : constexpr typename Tag::type&& get(TaggedVariant<Tags...>&& variant) {
     345             :   return get<TaggedVariant<Tags...>::template data_index<Tag>>(
     346             :       std::move(variant.data_));
     347             : }
     348             : template <typename Tag, typename... Tags>
     349           1 : constexpr const typename Tag::type&& get(
     350             :     const TaggedVariant<Tags...>&& variant) {
     351             :   return get<TaggedVariant<Tags...>::template data_index<Tag>>(
     352             :       std::move(variant.data_));
     353             : }
     354             : /// @}
     355             : 
     356             : /// Returns a pointer to the contained object if \p variant is a
     357             : /// non-null pointer and \p Tag is active.  Otherwise, returns
     358             : /// `nullptr`.
     359             : /// @{
     360             : template <typename Tag, typename... Tags>
     361           1 : constexpr const typename Tag::type* get_if(
     362             :     const TaggedVariant<Tags...>* variant) {
     363             :   if (variant != nullptr and holds_alternative<Tag>(*variant)) {
     364             :     return &get<Tag>(*variant);
     365             :   } else {
     366             :     return nullptr;
     367             :   }
     368             : }
     369             : template <typename Tag, typename... Tags>
     370           1 : constexpr typename Tag::type* get_if(TaggedVariant<Tags...>* variant) {
     371             :   if (variant != nullptr and holds_alternative<Tag>(*variant)) {
     372             :     return &get<Tag>(*variant);
     373             :   } else {
     374             :     return nullptr;
     375             :   }
     376             : }
     377             : /// @}
     378             : 
     379             : template <typename... Tags>
     380           0 : constexpr bool operator==(const TaggedVariant<Tags...>& a,
     381             :                           const TaggedVariant<Tags...>& b) {
     382             :   return a.data_ == b.data_;
     383             : }
     384             : template <typename... Tags>
     385           0 : constexpr bool operator!=(const TaggedVariant<Tags...>& a,
     386             :                           const TaggedVariant<Tags...>& b) {
     387             :   return not(a == b);
     388             : }
     389             : template <typename... Tags>
     390           0 : constexpr bool operator<(const TaggedVariant<Tags...>& a,
     391             :                          const TaggedVariant<Tags...>& b) {
     392             :   return a.data_ < b.data_;
     393             : }
     394             : template <typename... Tags>
     395           0 : constexpr bool operator>(const TaggedVariant<Tags...>& a,
     396             :                          const TaggedVariant<Tags...>& b) {
     397             :   return b < a;
     398             : }
     399             : template <typename... Tags>
     400           0 : constexpr bool operator<=(const TaggedVariant<Tags...>& a,
     401             :                           const TaggedVariant<Tags...>& b) {
     402             :   return not(b < a);
     403             : }
     404             : template <typename... Tags>
     405           0 : constexpr bool operator>=(const TaggedVariant<Tags...>& a,
     406             :                           const TaggedVariant<Tags...>& b) {
     407             :   return not(a < b);
     408             : }
     409             : 
     410             : template <
     411             :     typename... Tags,
     412             :     Requires<(... and (std::is_move_constructible_v<typename Tags::type> and
     413             :                        std::is_swappable_v<typename Tags::type>))> = nullptr>
     414           0 : constexpr void swap(TaggedVariant<Tags...>& a,
     415             :                     TaggedVariant<Tags...>& b) noexcept(noexcept(a.swap(b))) {
     416             :   a.swap(b);
     417             : }
     418             : 
     419             : template <typename... Tags>
     420             : template <
     421             :     typename... OtherTags,
     422             :     Requires<tmpl::size<tmpl::list_difference<TaggedVariant<OtherTags...>,
     423             :                                               TaggedVariant<Tags...>>>::value ==
     424             :              0>>
     425             : constexpr TaggedVariant<Tags...>::TaggedVariant(
     426             :     TaggedVariant<OtherTags...>&& other)
     427             :     : TaggedVariant(visit(
     428             :           []<typename Tag>(
     429             :               std::pair<tmpl::type_<Tag>, typename Tag::type&&> entry) {
     430             :             return TaggedVariant(std::in_place_type<Tag>,
     431             :                                  std::move(entry.second));
     432             :           },
     433             :           std::move(other))) {}
     434             : 
     435             : template <typename... Tags>
     436             : template <
     437             :     typename... OtherTags,
     438             :     Requires<tmpl::size<tmpl::list_difference<TaggedVariant<OtherTags...>,
     439             :                                               TaggedVariant<Tags...>>>::value ==
     440             :              0>>
     441           0 : constexpr TaggedVariant<Tags...>& TaggedVariant<Tags...>::operator=(
     442             :     TaggedVariant<OtherTags...>&& other) {
     443             :   visit(
     444             :       [&]<typename Tag>(
     445             :           std::pair<tmpl::type_<Tag>, typename Tag::type&&> entry) {
     446             :         emplace<Tag>(std::move(entry.second));
     447             :       },
     448             :       std::move(other));
     449             :   return *this;
     450             : }
     451             : }  // namespace variants
     452             : 
     453             : namespace std {
     454             : template <typename... Tags>
     455             : // https://github.com/llvm/llvm-project/issues/45454
     456             : // NOLINTNEXTLINE(cert-dcl58-cpp)
     457             : struct hash<::variants::TaggedVariant<Tags...>> {
     458             :   size_t operator()(const ::variants::TaggedVariant<Tags...>& variant) const {
     459             :     return std::hash<decltype(variant.data_)>{}(variant.data_);
     460             :   }
     461             : };
     462             : }  // namespace std

Generated by: LCOV version 1.14