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