OptionsDetails.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 helpers for the Options<T> class
6 
7 #pragma once
8 
9 #include <algorithm>
10 #include <array>
11 #include <iomanip>
12 #include <list>
13 #include <map>
14 #include <sstream>
15 #include <string>
16 #include <type_traits>
17 #include <unordered_set>
18 #include <utility>
19 #include <vector>
20 
21 #include "ErrorHandling/Assert.hpp"
22 #include "Utilities/PrettyType.hpp"
23 #include "Utilities/Requires.hpp"
24 #include "Utilities/TMPL.hpp"
25 #include "Utilities/TypeTraits.hpp"
26 
27 /// Holds details of the implementation of Options
28 namespace Options_detail {
29 template <typename T, typename = cpp17::void_t<>>
30 struct name_helper {
31  static std::string name() noexcept { return pretty_type::short_name<T>(); }
32 };
33 
34 template <typename T>
35 struct name_helper<T, cpp17::void_t<decltype(T::name())>> {
36  static std::string name() noexcept { return T::name(); }
37 };
38 
39 // The name in the YAML file for a struct.
40 template <typename T>
41 std::string name() noexcept {
42  return name_helper<T>::name();
43 }
44 
45 // Display a type in a pseudo-YAML form, leaving out likely irrelevant
46 // information.
47 template <typename T>
48 struct yaml_type {
49  static std::string value() noexcept { return pretty_type::get_name<T>(); }
50 };
51 
52 template <typename T>
53 struct yaml_type<std::unique_ptr<T>> {
54  static std::string value() noexcept { return yaml_type<T>::value(); }
55 };
56 
57 template <typename T>
58 struct yaml_type<std::vector<T>> {
59  static std::string value() noexcept {
60  return "[" + yaml_type<T>::value() + ", ...]";
61  }
62 };
63 
64 template <typename T>
65 struct yaml_type<std::list<T>> {
66  static std::string value() noexcept {
67  return "[" + yaml_type<T>::value() + ", ...]";
68  }
69 };
70 
71 template <typename T, size_t N>
72 struct yaml_type<std::array<T, N>> {
73  static std::string value() noexcept {
74  return "[" + yaml_type<T>::value() + " x" + std::to_string(N) + "]";
75  }
76 };
77 
78 template <typename K, typename V, typename C>
79 struct yaml_type<std::map<K, V, C>> {
80  static std::string value() noexcept {
81  return "{" + yaml_type<K>::value() + ": " + yaml_type<V>::value() + "}"; }
82 };
83 
84 template <typename K, typename V, typename H, typename E>
85 struct yaml_type<std::unordered_map<K, V, H, E>> {
86  static std::string value() noexcept {
87  return "{" + yaml_type<K>::value() + ": " + yaml_type<V>::value() + "}"; }
88 };
89 
90 template <typename T, typename U>
91 struct yaml_type<std::pair<T, U>> {
92  static std::string value() noexcept {
93  return "[" + yaml_type<T>::value() + ", " + yaml_type<U>::value() + "]";
94  }
95 };
96 
97 template <typename S, typename = cpp17::void_t<>>
98 struct has_default : std::false_type {};
99 template <typename S>
100 struct has_default<S,
101  cpp17::void_t<decltype(std::declval<S>().default_value())>>
102  : std::true_type {};
103 
104 template <typename S, typename = cpp17::void_t<>>
105 struct has_lower_bound : std::false_type {};
106 template <typename S>
107 struct has_lower_bound<S,
108  cpp17::void_t<decltype(std::declval<S>().lower_bound())>>
109  : std::true_type {};
110 
111 template <typename S, typename = cpp17::void_t<>>
112 struct has_upper_bound : std::false_type {};
113 template <typename S>
114 struct has_upper_bound<S,
115  cpp17::void_t<decltype(std::declval<S>().upper_bound())>>
116  : std::true_type {};
117 
118 template <typename S, typename = cpp17::void_t<>>
119 struct has_lower_bound_on_size : std::false_type {};
120 template <typename S>
121 struct has_lower_bound_on_size<
122  S, cpp17::void_t<decltype(std::declval<S>().lower_bound_on_size())>>
123  : std::true_type {};
124 
125 template <typename S, typename = cpp17::void_t<>>
126 struct has_upper_bound_on_size : std::false_type {};
127 template <typename S>
128 struct has_upper_bound_on_size<
129  S, cpp17::void_t<decltype(std::declval<S>().upper_bound_on_size())>>
130  : std::true_type {};
131 
132 struct print {
133  explicit print(const int max_label_size) noexcept
134  : max_label_size_(max_label_size) {}
135 
136  using value_type = std::string;
137  template <typename T, Requires<has_default<T>::value> = nullptr>
138  static std::string print_default() noexcept {
140  ss << "default=" << std::boolalpha << T::default_value();
141  return ss.str();
142  }
143  template <typename T, Requires<not has_default<T>::value> = nullptr>
144  static std::string print_default() noexcept {
145  return "";
146  }
147  template <typename T, Requires<has_lower_bound<T>::value> = nullptr>
148  static std::string print_lower_bound() noexcept {
150  ss << "min=" << T::lower_bound();
151  return ss.str();
152  }
153  template <typename T, Requires<not has_lower_bound<T>::value> = nullptr>
154  static std::string print_lower_bound() noexcept {
155  return "";
156  }
157  template <typename T, Requires<has_upper_bound<T>::value> = nullptr>
158  static std::string print_upper_bound() noexcept {
160  ss << "max=" << T::upper_bound();
161  return ss.str();
162  }
163  template <typename T, Requires<not has_upper_bound<T>::value> = nullptr>
164  static std::string print_upper_bound() noexcept {
165  return "";
166  }
167  template <typename T, Requires<has_lower_bound_on_size<T>::value> = nullptr>
168  static std::string print_lower_bound_on_size() noexcept {
170  ss << "min size=" << T::lower_bound_on_size();
171  return ss.str();
172  }
173  template <typename T,
175  static std::string print_lower_bound_on_size() noexcept {
176  return "";
177  }
178  template <typename T, Requires<has_upper_bound_on_size<T>::value> = nullptr>
179  static std::string print_upper_bound_on_size() noexcept {
181  ss << "max size=" << T::upper_bound_on_size();
182  return ss.str();
183  }
184  template <typename T,
186  static std::string print_upper_bound_on_size() noexcept {
187  return "";
188  }
189 
190  template <typename T>
191  void operator()(tmpl::type_<T> /*meta*/) noexcept {
193  ss << " " << std::setw(max_label_size_) << std::left << name<T>()
194  << yaml_type<typename T::type>::value();
195  std::string limits;
196  for (const auto& limit : {
197  print_default<T>(),
198  print_lower_bound<T>(),
199  print_upper_bound<T>(),
200  print_lower_bound_on_size<T>(),
201  print_upper_bound_on_size<T>() }) {
202  if (not limits.empty() and not limit.empty()) {
203  limits += ", ";
204  }
205  limits += limit;
206  }
207  if (not limits.empty()) {
208  ss << " [" << limits << "]";
209  }
210  ss << "\n" << std::setw(max_label_size_ + 2) << "" << T::help << "\n\n";
211  value += ss.str();
212  }
213 
214  value_type value{};
215 
216  private:
217  const int max_label_size_;
218 };
219 
220 // TMP function to create an unordered_set of option names.
221 struct create_valid_names {
222  using value_type = std::unordered_set<std::string>;
223  value_type value{};
224  template <typename T>
225  void operator()(tmpl::type_<T> /*meta*/) noexcept {
226  const std::string label = name<T>();
227  ASSERT(0 == value.count(label), "Duplicate option name: " << label);
228  value.insert(label);
229  }
230 };
231 
232 template <typename T, typename Metavariables>
233 struct CreateWrapper {
234  using metavariables = Metavariables;
235  T data;
236 };
237 #define CREATE_WRAPPER_FORWARD_OP(op) \
238  template <typename T, typename Metavariables> \
239  bool operator op(const CreateWrapper<T, Metavariables>& a, \
240  const CreateWrapper<T, Metavariables>& b) { \
241  return a.data op b.data; \
242  }
243 CREATE_WRAPPER_FORWARD_OP(==)
244 CREATE_WRAPPER_FORWARD_OP(!=)
245 CREATE_WRAPPER_FORWARD_OP(<)
246 CREATE_WRAPPER_FORWARD_OP(>)
247 CREATE_WRAPPER_FORWARD_OP(<=)
248 CREATE_WRAPPER_FORWARD_OP(>=)
249 #undef CREATE_WRAPPER_FORWARD_OP
250 
251 template <typename T, typename = std::nullptr_t>
252 struct wrap_create_types_impl;
253 
254 template <typename T, typename Metavariables>
255 using wrap_create_types =
256  typename wrap_create_types_impl<T>::template wrapped_type<Metavariables>;
257 
258 template <typename T>
259 auto unwrap_create_types(T wrapped) {
260  return wrap_create_types_impl<T>::unwrap(std::move(wrapped));
261 }
262 
263 template <typename T, typename>
264 struct wrap_create_types_impl {
265  template <typename Metavariables>
266  using wrapped_type = CreateWrapper<T, Metavariables>;
267 };
268 
269 template <typename T, typename Metavariables>
270 struct wrap_create_types_impl<CreateWrapper<T, Metavariables>> {
271  // Never actually used, but instantiated during unwrapping
272  template <typename /*Metavars*/>
273  using wrapped_type = void;
274 
275  static T unwrap(CreateWrapper<T, Metavariables> wrapped) {
276  return std::move(wrapped.data);
277  }
278 };
279 
280 template <typename T>
281 struct wrap_create_types_impl<T, Requires<std::is_fundamental<T>::value>> {
282  template <typename Metavariables>
283  using wrapped_type = T;
284 
285  static T unwrap(T wrapped) { return wrapped; }
286 };
287 
288 // Classes convertible by yaml-cpp
289 template <>
290 struct wrap_create_types_impl<std::string> {
291  template <typename Metavariables>
292  using wrapped_type = std::string;
293 
294  static std::string unwrap(std::string wrapped) { return wrapped; }
295 };
296 
297 template <typename K, typename V>
298 struct wrap_create_types_impl<std::map<K, V>> {
299  template <typename Metavariables>
301  wrap_create_types<V, Metavariables>>;
302 
303  static auto unwrap(std::map<K, V> wrapped) {
304  using UnwrappedK = decltype(unwrap_create_types<K>(std::declval<K>()));
305  using UnwrappedV = decltype(unwrap_create_types<V>(std::declval<V>()));
307  for (auto& w : wrapped) {
308  result.emplace(unwrap_create_types<K>(std::move(w.first)),
309  unwrap_create_types<V>(std::move(w.second)));
310  }
311  return result;
312  }
313 };
314 
315 template <typename T>
316 struct wrap_create_types_impl<std::vector<T>> {
317  template <typename Metavariables>
319 
320  static auto unwrap(std::vector<T> wrapped) {
321  using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));
323  result.reserve(wrapped.size());
324  for (auto& w : wrapped) {
325  result.push_back(unwrap_create_types<T>(std::move(w)));
326  }
327  return result;
328  }
329 };
330 
331 template <typename T>
332 struct wrap_create_types_impl<std::list<T>> {
333  template <typename Metavariables>
334  using wrapped_type = std::list<wrap_create_types<T, Metavariables>>;
335 
336  static auto unwrap(std::list<T> wrapped) {
337  using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));
338  std::list<UnwrappedT> result;
339  for (auto& w : wrapped) {
340  result.push_back(unwrap_create_types<T>(std::move(w)));
341  }
342  return result;
343  }
344 };
345 
346 template <typename T, size_t N>
347 struct wrap_create_types_impl<std::array<T, N>> {
348  template <typename Metavariables>
349  using wrapped_type = std::array<wrap_create_types<T, Metavariables>, N>;
350 
351  static auto unwrap(std::array<T, N> wrapped) {
352  return unwrap_helper(std::move(wrapped), std::make_index_sequence<N>{});
353  }
354 
355  template <size_t... Is>
356  static auto unwrap_helper(
357  std::array<T, N> wrapped,
359  using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));
360  static_cast<void>(wrapped); // Work around broken GCC warning
362  {unwrap_create_types<T>(std::move(wrapped[Is]))...}};
363  }
364 };
365 
366 template <typename T, typename U>
367 struct wrap_create_types_impl<std::pair<T, U>> {
368  template <typename Metavariables>
370  wrap_create_types<U, Metavariables>>;
371 
372  static auto unwrap(std::pair<T, U> wrapped) {
373  using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));
374  using UnwrappedU = decltype(unwrap_create_types<U>(std::declval<U>()));
376  unwrap_create_types<T>(std::move(wrapped.first)),
377  unwrap_create_types<U>(std::move(wrapped.second)));
378  }
379 };
380 } // namespace Options_detail
void void_t
Given a set of types, returns void
Definition: TypeTraits.hpp:214
#define ASSERT(a, m)
Assert that an expression should be true.
Definition: Assert.hpp:49
Defines the type alias Requires.
T data(T... args)
Defines macro ASSERT.
Contains a pretty_type library to write types in a "pretty" format.
Wraps the template metaprogramming library used (brigand)
Holds details of the implementation of Options.
Definition: Options.hpp:81
typename Requires_detail::requires_impl< B >::template_error_type_failed_to_meet_requirements_on_template_parameters Requires
Express requirements on the template parameters of a function or class, replaces std::enable_if_t ...
Definition: Requires.hpp:67
C++ STL code present in C++17.
Definition: Array.hpp:16
Definition: SolvePoissonProblem.hpp:38
Defines type traits, some of which are future STL type_traits header.