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
|