SpECTRE Documentation Coverage Report
Current view: top level - DataStructures - TaggedVariant.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 26 49 53.1 %
Date: 2025-12-05 05:03:31
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             :       : TaggedVariant(visit(
     115             :             []<typename Tag>(
     116             :                 std::pair<tmpl::type_<Tag>, typename Tag::type&&> entry) {
     117             :               return TaggedVariant(std::in_place_type<Tag>,
     118             :                                    std::move(entry.second));
     119             :             },
     120             :             std::move(other))) {}
     121             : 
     122             :   template <typename... OtherTags,
     123             :             Requires<tmpl::size<tmpl::list_difference<
     124             :                          TaggedVariant<OtherTags...>, TaggedVariant>>::value ==
     125             :                      0> = nullptr>
     126           1 :   constexpr TaggedVariant& operator=(TaggedVariant<OtherTags...>&& other) {
     127             :     visit(
     128             :         [&]<typename Tag>(
     129             :             std::pair<tmpl::type_<Tag>, typename Tag::type&&> entry) {
     130             :           emplace<Tag>(std::move(entry.second));
     131             :         },
     132             :         std::move(other));
     133             :     return *this;
     134             :   }
     135             :   /// @}
     136             : 
     137             :   /// The index into the `Tags...` of the active object.
     138           1 :   constexpr size_t index() const { return data_.index(); }
     139             : 
     140             :   /// See `std::variant::valueless_by_exception`.
     141           1 :   constexpr bool valueless_by_exception() const {
     142             :     return data_.valueless_by_exception();
     143             :   }
     144             : 
     145             :   /// Destroys the contained object and actives \p Tag, constructing a
     146             :   /// new value from \p args.
     147             :   ///
     148             :   /// \snippet DataStructures/Test_TaggedVariant.cpp emplace
     149             :   template <
     150             :       typename Tag, typename... Args,
     151             :       Requires<(... or std::is_same_v<Tag, Tags>) and
     152             :                std::is_constructible_v<typename Tag::type, Args...>> = nullptr>
     153           1 :   constexpr typename Tag::type& emplace(Args&&... args) {
     154             :     return data_.template emplace<data_index<Tag>>(std::forward<Args>(args)...);
     155             :   }
     156             : 
     157           0 :   constexpr void swap(TaggedVariant& other) noexcept(noexcept(
     158             :       (... and (std::is_nothrow_move_constructible_v<typename Tags::type> and
     159             :                 std::is_nothrow_swappable_v<typename Tags::type>)))) {
     160             :     data_.swap(other.data_);
     161             :   }
     162             : 
     163           0 :   void pup(PUP::er& p) { p | data_; }
     164             : 
     165             :   /// A TaggedVariant over option tags can be parsed as any of them.
     166             :   /// @{
     167           1 :   static constexpr Options::String help = "One of multiple options";
     168           1 :   using options = tmpl::list<Options::Alternatives<tmpl::list<Tags>...>>;
     169             :   template <typename Tag>
     170           1 :   explicit TaggedVariant(tmpl::list<Tag> /*meta*/, typename Tag::type value)
     171             :       : TaggedVariant(std::in_place_type<Tag>, std::move(value)) {}
     172             :   /// @}
     173             : 
     174             :  private:
     175             :   template <typename R, typename Variant, typename Tag, typename Visitor>
     176             :   friend struct TaggedVariant_detail::VisitAlternative;
     177             : 
     178             :   template <typename Tag, typename... Tags2>
     179           0 :   friend constexpr typename Tag::type& get(TaggedVariant<Tags2...>& variant);
     180             :   template <typename Tag, typename... Tags2>
     181           0 :   friend constexpr const typename Tag::type& get(
     182             :       const TaggedVariant<Tags2...>& variant);
     183             :   template <typename Tag, typename... Tags2>
     184           0 :   friend constexpr typename Tag::type&& get(TaggedVariant<Tags2...>&& variant);
     185             :   template <typename Tag, typename... Tags2>
     186           0 :   friend constexpr const typename Tag::type&& get(
     187             :       const TaggedVariant<Tags2...>&& variant);
     188             : 
     189           0 :   friend constexpr bool operator== <Tags...>(const TaggedVariant<Tags...>& a,
     190             :                                              const TaggedVariant<Tags...>& b);
     191           0 :   friend constexpr bool operator< <Tags...>(const TaggedVariant<Tags...>& a,
     192             :                                             const TaggedVariant<Tags...>& b);
     193             : 
     194             :   friend struct std::hash<TaggedVariant>;
     195             : 
     196           0 :   std::variant<typename Tags::type...> data_;
     197             : };
     198             : 
     199             : namespace TaggedVariant_detail {
     200             : template <typename... Tags>
     201             : constexpr bool is_variant_or_derived(const TaggedVariant<Tags...>* /*meta*/) {
     202             :   return true;
     203             : }
     204             : // NOLINTNEXTLINE(cert-dcl50-cpp) - variadic function
     205             : constexpr bool is_variant_or_derived(...) { return false; }
     206             : 
     207             : template <typename Tag, typename Value>
     208             : constexpr std::pair<tmpl::type_<Tag>, Value&&> make_visitor_pair(
     209             :     Value&& value) {
     210             :   return {tmpl::type_<Tag>{}, std::forward<Value>(value)};
     211             : }
     212             : 
     213             : struct DeduceReturn;
     214             : 
     215             : template <typename R, typename Variant, typename Tag, typename Visitor>
     216             : struct VisitAlternative {
     217             :   static constexpr R apply(Variant&& variant, const Visitor& visitor) {
     218             :     return std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(
     219             :         get<std::decay_t<Variant>::template data_index<Tag>>(
     220             :             std::forward<Variant>(variant).data_)));
     221             :   }
     222             : };
     223             : 
     224             : template <typename Variant, typename Tag, typename Visitor>
     225             : struct VisitAlternative<void, Variant, Tag, Visitor> {
     226             :   static constexpr void apply(Variant&& variant, const Visitor& visitor) {
     227             :     std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(
     228             :         get<std::decay_t<Variant>::template data_index<Tag>>(
     229             :             std::forward<Variant>(variant).data_)));
     230             :   }
     231             : };
     232             : 
     233             : template <typename Variant, typename Tag, typename Visitor>
     234             : struct VisitAlternative<DeduceReturn, Variant, Tag, Visitor> {
     235             :   static constexpr decltype(auto) apply(Variant&& variant,
     236             :                                         const Visitor& visitor) {
     237             :     return std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(
     238             :         get<std::decay_t<Variant>::template data_index<Tag>>(
     239             :             std::forward<Variant>(variant).data_)));
     240             :   }
     241             : };
     242             : 
     243             : template <typename... Tags>
     244             : constexpr TaggedVariant<Tags...>& as_variant(TaggedVariant<Tags...>& variant) {
     245             :   return variant;
     246             : }
     247             : template <typename... Tags>
     248             : constexpr const TaggedVariant<Tags...>& as_variant(
     249             :     const TaggedVariant<Tags...>& variant) {
     250             :   return variant;
     251             : }
     252             : template <typename... Tags>
     253             : constexpr TaggedVariant<Tags...>&& as_variant(
     254             :     TaggedVariant<Tags...>&& variant) {
     255             :   return std::move(variant);
     256             : }
     257             : template <typename... Tags>
     258             : constexpr const TaggedVariant<Tags...>&& as_variant(
     259             :     const TaggedVariant<Tags...>&& variant) {
     260             :   return std::move(variant);
     261             : }
     262             : 
     263             : template <typename R, typename Visitor, typename... Variants>
     264             : constexpr decltype(auto) visit_impl(Visitor&& visitor) {
     265             :   return std::forward<Visitor>(visitor)();
     266             : }
     267             : 
     268             : template <typename R, typename Variant, typename Visitor,
     269             :           typename DecayedVariant = std::decay_t<Variant>>
     270             : struct VisitJumpTable;
     271             : 
     272             : template <typename R, typename Variant, typename Visitor, typename... Tags>
     273             : struct VisitJumpTable<R, Variant, Visitor, TaggedVariant<Tags...>> {
     274             :   static constexpr std::array value{
     275             :       VisitAlternative<R, Variant, Tags, Visitor>::apply...};
     276             : };
     277             : 
     278             : template <typename R, typename Visitor, typename FirstVariant,
     279             :           typename... Variants>
     280             : constexpr decltype(auto) visit_impl(Visitor&& visitor,
     281             :                                     FirstVariant&& first_variant,
     282             :                                     Variants&&... variants) {
     283             :   const auto recurse = [&]<typename Arg>(Arg&& first_arg) {
     284             :     return visit_impl<R>(
     285             :         [&]<typename... Rest>(Rest&&... rest) {
     286             :           return std::forward<Visitor>(visitor)(std::forward<Arg>(first_arg),
     287             :                                                 std::forward<Rest>(rest)...);
     288             :         },
     289             :         std::forward<Variants>(variants)...);
     290             :   };
     291             :   if (UNLIKELY(first_variant.valueless_by_exception())) {
     292             :     throw std::bad_variant_access{};
     293             :   }
     294             :   return gsl::at(VisitJumpTable<R, FirstVariant, decltype(recurse)>::value,
     295             :                  first_variant.index())(
     296             :       std::forward<FirstVariant>(first_variant), recurse);
     297             : }
     298             : }  // namespace TaggedVariant_detail
     299             : 
     300             : /// Call \p visitor with the contents of one or more variants.
     301             : ///
     302             : /// Calls \p visitor with the contents of each variant as arguments,
     303             : /// passed as `std::pair<tmpl::type_<Tag>, typename Tag::type ref>`,
     304             : /// where `Tag` is the active tag of the variant and `ref` is a
     305             : /// reference qualifier matching that of the passed variant.
     306             : ///
     307             : /// If the template parameter \p R is supplied, the result is
     308             : /// implicitly converted to that type (which may be `void`).
     309             : /// Otherwise it is deduced from the return type of \p visitor, which
     310             : /// must be the same for all tags in the variant.
     311             : ///
     312             : /// \warning Unlike `visit` for `std::variant`, the types of the
     313             : /// visitor arguments do not allow for implicit conversions between
     314             : /// reference types.  If the visitor expects, for example,
     315             : /// `std::pair<tmpl::type_<Tag>, const typename Tag::type&>`, the caller must
     316             : /// ensure that the passed variant is a const lvalue.
     317             : ///
     318             : /// \snippet DataStructures/Test_TaggedVariant.cpp visit
     319             : /// @{
     320             : template <typename Visitor, typename... Variants,
     321             :           Requires<(... and TaggedVariant_detail::is_variant_or_derived(
     322             :                                 std::add_pointer_t<std::remove_reference_t<
     323             :                                     Variants>>{}))> = nullptr>
     324           1 : constexpr decltype(auto) visit(Visitor&& visitor, Variants&&... variants) {
     325             :   return TaggedVariant_detail::visit_impl<TaggedVariant_detail::DeduceReturn>(
     326             :       visitor,
     327             :       TaggedVariant_detail::as_variant(std::forward<Variants>(variants))...);
     328             : }
     329             : 
     330             : template <typename R, typename Visitor, typename... Variants,
     331             :           Requires<(... and TaggedVariant_detail::is_variant_or_derived(
     332             :                                 std::add_pointer_t<std::remove_reference_t<
     333             :                                     Variants>>{}))> = nullptr>
     334           1 : constexpr R visit(Visitor&& visitor, Variants&&... variants) {
     335             :   return TaggedVariant_detail::visit_impl<R>(
     336             :       visitor,
     337             :       TaggedVariant_detail::as_variant(std::forward<Variants>(variants))...);
     338             : }
     339             : /// @}
     340             : 
     341             : /// Check whether \p Tag is active.
     342             : template <typename Tag, typename... Tags>
     343           1 : constexpr bool holds_alternative(const TaggedVariant<Tags...>& variant) {
     344             :   return variant.index() == tmpl::index_of<TaggedVariant<Tags...>, Tag>::value;
     345             : }
     346             : 
     347             : /// Access the contained object.  Throws `std::bad_variant_access` if
     348             : /// \p Tag is not active.
     349             : /// @{
     350             : template <typename Tag, typename... Tags>
     351           1 : constexpr typename Tag::type& get(TaggedVariant<Tags...>& variant) {
     352             :   return get<TaggedVariant<Tags...>::template data_index<Tag>>(variant.data_);
     353             : }
     354             : template <typename Tag, typename... Tags>
     355           1 : constexpr const typename Tag::type& get(const TaggedVariant<Tags...>& variant) {
     356             :   return get<TaggedVariant<Tags...>::template data_index<Tag>>(variant.data_);
     357             : }
     358             : template <typename Tag, typename... Tags>
     359           1 : constexpr typename Tag::type&& get(TaggedVariant<Tags...>&& variant) {
     360             :   return get<TaggedVariant<Tags...>::template data_index<Tag>>(
     361             :       std::move(variant.data_));
     362             : }
     363             : template <typename Tag, typename... Tags>
     364           1 : constexpr const typename Tag::type&& get(
     365             :     const TaggedVariant<Tags...>&& variant) {
     366             :   return get<TaggedVariant<Tags...>::template data_index<Tag>>(
     367             :       std::move(variant.data_));
     368             : }
     369             : /// @}
     370             : 
     371             : /// Returns a pointer to the contained object if \p variant is a
     372             : /// non-null pointer and \p Tag is active.  Otherwise, returns
     373             : /// `nullptr`.
     374             : /// @{
     375             : template <typename Tag, typename... Tags>
     376           1 : constexpr const typename Tag::type* get_if(
     377             :     const TaggedVariant<Tags...>* variant) {
     378             :   if (variant != nullptr and holds_alternative<Tag>(*variant)) {
     379             :     return &get<Tag>(*variant);
     380             :   } else {
     381             :     return nullptr;
     382             :   }
     383             : }
     384             : template <typename Tag, typename... Tags>
     385           1 : constexpr typename Tag::type* get_if(TaggedVariant<Tags...>* variant) {
     386             :   if (variant != nullptr and holds_alternative<Tag>(*variant)) {
     387             :     return &get<Tag>(*variant);
     388             :   } else {
     389             :     return nullptr;
     390             :   }
     391             : }
     392             : /// @}
     393             : 
     394             : template <typename... Tags>
     395           0 : constexpr bool operator==(const TaggedVariant<Tags...>& a,
     396             :                           const TaggedVariant<Tags...>& b) {
     397             :   return a.data_ == b.data_;
     398             : }
     399             : template <typename... Tags>
     400           0 : constexpr bool operator!=(const TaggedVariant<Tags...>& a,
     401             :                           const TaggedVariant<Tags...>& b) {
     402             :   return not(a == b);
     403             : }
     404             : template <typename... Tags>
     405           0 : constexpr bool operator<(const TaggedVariant<Tags...>& a,
     406             :                          const TaggedVariant<Tags...>& b) {
     407             :   return a.data_ < b.data_;
     408             : }
     409             : template <typename... Tags>
     410           0 : constexpr bool operator>(const TaggedVariant<Tags...>& a,
     411             :                          const TaggedVariant<Tags...>& b) {
     412             :   return b < a;
     413             : }
     414             : template <typename... Tags>
     415           0 : constexpr bool operator<=(const TaggedVariant<Tags...>& a,
     416             :                           const TaggedVariant<Tags...>& b) {
     417             :   return not(b < a);
     418             : }
     419             : template <typename... Tags>
     420           0 : constexpr bool operator>=(const TaggedVariant<Tags...>& a,
     421             :                           const TaggedVariant<Tags...>& b) {
     422             :   return not(a < b);
     423             : }
     424             : 
     425             : /// Comparison operators against single-tag variants.  Primarily useful
     426             : /// in tests.
     427             : /// @{
     428             : template <typename... Tags, typename Tag,
     429             :           Requires<(sizeof...(Tags) > 1 and
     430             :                     (... or std::is_same_v<Tags, Tag>))> = nullptr>
     431           1 : constexpr bool operator==(const TaggedVariant<Tags...>& a,
     432             :                           const TaggedVariant<Tag>& b) {
     433             :   return holds_alternative<Tag>(a) and get<Tag>(a) == get<Tag>(b);
     434             : }
     435             : template <typename... Tags, typename Tag,
     436             :           Requires<(sizeof...(Tags) > 1 and
     437             :                     (... or std::is_same_v<Tags, Tag>))> = nullptr>
     438           1 : constexpr bool operator==(const TaggedVariant<Tag>& a,
     439             :                           const TaggedVariant<Tags...>& b) {
     440             :   return b == a;
     441             : }
     442             : template <typename... Tags, typename Tag,
     443             :           Requires<(sizeof...(Tags) > 1 and
     444             :                     (... or std::is_same_v<Tags, Tag>))> = nullptr>
     445           1 : constexpr bool operator!=(const TaggedVariant<Tags...>& a,
     446             :                           const TaggedVariant<Tag>& b) {
     447             :   return not(a == b);
     448             : }
     449             : template <typename... Tags, typename Tag,
     450             :           Requires<(sizeof...(Tags) > 1 and
     451             :                     (... or std::is_same_v<Tags, Tag>))> = nullptr>
     452           1 : constexpr bool operator!=(const TaggedVariant<Tag>& a,
     453             :                           const TaggedVariant<Tags...>& b) {
     454             :   return not(a == b);
     455             : }
     456             : /// @}
     457             : 
     458             : template <
     459             :     typename... Tags,
     460             :     Requires<(... and (std::is_move_constructible_v<typename Tags::type> and
     461             :                        std::is_swappable_v<typename Tags::type>))> = nullptr>
     462           0 : constexpr void swap(TaggedVariant<Tags...>& a,
     463             :                     TaggedVariant<Tags...>& b) noexcept(noexcept(a.swap(b))) {
     464             :   a.swap(b);
     465             : }
     466             : }  // namespace variants
     467             : 
     468             : namespace std {
     469             : template <typename... Tags>
     470             : // https://github.com/llvm/llvm-project/issues/45454
     471             : // NOLINTNEXTLINE(cert-dcl58-cpp)
     472             : struct hash<::variants::TaggedVariant<Tags...>> {
     473             :   size_t operator()(const ::variants::TaggedVariant<Tags...>& variant) const {
     474             :     return std::hash<decltype(variant.data_)>{}(variant.data_);
     475             :   }
     476             : };
     477             : }  // namespace std

Generated by: LCOV version 1.14