Factory.hpp
Go to the documentation of this file.
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/Protocols/FactoryCreation.hpp"
20 #include "Utilities/ProtocolHelpers.hpp"
21 #include "Utilities/StdHelpers.hpp"
22 #include "Utilities/TMPL.hpp"
23 
24 namespace Options {
25 namespace Factory_detail {
26 struct print_derived {
27  // Not a stream because brigand requires the functor to be copyable.
28  std::string value;
29  template <typename T>
30  void operator()(tmpl::type_<T> /*meta*/) noexcept {
31  // These are zero-based
32  const size_t name_col = 2;
33  const size_t help_col = 22;
34  const size_t end_col = 80;
35 
37  ss << std::left
38  << std::setw(name_col) << ""
39  << std::setw(help_col - name_col - 1) << name<T>();
40  if (ss.str().size() >= help_col) {
41  ss << "\n" << std::setw(help_col - 1) << "";
42  }
43 
44  std::string help_snippet(T::help);
45  if (help_snippet.size() > end_col - help_col) {
46  help_snippet.resize(end_col - help_col - 3);
47  help_snippet += "...";
48  }
49  std::replace(help_snippet.begin(), help_snippet.end(), '\n', ' ');
50  ss << " " << help_snippet << "\n";
51 
52  value += ss.str();
53  }
54 };
55 
56 template <typename CreatableClasses>
57 std::string help_derived() noexcept {
58  return "Known Ids:\n" +
59  tmpl::for_each<CreatableClasses>(
60  Factory_detail::print_derived{})
61  .value;
62 }
63 
64 // This is for handling legacy code that still uses creatable_classes.
65 // It should be inlined once everything is converted.
66 template <typename BaseClass, typename Metavariables, typename = std::void_t<>>
67 struct get_creatable_classes {
68  using factory_creation = typename Metavariables::factory_creation;
69  // This assertion is normally done in tests, but the executable
70  // metavariables don't have tests, so we do it here.
71  static_assert(tt::assert_conforms_to<factory_creation,
73  using type = tmpl::at<typename factory_creation::factory_classes, BaseClass>;
74 };
75 
76 template <typename BaseClass, typename Metavariables>
77 struct get_creatable_classes<
78  BaseClass, Metavariables,
79  std::void_t<typename BaseClass::creatable_classes>> {
80  using type = typename BaseClass::creatable_classes;
81 };
82 
83 template <typename BaseClass, typename Metavariables>
84 std::unique_ptr<BaseClass> create(const Option& options) {
85  using creatable_classes =
86  typename get_creatable_classes<BaseClass, Metavariables>::type;
87  static_assert(not std::is_same_v<creatable_classes, tmpl::no_such_type_>,
88  "List of creatable derived types for this class is missing "
89  "from Metavariables::factory_classes.");
90 
91  const auto& node = options.node();
92  Option derived_opts(options.context());
93  derived_opts.append_context("While operating factory for " +
94  name<BaseClass>());
95  std::string id;
96  if (node.IsScalar()) {
97  id = node.as<std::string>();
98  } else if (node.IsMap()) {
99  if (node.size() != 1) {
100  PARSE_ERROR(derived_opts.context(),
101  "Expected a single class to create, got "
102  << node.size() << ":\n" << node);
103  }
104  id = node.begin()->first.as<std::string>();
105  derived_opts.set_node(node.begin()->second);
106  } else if (node.IsNull()) {
107  PARSE_ERROR(derived_opts.context(),
108  "Expected a class to create:\n"
109  << help_derived<creatable_classes>());
110  } else {
111  PARSE_ERROR(derived_opts.context(),
112  "Expected a class or a class with options, got:\n"
113  << node);
114  }
115 
117  tmpl::for_each<creatable_classes>(
118  [&id, &derived_opts, &result](auto derived_v) {
119  using Derived = tmpl::type_from<decltype(derived_v)>;
120  if (name<Derived>() == id) {
121  ASSERT(result == nullptr, "Duplicate factory id: " << id);
122  result = std::make_unique<Derived>(
123  derived_opts.parse_as<Derived, Metavariables>());
124  }
125  });
126  if (result != nullptr) {
127  return result;
128  }
129  PARSE_ERROR(derived_opts.context(),
130  "Unknown Id '" << id << "'\n"
131  << help_derived<creatable_classes>());
132 }
133 } // namespace Factory_detail
134 
135 template <typename T>
136 struct create_from_yaml<std::unique_ptr<T>> {
137  template <typename Metavariables>
138  static std::unique_ptr<T> create(const Option& options) {
139  return Factory_detail::create<T, Metavariables>(options);
140  }
141 };
142 } // namespace Options
sstream
std::string
utility
PARSE_ERROR
#define PARSE_ERROR(context, m)
Definition: Options.hpp:71
Options.hpp
algorithm
db::create
constexpr auto create(Args &&... args)
Create a new DataBox.
Definition: DataBox.hpp:907
Options
Utilities for parsing input files.
Definition: MinmodType.hpp:8
Assert.hpp
Options::protocols::FactoryCreation
Compile-time information for factory-creation.
Definition: FactoryCreation.hpp:27
memory
std::ostringstream
ASSERT
#define ASSERT(a, m)
Assert that an expression should be true.
Definition: Assert.hpp:49
iomanip
StdHelpers.hpp
std::unique_ptr
TMPL.hpp
string