SpECTRE Documentation Coverage Report
Current view: top level - Options - OptionsDetails.hpp Hit Total Coverage
Commit: 1d64892952139ddfbae7cdbdcd87dce73af8d9fe Lines: 1 2 50.0 %
Date: 2025-12-12 21:49:13
Legend: Lines: hit not hit

          Line data    Source code
       1           1 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : /// \file
       5             : /// Defines helpers for the Parser<T> class
       6             : 
       7             : #pragma once
       8             : 
       9             : #include <algorithm>
      10             : #include <array>
      11             : #include <iomanip>
      12             : #include <list>
      13             : #include <map>
      14             : #include <memory>
      15             : #include <sstream>
      16             : #include <string>
      17             : #include <type_traits>
      18             : #include <unordered_set>
      19             : #include <utility>
      20             : #include <variant>
      21             : #include <vector>
      22             : 
      23             : #include "Options/Options.hpp"
      24             : #include "Utilities/CallWithDynamicType.hpp"
      25             : #include "Utilities/MakeString.hpp"
      26             : #include "Utilities/PrettyType.hpp"
      27             : #include "Utilities/Requires.hpp"
      28             : #include "Utilities/TMPL.hpp"
      29             : #include "Utilities/TypeTraits.hpp"
      30             : #include "Utilities/TypeTraits/IsA.hpp"
      31             : #include "Utilities/TypeTraits/IsInteger.hpp"
      32             : #include "Utilities/WrapText.hpp"
      33             : 
      34             : namespace Options::Options_detail {
      35             : 
      36             : template <typename Tag>
      37             : struct flatten_alternatives {
      38             :   using type = Tag;
      39             : };
      40             : 
      41             : template <typename... Tags>
      42             : struct flatten_alternatives<tmpl::list<Tags...>> {
      43             :   using type =
      44             :       tmpl::flatten<tmpl::list<typename flatten_alternatives<Tags>::type...>>;
      45             : };
      46             : 
      47             : template <typename... Tags>
      48             : struct flatten_alternatives<Options::Alternatives<Tags...>> {
      49             :   using type = tmpl::list<typename flatten_alternatives<Tags>::type...>;
      50             : };
      51             : 
      52             : // Traverses the group hierarchy of `Tag`, returning the topmost group that is
      53             : // a subgroup of `Root`. Directly returns `Tag` if it has no group.
      54             : // This means that the returned type is always the direct child node of `Root`
      55             : // that contains `Tag` in its hierarchy.
      56             : // If `Root` is not in the group hierarchy of `Tag`, this function returns the
      57             : // topmost group of `Tag` (meaning that `Root` is treated as the root of its
      58             : // group hierarchy).
      59             : template <typename Tag, typename Root, typename = std::void_t<>>
      60             : struct find_subgroup {
      61             :   using type = Tag;
      62             : };
      63             : 
      64             : template <typename Tag>
      65             : struct find_subgroup<Tag, typename Tag::group,
      66             :                      std::void_t<typename Tag::group>> {
      67             :   using type = Tag;
      68             : };
      69             : 
      70             : template <typename Tag, typename Root>
      71             : struct find_subgroup<Tag, Root, std::void_t<typename Tag::group>> {
      72             :   using type = typename find_subgroup<typename Tag::group, Root>::type;
      73             : };
      74             : 
      75             : /// Checks if `Tag` is within the group hierarchy of `Group`.
      76             : template <typename Tag, typename Group, typename = std::void_t<>>
      77             : struct is_in_group : std::false_type {};
      78             : 
      79             : template <typename Tag>
      80             : struct is_in_group<Tag, typename Tag::group, std::void_t<typename Tag::group>>
      81             :     : std::true_type {};
      82             : 
      83             : template <typename Tag, typename Group>
      84             : struct is_in_group<Tag, Group, std::void_t<typename Tag::group>>
      85             :     : is_in_group<typename Tag::group, Group> {};
      86             : 
      87             : /// The subset of tags in `OptionList` that are in the hierarchy of `Group`
      88             : template <typename OptionList, typename Group>
      89             : using options_in_group =
      90             :     tmpl::filter<OptionList, is_in_group<tmpl::_1, tmpl::pin<Group>>>;
      91             : 
      92             : // Display a type in a pseudo-YAML form, leaving out likely irrelevant
      93             : // information.
      94             : template <typename T>
      95             : struct yaml_type {
      96             :   static std::string value() { return pretty_type::name<T>(); }
      97             : };
      98             : 
      99             : template <typename T>
     100             : struct yaml_type<std::unique_ptr<T>> {
     101             :   static std::string value() { return yaml_type<T>::value(); }
     102             : };
     103             : 
     104             : template <typename T>
     105             : struct yaml_type<std::vector<T>> {
     106             :   static std::string value() { return "[" + yaml_type<T>::value() + ", ...]"; }
     107             : };
     108             : 
     109             : template <typename T>
     110             : struct yaml_type<std::list<T>> {
     111             :   static std::string value() { return "[" + yaml_type<T>::value() + ", ...]"; }
     112             : };
     113             : 
     114             : template <typename T, size_t N>
     115             : struct yaml_type<std::array<T, N>> {
     116             :   static std::string value() {
     117             :     return "[" + yaml_type<T>::value() + " x" + std::to_string(N) + "]";
     118             :   }
     119             : };
     120             : 
     121             : template <typename K, typename V, typename C>
     122             : struct yaml_type<std::map<K, V, C>> {
     123             :   static std::string value() {
     124             :     return "{" + yaml_type<K>::value() + ": " + yaml_type<V>::value() + "}";
     125             :   }
     126             : };
     127             : 
     128             : template <typename K, typename V, typename H, typename E>
     129             : struct yaml_type<std::unordered_map<K, V, H, E>> {
     130             :   static std::string value() {
     131             :     return "{" + yaml_type<K>::value() + ": " + yaml_type<V>::value() + "}";
     132             :   }
     133             : };
     134             : 
     135             : template <typename T, typename U>
     136             : struct yaml_type<std::pair<T, U>> {
     137             :   static std::string value() {
     138             :     return "[" + yaml_type<T>::value() + ", " + yaml_type<U>::value() + "]";
     139             :   }
     140             : };
     141             : 
     142             : template <typename... T>
     143             : struct yaml_type<std::variant<T...>> {
     144             :   static std::string value() {
     145             :     bool first = true;
     146             :     std::string result;
     147             :     const auto add_type = [&first, &result](auto alternative) {
     148             :       if (not first) {
     149             :         result += " or ";
     150             :       }
     151             :       first = false;
     152             :       result += yaml_type<tmpl::type_from<decltype(alternative)>>::value();
     153             :     };
     154             :     EXPAND_PACK_LEFT_TO_RIGHT(add_type(tmpl::type_<T>{}));
     155             :     return result;
     156             :   }
     157             : };
     158             : 
     159             : template <typename S, typename = std::void_t<>>
     160             : struct has_suggested : std::false_type {};
     161             : template <typename S>
     162             : struct has_suggested<
     163             :     S, std::void_t<decltype(std::declval<S>().suggested_value())>>
     164             :     : std::true_type {};
     165             : 
     166             : template <typename S, typename = std::void_t<>>
     167             : struct has_lower_bound : std::false_type {};
     168             : template <typename S>
     169             : struct has_lower_bound<S,
     170             :                        std::void_t<decltype(std::declval<S>().lower_bound())>>
     171             :     : std::true_type {};
     172             : 
     173             : template <typename S, typename = std::void_t<>>
     174             : struct has_upper_bound : std::false_type {};
     175             : template <typename S>
     176             : struct has_upper_bound<S,
     177             :                        std::void_t<decltype(std::declval<S>().upper_bound())>>
     178             :     : std::true_type {};
     179             : 
     180             : template <typename S, typename = std::void_t<>>
     181             : struct has_lower_bound_on_size : std::false_type {};
     182             : template <typename S>
     183             : struct has_lower_bound_on_size<
     184             :     S, std::void_t<decltype(std::declval<S>().lower_bound_on_size())>>
     185             :     : std::true_type {};
     186             : 
     187             : template <typename S, typename = std::void_t<>>
     188             : struct has_upper_bound_on_size : std::false_type {};
     189             : template <typename S>
     190             : struct has_upper_bound_on_size<
     191             :     S, std::void_t<decltype(std::declval<S>().upper_bound_on_size())>>
     192             :     : std::true_type {};
     193             : 
     194             : template <typename Tag>
     195             : std::string print_tag(const std::string& indent) {
     196             :   const std::string new_line = "\n" + indent + "  ";
     197             :   std::ostringstream ss;
     198             :   ss << indent << pretty_type::name<Tag>() << ":" << new_line
     199             :      << "type=" << yaml_type<typename Tag::type>::value();
     200             :   if constexpr (has_suggested<Tag>::value) {
     201             :     if constexpr (tt::is_a_v<std::unique_ptr, typename Tag::type>) {
     202             :       call_with_dynamic_type<
     203             :           void, typename Tag::type::element_type::creatable_classes>(
     204             :           Tag::suggested_value().get(), [&new_line, &ss](const auto* derived) {
     205             :             ss << new_line << "suggested=" << std::boolalpha
     206             :                << pretty_type::short_name<decltype(*derived)>();
     207             :           });
     208             :     } else {
     209             :       ss << new_line << "suggested="
     210             :          << (MakeString{} << std::boolalpha << Tag::suggested_value());
     211             :     }
     212             :   }
     213             :   if constexpr (has_lower_bound<Tag>::value) {
     214             :     ss << new_line << "min=" << (MakeString{} << Tag::lower_bound());
     215             :   }
     216             :   if constexpr (has_upper_bound<Tag>::value) {
     217             :     ss << new_line << "max=" << (MakeString{} << Tag::upper_bound());
     218             :   }
     219             :   if constexpr (has_lower_bound_on_size<Tag>::value) {
     220             :     ss << new_line << "min size=" << Tag::lower_bound_on_size();
     221             :   }
     222             :   if constexpr (has_upper_bound_on_size<Tag>::value) {
     223             :     ss << new_line << "max size=" << Tag::upper_bound_on_size();
     224             :   }
     225             :   ss << "\n" << wrap_text(Tag::help, 77, indent + "  ") << "\n\n";
     226             :   return ss.str();
     227             : }
     228             : 
     229             : template <typename OptionList, typename TagsAndSubgroups>
     230             : struct print;
     231             : 
     232             : template <typename Tag, typename OptionList>
     233             : struct print_impl {
     234             :   static std::string apply(const std::string& indent) {
     235             :     if constexpr (tmpl::list_contains_v<OptionList, Tag>) {
     236             :       return print_tag<Tag>(indent);
     237             :     } else {
     238             :       // A group
     239             :       std::ostringstream ss;
     240             :       ss << indent << pretty_type::name<Tag>() << ":\n"
     241             :          << wrap_text(Tag::help, 77, indent + "  ") << "\n\n";
     242             :       return ss.str();
     243             :     }
     244             :   }
     245             : };
     246             : 
     247             : template <typename... Alternatives>
     248             : std::string print_alternatives(const std::string& header,
     249             :                                const std::string& indent) {
     250             :   return (
     251             :       indent + header + "\n" + ... +
     252             :       (print<tmpl::list<Alternatives...>, Alternatives>::apply(indent + "  ")));
     253             : }
     254             : 
     255             : template <typename FirstAlternative, typename... OtherAlternatives,
     256             :           typename OptionList>
     257             : struct print_impl<Alternatives<FirstAlternative, OtherAlternatives...>,
     258             :                   OptionList> {
     259             :   static std::string apply(const std::string& indent) {
     260             :     return print_alternatives<FirstAlternative>("EITHER", indent) +
     261             :            print_alternatives<OtherAlternatives...>("OR", indent);
     262             :   }
     263             : };
     264             : 
     265             : template <typename OptionList, typename... TagsAndSubgroups>
     266             : struct print<OptionList, tmpl::list<TagsAndSubgroups...>> {
     267             :   static std::string apply(const std::string& indent) {
     268             :     return ("" + ... +
     269             :             (print_impl<TagsAndSubgroups, OptionList>::apply(indent)));
     270             :   }
     271             : };
     272             : 
     273             : template <typename T, typename Metavariables>
     274             : struct CreateWrapper {
     275             :   using metavariables = Metavariables;
     276             :   T data{};
     277             : };
     278           0 : #define CREATE_WRAPPER_FORWARD_OP(op)                          \
     279             :   template <typename T, typename Metavariables>                \
     280             :   bool operator op(const CreateWrapper<T, Metavariables>& a,   \
     281             :                    const CreateWrapper<T, Metavariables>& b) { \
     282             :     return a.data op b.data;                                   \
     283             :   }
     284             : CREATE_WRAPPER_FORWARD_OP(==)
     285             : CREATE_WRAPPER_FORWARD_OP(!=)
     286             : CREATE_WRAPPER_FORWARD_OP(<)
     287             : CREATE_WRAPPER_FORWARD_OP(>)
     288             : CREATE_WRAPPER_FORWARD_OP(<=)
     289             : CREATE_WRAPPER_FORWARD_OP(>=)
     290             : #undef CREATE_WRAPPER_FORWARD_OP
     291             : 
     292             : template <typename T, typename = std::nullptr_t>
     293             : struct wrap_create_types_impl;
     294             : 
     295             : template <typename T, typename Metavariables>
     296             : using wrap_create_types =
     297             :     typename wrap_create_types_impl<T>::template wrapped_type<Metavariables>;
     298             : 
     299             : template <typename T>
     300             : auto unwrap_create_types(T wrapped) {
     301             :   return wrap_create_types_impl<T>::unwrap(std::move(wrapped));
     302             : }
     303             : 
     304             : template <typename T, typename>
     305             : struct wrap_create_types_impl {
     306             :   template <typename Metavariables>
     307             :   using wrapped_type = CreateWrapper<T, Metavariables>;
     308             : };
     309             : 
     310             : template <typename T, typename Metavariables>
     311             : struct wrap_create_types_impl<CreateWrapper<T, Metavariables>> {
     312             :   // Never actually used, but instantiated during unwrapping
     313             :   template <typename /*Metavars*/>
     314             :   using wrapped_type = void;
     315             : 
     316             :   static T unwrap(CreateWrapper<T, Metavariables> wrapped) {
     317             :     return std::move(wrapped.data);
     318             :   }
     319             : };
     320             : 
     321             : template <typename T>
     322             : struct wrap_create_types_impl<
     323             :     T, Requires<std::is_fundamental_v<T> and not tt::is_integer_v<T>>> {
     324             :   template <typename Metavariables>
     325             :   using wrapped_type = T;
     326             : 
     327             :   static T unwrap(T wrapped) { return wrapped; }
     328             : };
     329             : 
     330             : // Classes convertible by yaml-cpp
     331             : template <>
     332             : struct wrap_create_types_impl<std::string> {
     333             :   template <typename Metavariables>
     334             :   using wrapped_type = std::string;
     335             : 
     336             :   static std::string unwrap(std::string wrapped) { return wrapped; }
     337             : };
     338             : 
     339             : template <typename K, typename V>
     340             : struct wrap_create_types_impl<std::map<K, V>> {
     341             :   template <typename Metavariables>
     342             :   using wrapped_type = std::map<wrap_create_types<K, Metavariables>,
     343             :                                 wrap_create_types<V, Metavariables>>;
     344             : 
     345             :   static auto unwrap(std::map<K, V> wrapped) {
     346             :     using UnwrappedK = decltype(unwrap_create_types<K>(std::declval<K>()));
     347             :     using UnwrappedV = decltype(unwrap_create_types<V>(std::declval<V>()));
     348             :     std::map<UnwrappedK, UnwrappedV> result;
     349             :     for (auto it = wrapped.begin(); it != wrapped.end();) {
     350             :       auto node = wrapped.extract(it++);
     351             :       result.emplace(unwrap_create_types<K>(std::move(node.key())),
     352             :                      unwrap_create_types<V>(std::move(node.mapped())));
     353             :     }
     354             :     return result;
     355             :   }
     356             : };
     357             : 
     358             : template <typename T>
     359             : struct wrap_create_types_impl<std::vector<T>> {
     360             :   template <typename Metavariables>
     361             :   using wrapped_type = std::vector<wrap_create_types<T, Metavariables>>;
     362             : 
     363             :   static auto unwrap(std::vector<T> wrapped) {
     364             :     using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));
     365             :     std::vector<UnwrappedT> result;
     366             :     result.reserve(wrapped.size());
     367             :     for (auto& w : wrapped) {
     368             :       result.push_back(unwrap_create_types<T>(std::move(w)));
     369             :     }
     370             :     return result;
     371             :   }
     372             : };
     373             : 
     374             : template <typename T>
     375             : struct wrap_create_types_impl<std::list<T>> {
     376             :   template <typename Metavariables>
     377             :   using wrapped_type = std::list<wrap_create_types<T, Metavariables>>;
     378             : 
     379             :   static auto unwrap(std::list<T> wrapped) {
     380             :     using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));
     381             :     std::list<UnwrappedT> result;
     382             :     for (auto& w : wrapped) {
     383             :       result.push_back(unwrap_create_types<T>(std::move(w)));
     384             :     }
     385             :     return result;
     386             :   }
     387             : };
     388             : 
     389             : template <typename T, size_t N>
     390             : struct wrap_create_types_impl<std::array<T, N>> {
     391             :   template <typename Metavariables>
     392             :   using wrapped_type = std::array<wrap_create_types<T, Metavariables>, N>;
     393             : 
     394             :   static auto unwrap(std::array<T, N> wrapped) {
     395             :     return unwrap_helper(std::move(wrapped), std::make_index_sequence<N>{});
     396             :   }
     397             : 
     398             :   template <size_t... Is>
     399             :   static auto unwrap_helper(std::array<T, N> wrapped,
     400             :                             std::integer_sequence<size_t, Is...> /*meta*/) {
     401             :     using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));
     402             :     static_cast<void>(wrapped);  // Work around broken GCC warning
     403             :     return std::array<UnwrappedT, N>{
     404             :         {unwrap_create_types<T>(std::move(wrapped[Is]))...}};
     405             :   }
     406             : };
     407             : 
     408             : template <typename T, typename U>
     409             : struct wrap_create_types_impl<std::pair<T, U>> {
     410             :   template <typename Metavariables>
     411             :   using wrapped_type = std::pair<wrap_create_types<T, Metavariables>,
     412             :                                  wrap_create_types<U, Metavariables>>;
     413             : 
     414             :   static auto unwrap(std::pair<T, U> wrapped) {
     415             :     using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));
     416             :     using UnwrappedU = decltype(unwrap_create_types<U>(std::declval<U>()));
     417             :     return std::pair<UnwrappedT, UnwrappedU>(
     418             :         unwrap_create_types<T>(std::move(wrapped.first)),
     419             :         unwrap_create_types<U>(std::move(wrapped.second)));
     420             :   }
     421             : };
     422             : }  // namespace Options::Options_detail

Generated by: LCOV version 1.14