SpECTRE Documentation Coverage Report
Current view: top level - DataStructures - TaggedVariant.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 53.1 % 49 26
Test Date: 2026-10-22 17:29:42
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 2.0-1