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