SpECTRE Documentation Coverage Report
Current view: top level - Options - Factory.hpp Hit Total Coverage
Commit: 35a1e98cd3e4fdea528eb8100f99c2f707894fda Lines: 1 1 100.0 %
Date: 2024-04-19 00:10:48
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 class template Factory.
       6             : 
       7             : #pragma once
       8             : 
       9             : #include <algorithm>
      10             : #include <iomanip>
      11             : #include <memory>
      12             : #include <sstream>
      13             : #include <string>
      14             : #include <utility>
      15             : #include <yaml-cpp/yaml.h>
      16             : 
      17             : #include "Options/Options.hpp"
      18             : #include "Options/ParseError.hpp"
      19             : #include "Options/Protocols/FactoryCreation.hpp"
      20             : #include "Utilities/ErrorHandling/Assert.hpp"
      21             : #include "Utilities/PrettyType.hpp"
      22             : #include "Utilities/ProtocolHelpers.hpp"
      23             : #include "Utilities/StdHelpers.hpp"
      24             : #include "Utilities/TMPL.hpp"
      25             : #include "Utilities/TypeTraits/CreateGetStaticMemberVariableOrDefault.hpp"
      26             : 
      27             : namespace Options {
      28             : namespace Factory_detail {
      29             : struct print_derived {
      30             :   // Not a stream because brigand requires the functor to be copyable.
      31             :   std::string value;
      32             :   template <typename T>
      33             :   void operator()(tmpl::type_<T> /*meta*/) {
      34             :     // These are zero-based
      35             :     const size_t name_col = 2;
      36             :     const size_t help_col = 22;
      37             :     const size_t end_col = 80;
      38             : 
      39             :     std::ostringstream ss;
      40             :     ss << std::left << std::setw(name_col) << ""
      41             :        << std::setw(help_col - name_col - 1) << pretty_type::name<T>();
      42             :     if (ss.str().size() >= help_col) {
      43             :       ss << "\n" << std::setw(help_col - 1) << "";
      44             :     }
      45             : 
      46             :     std::string help_snippet(T::help);
      47             :     if (help_snippet.size() > end_col - help_col) {
      48             :       help_snippet.resize(end_col - help_col - 3);
      49             :       help_snippet += "...";
      50             :     }
      51             :     std::replace(help_snippet.begin(), help_snippet.end(), '\n', ' ');
      52             :     ss << " " << help_snippet << "\n";
      53             : 
      54             :     value += ss.str();
      55             :   }
      56             : };
      57             : 
      58             : template <typename CreatableClasses>
      59             : std::string help_derived() {
      60             :   return "Known Ids:\n" +
      61             :          tmpl::for_each<CreatableClasses>(
      62             :              Factory_detail::print_derived{})
      63             :              .value;
      64             : }
      65             : 
      66             : // This is for handling legacy code that still uses creatable_classes.
      67             : // It should be inlined once everything is converted.
      68             : template <typename BaseClass, typename Metavariables, typename = std::void_t<>>
      69             : struct get_creatable_classes {
      70             :   using factory_creation = typename Metavariables::factory_creation;
      71             :   // This assertion is normally done in tests, but the executable
      72             :   // metavariables don't have tests, so we do it here.
      73             :   static_assert(tt::assert_conforms_to_v<factory_creation,
      74             :                                          Options::protocols::FactoryCreation>);
      75             :   using type = tmpl::at<typename factory_creation::factory_classes, BaseClass>;
      76             : };
      77             : 
      78             : template <typename BaseClass, typename Metavariables>
      79             : struct get_creatable_classes<
      80             :   BaseClass, Metavariables,
      81             :   std::void_t<typename BaseClass::creatable_classes>> {
      82             :   using type = typename BaseClass::creatable_classes;
      83             : };
      84             : 
      85             : CREATE_GET_STATIC_MEMBER_VARIABLE_OR_DEFAULT(factory_creatable)
      86             : 
      87             : template <typename T>
      88             : struct is_factory_creatable
      89             :     : std::bool_constant<get_factory_creatable_or_default_v<T, true>> {};
      90             : 
      91             : template <typename BaseClass, typename Metavariables>
      92             : std::unique_ptr<BaseClass> create(const Option& options) {
      93             :   using all_creatable_classes =
      94             :       typename get_creatable_classes<BaseClass, Metavariables>::type;
      95             :   static_assert(not std::is_same_v<all_creatable_classes, tmpl::no_such_type_>,
      96             :                 "List of creatable derived types for this class is missing "
      97             :                 "from Metavariables::factory_classes.");
      98             :   using creatable_classes =
      99             :       tmpl::filter<all_creatable_classes, is_factory_creatable<tmpl::_1>>;
     100             : 
     101             :   const auto& node = options.node();
     102             :   Option derived_opts(options.context());
     103             :   derived_opts.append_context("While operating factory for " +
     104             :                               pretty_type::name<BaseClass>());
     105             :   std::string id;
     106             :   if (node.IsScalar()) {
     107             :     id = node.as<std::string>();
     108             :   } else if (node.IsMap()) {
     109             :     if (node.size() != 1) {
     110             :       PARSE_ERROR(derived_opts.context(),
     111             :                   "Expected a single class to create, got "
     112             :                   << node.size() << ":\n" << node);
     113             :     }
     114             :     id = node.begin()->first.as<std::string>();
     115             :     derived_opts.set_node(node.begin()->second);
     116             :   } else if (node.IsNull()) {
     117             :     PARSE_ERROR(derived_opts.context(),
     118             :                 "Expected a class to create:\n"
     119             :                 << help_derived<creatable_classes>());
     120             :   } else {
     121             :     PARSE_ERROR(derived_opts.context(),
     122             :                 "Expected a class or a class with options, got:\n"
     123             :                 << node);
     124             :   }
     125             : 
     126             :   std::unique_ptr<BaseClass> result;
     127             :   tmpl::for_each<creatable_classes>(
     128             :       [&id, &derived_opts, &result](auto derived_v) {
     129             :         using Derived = tmpl::type_from<decltype(derived_v)>;
     130             :         if (pretty_type::name<Derived>() == id) {
     131             :           ASSERT(result == nullptr, "Duplicate factory id: " << id);
     132             :           result = std::make_unique<Derived>(
     133             :               derived_opts.parse_as<Derived, Metavariables>());
     134             :         }
     135             :       });
     136             :   if (result != nullptr) {
     137             :     return result;
     138             :   }
     139             :   PARSE_ERROR(derived_opts.context(),
     140             :               "Unknown Id '" << id << "'\n"
     141             :               << help_derived<creatable_classes>());
     142             : }
     143             : }  // namespace Factory_detail
     144             : 
     145             : template <typename T>
     146             : struct create_from_yaml<std::unique_ptr<T>> {
     147             :   template <typename Metavariables>
     148             :   static std::unique_ptr<T> create(const Option& options) {
     149             :     return Factory_detail::create<T, Metavariables>(options);
     150             :   }
     151             : };
     152             : }  // namespace Options

Generated by: LCOV version 1.14