Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <array>
7 : #include <cstddef>
8 : #include <functional>
9 : #include <type_traits>
10 : #include <utility>
11 : #include <variant>
12 :
13 : #include "Options/String.hpp"
14 : #include "Utilities/ErrorHandling/Error.hpp"
15 : #include "Utilities/Gsl.hpp"
16 : #include "Utilities/Requires.hpp"
17 : #include "Utilities/Serialization/PupStlCpp17.hpp"
18 : #include "Utilities/TMPL.hpp"
19 :
20 : /// \cond
21 : namespace Options {
22 : template <typename... AlternativeLists>
23 : struct Alternatives;
24 : } // namespace Options
25 : namespace PUP {
26 : class er;
27 : } // namespace PUP
28 : /// \endcond
29 :
30 : /// \ingroup DataStructuresGroup
31 : ///
32 : /// TaggedVariant and related functionality.
33 1 : namespace variants {
34 : namespace TaggedVariant_detail {
35 : template <typename R, typename Variant, typename Tag, typename Visitor>
36 : struct VisitAlternative;
37 : } // namespace TaggedVariant_detail
38 :
39 : /// \cond
40 : template <typename... Tags>
41 : class TaggedVariant;
42 : template <typename... Tags>
43 : constexpr bool operator==(const TaggedVariant<Tags...>& a,
44 : const TaggedVariant<Tags...>& b);
45 : template <typename... Tags>
46 : constexpr bool operator<(const TaggedVariant<Tags...>& a,
47 : const TaggedVariant<Tags...>& b);
48 : /// \endcond
49 :
50 : /// \ingroup DataStructuresGroup
51 : ///
52 : /// A class similar to `std::variant`, but indexed by tag structs.
53 : ///
54 : /// \see variants::get, variants::get_if, variants::holds_alternative,
55 : /// variants::visit
56 : template <typename... Tags>
57 1 : class TaggedVariant {
58 : private:
59 : static_assert(sizeof...(Tags) > 0);
60 : static_assert(
61 : std::is_same_v<tmpl::remove_duplicates<TaggedVariant>, TaggedVariant>,
62 : "TaggedVariant cannot have duplicate tags.");
63 :
64 : template <typename Tag>
65 0 : static constexpr size_t data_index =
66 : tmpl::index_of<TaggedVariant, Tag>::value;
67 :
68 : public:
69 : /// A default constructed instance has the first tag active.
70 1 : TaggedVariant() = default;
71 0 : TaggedVariant(const TaggedVariant&) = default;
72 0 : TaggedVariant(TaggedVariant&&) = default;
73 0 : TaggedVariant& operator=(const TaggedVariant&) = default;
74 0 : TaggedVariant& operator=(TaggedVariant&&) = default;
75 0 : ~TaggedVariant() = default;
76 :
77 : /// Construct with \p Tag active, using \p args to construct the
78 : /// contained object.
79 : ///
80 : /// \snippet DataStructures/Test_TaggedVariant.cpp construct in_place_type
81 : template <
82 : typename Tag, typename... Args,
83 : Requires<(... or std::is_same_v<Tag, Tags>) and
84 : std::is_constructible_v<typename Tag::type, Args...>> = nullptr>
85 1 : constexpr explicit TaggedVariant(std::in_place_type_t<Tag> /*meta*/,
86 : Args&&... args)
87 : : data_(std::in_place_index<data_index<Tag>>,
88 : std::forward<Args>(args)...) {}
89 :
90 : /// Construct the contained object from \p args. Only available if
91 : /// the TaggedVariant only has one tag.
92 : ///
93 : /// \snippet DataStructures/Test_TaggedVariant.cpp construct single
94 : template <typename... Args,
95 : Requires<sizeof...(Tags) == 1 and
96 : std::is_constructible_v<
97 : typename tmpl::front<TaggedVariant>::type, Args...>> =
98 : nullptr>
99 1 : constexpr explicit TaggedVariant(Args&&... args)
100 : : TaggedVariant(std::in_place_type<tmpl::front<TaggedVariant>>,
101 : std::forward<Args>(args)...) {}
102 :
103 : /// A TaggedVariant can be implicitly move-converted to another
104 : /// variant with a superset of the tags.
105 : ///
106 : /// \snippet DataStructures/Test_TaggedVariant.cpp convert
107 : /// @{
108 : template <typename... OtherTags,
109 : Requires<tmpl::size<tmpl::list_difference<
110 : TaggedVariant<OtherTags...>, TaggedVariant>>::value ==
111 : 0> = nullptr>
112 : // NOLINTNEXTLINE(google-explicit-constructor)
113 1 : constexpr TaggedVariant(TaggedVariant<OtherTags...>&& other);
114 :
115 : template <typename... OtherTags,
116 : Requires<tmpl::size<tmpl::list_difference<
117 : TaggedVariant<OtherTags...>, TaggedVariant>>::value ==
118 : 0> = nullptr>
119 1 : constexpr TaggedVariant& operator=(TaggedVariant<OtherTags...>&& other);
120 : /// @}
121 :
122 : /// The index into the `Tags...` of the active object.
123 1 : constexpr size_t index() const { return data_.index(); }
124 :
125 : /// See `std::variant::valueless_by_exception`.
126 1 : constexpr bool valueless_by_exception() const {
127 : return data_.valueless_by_exception();
128 : }
129 :
130 : /// Destroys the contained object and actives \p Tag, constructing a
131 : /// new value from \p args.
132 : ///
133 : /// \snippet DataStructures/Test_TaggedVariant.cpp emplace
134 : template <
135 : typename Tag, typename... Args,
136 : Requires<(... or std::is_same_v<Tag, Tags>) and
137 : std::is_constructible_v<typename Tag::type, Args...>> = nullptr>
138 1 : constexpr typename Tag::type& emplace(Args&&... args) {
139 : return data_.template emplace<data_index<Tag>>(std::forward<Args>(args)...);
140 : }
141 :
142 0 : constexpr void swap(TaggedVariant& other) noexcept(noexcept(
143 : (... and (std::is_nothrow_move_constructible_v<typename Tags::type> and
144 : std::is_nothrow_swappable_v<typename Tags::type>)))) {
145 : data_.swap(other.data_);
146 : }
147 :
148 0 : void pup(PUP::er& p) { p | data_; }
149 :
150 : /// A TaggedVariant over option tags can be parsed as any of them.
151 : /// @{
152 1 : static constexpr Options::String help = "One of multiple options";
153 1 : using options = tmpl::list<Options::Alternatives<tmpl::list<Tags>...>>;
154 : template <typename Tag>
155 1 : explicit TaggedVariant(tmpl::list<Tag> /*meta*/, typename Tag::type value)
156 : : TaggedVariant(std::in_place_type<Tag>, std::move(value)) {}
157 : /// @}
158 :
159 : private:
160 : template <typename R, typename Variant, typename Tag, typename Visitor>
161 : friend struct TaggedVariant_detail::VisitAlternative;
162 :
163 : template <typename Tag, typename... Tags2>
164 0 : friend constexpr typename Tag::type& get(TaggedVariant<Tags2...>& variant);
165 : template <typename Tag, typename... Tags2>
166 0 : friend constexpr const typename Tag::type& get(
167 : const TaggedVariant<Tags2...>& variant);
168 : template <typename Tag, typename... Tags2>
169 0 : friend constexpr typename Tag::type&& get(TaggedVariant<Tags2...>&& variant);
170 : template <typename Tag, typename... Tags2>
171 0 : friend constexpr const typename Tag::type&& get(
172 : const TaggedVariant<Tags2...>&& variant);
173 :
174 0 : friend constexpr bool operator== <Tags...>(const TaggedVariant<Tags...>& a,
175 : const TaggedVariant<Tags...>& b);
176 0 : friend constexpr bool operator< <Tags...>(const TaggedVariant<Tags...>& a,
177 : const TaggedVariant<Tags...>& b);
178 :
179 : friend struct std::hash<TaggedVariant>;
180 :
181 0 : std::variant<typename Tags::type...> data_;
182 : };
183 :
184 : namespace TaggedVariant_detail {
185 : template <typename... Tags>
186 : constexpr bool is_variant_or_derived(const TaggedVariant<Tags...>* /*meta*/) {
187 : return true;
188 : }
189 : // NOLINTNEXTLINE(cert-dcl50-cpp) - variadic function
190 : constexpr bool is_variant_or_derived(...) { return false; }
191 :
192 : template <typename Tag, typename Value>
193 : constexpr std::pair<tmpl::type_<Tag>, Value&&> make_visitor_pair(
194 : Value&& value) {
195 : return {tmpl::type_<Tag>{}, std::forward<Value>(value)};
196 : }
197 :
198 : struct DeduceReturn;
199 :
200 : template <typename R, typename Variant, typename Tag, typename Visitor>
201 : struct VisitAlternative {
202 : static constexpr R apply(Variant&& variant, const Visitor& visitor) {
203 : return std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(
204 : get<std::decay_t<Variant>::template data_index<Tag>>(
205 : std::forward<Variant>(variant).data_)));
206 : }
207 : };
208 :
209 : template <typename Variant, typename Tag, typename Visitor>
210 : struct VisitAlternative<void, Variant, Tag, Visitor> {
211 : static constexpr void apply(Variant&& variant, const Visitor& visitor) {
212 : std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(
213 : get<std::decay_t<Variant>::template data_index<Tag>>(
214 : std::forward<Variant>(variant).data_)));
215 : }
216 : };
217 :
218 : template <typename Variant, typename Tag, typename Visitor>
219 : struct VisitAlternative<DeduceReturn, Variant, Tag, Visitor> {
220 : static constexpr decltype(auto) apply(Variant&& variant,
221 : const Visitor& visitor) {
222 : return std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(
223 : get<std::decay_t<Variant>::template data_index<Tag>>(
224 : std::forward<Variant>(variant).data_)));
225 : }
226 : };
227 :
228 : template <typename... Tags>
229 : constexpr TaggedVariant<Tags...>& as_variant(TaggedVariant<Tags...>& variant) {
230 : return variant;
231 : }
232 : template <typename... Tags>
233 : constexpr const TaggedVariant<Tags...>& as_variant(
234 : const TaggedVariant<Tags...>& variant) {
235 : return variant;
236 : }
237 : template <typename... Tags>
238 : constexpr TaggedVariant<Tags...>&& as_variant(
239 : TaggedVariant<Tags...>&& variant) {
240 : return std::move(variant);
241 : }
242 : template <typename... Tags>
243 : constexpr const TaggedVariant<Tags...>&& as_variant(
244 : const TaggedVariant<Tags...>&& variant) {
245 : return std::move(variant);
246 : }
247 :
248 : template <typename R, typename Visitor, typename... Variants>
249 : constexpr decltype(auto) visit_impl(Visitor&& visitor) {
250 : return std::forward<Visitor>(visitor)();
251 : }
252 :
253 : template <typename R, typename Variant, typename Visitor,
254 : typename DecayedVariant = std::decay_t<Variant>>
255 : struct VisitJumpTable;
256 :
257 : template <typename R, typename Variant, typename Visitor, typename... Tags>
258 : struct VisitJumpTable<R, Variant, Visitor, TaggedVariant<Tags...>> {
259 : static constexpr std::array value{
260 : VisitAlternative<R, Variant, Tags, Visitor>::apply...};
261 : };
262 :
263 : template <typename R, typename Visitor, typename FirstVariant,
264 : typename... Variants>
265 : constexpr decltype(auto) visit_impl(Visitor&& visitor,
266 : FirstVariant&& first_variant,
267 : Variants&&... variants) {
268 : const auto recurse = [&]<typename Arg>(Arg&& first_arg) {
269 : return visit_impl<R>(
270 : [&]<typename... Rest>(Rest&&... rest) {
271 : return std::forward<Visitor>(visitor)(std::forward<Arg>(first_arg),
272 : std::forward<Rest>(rest)...);
273 : },
274 : std::forward<Variants>(variants)...);
275 : };
276 : if (UNLIKELY(first_variant.valueless_by_exception())) {
277 : throw std::bad_variant_access{};
278 : }
279 : return gsl::at(VisitJumpTable<R, FirstVariant, decltype(recurse)>::value,
280 : first_variant.index())(
281 : std::forward<FirstVariant>(first_variant), recurse);
282 : }
283 : } // namespace TaggedVariant_detail
284 :
285 : /// Call \p visitor with the contents of one or more variants.
286 : ///
287 : /// Calls \p visitor with the contents of each variant as arguments,
288 : /// passed as `std::pair<tmpl::type_<Tag>, typename Tag::type ref>`,
289 : /// where `Tag` is the active tag of the variant and `ref` is a
290 : /// reference qualifier matching that of the passed variant.
291 : ///
292 : /// If the template parameter \p R is supplied, the result is
293 : /// implicitly converted to that type (which may be `void`).
294 : /// Otherwise it is deduced from the return type of \p visitor, which
295 : /// must be the same for all tags in the variant.
296 : ///
297 : /// \warning Unlike `visit` for `std::variant`, the types of the
298 : /// visitor arguments do not allow for implicit conversions between
299 : /// reference types. If the visitor expects, for example,
300 : /// `std::pair<tmpl::type_<Tag>, const typename Tag::type&>`, the caller must
301 : /// ensure that the passed variant is a const lvalue.
302 : ///
303 : /// \snippet DataStructures/Test_TaggedVariant.cpp visit
304 : /// @{
305 : template <typename Visitor, typename... Variants,
306 : Requires<(... and TaggedVariant_detail::is_variant_or_derived(
307 : std::add_pointer_t<std::remove_reference_t<
308 : Variants>>{}))> = nullptr>
309 1 : constexpr decltype(auto) visit(Visitor&& visitor, Variants&&... variants) {
310 : return TaggedVariant_detail::visit_impl<TaggedVariant_detail::DeduceReturn>(
311 : visitor,
312 : TaggedVariant_detail::as_variant(std::forward<Variants>(variants))...);
313 : }
314 :
315 : template <typename R, typename Visitor, typename... Variants,
316 : Requires<(... and TaggedVariant_detail::is_variant_or_derived(
317 : std::add_pointer_t<std::remove_reference_t<
318 : Variants>>{}))> = nullptr>
319 1 : constexpr R visit(Visitor&& visitor, Variants&&... variants) {
320 : return TaggedVariant_detail::visit_impl<R>(
321 : visitor,
322 : TaggedVariant_detail::as_variant(std::forward<Variants>(variants))...);
323 : }
324 : /// @}
325 :
326 : /// Check whether \p Tag is active.
327 : template <typename Tag, typename... Tags>
328 1 : constexpr bool holds_alternative(const TaggedVariant<Tags...>& variant) {
329 : return variant.index() == tmpl::index_of<TaggedVariant<Tags...>, Tag>::value;
330 : }
331 :
332 : /// Access the contained object. Throws `std::bad_variant_access` if
333 : /// \p Tag is not active.
334 : /// @{
335 : template <typename Tag, typename... Tags>
336 1 : constexpr typename Tag::type& get(TaggedVariant<Tags...>& variant) {
337 : return get<TaggedVariant<Tags...>::template data_index<Tag>>(variant.data_);
338 : }
339 : template <typename Tag, typename... Tags>
340 1 : constexpr const typename Tag::type& get(const TaggedVariant<Tags...>& variant) {
341 : return get<TaggedVariant<Tags...>::template data_index<Tag>>(variant.data_);
342 : }
343 : template <typename Tag, typename... Tags>
344 1 : constexpr typename Tag::type&& get(TaggedVariant<Tags...>&& variant) {
345 : return get<TaggedVariant<Tags...>::template data_index<Tag>>(
346 : std::move(variant.data_));
347 : }
348 : template <typename Tag, typename... Tags>
349 1 : constexpr const typename Tag::type&& get(
350 : const TaggedVariant<Tags...>&& variant) {
351 : return get<TaggedVariant<Tags...>::template data_index<Tag>>(
352 : std::move(variant.data_));
353 : }
354 : /// @}
355 :
356 : /// Returns a pointer to the contained object if \p variant is a
357 : /// non-null pointer and \p Tag is active. Otherwise, returns
358 : /// `nullptr`.
359 : /// @{
360 : template <typename Tag, typename... Tags>
361 1 : constexpr const typename Tag::type* get_if(
362 : const TaggedVariant<Tags...>* variant) {
363 : if (variant != nullptr and holds_alternative<Tag>(*variant)) {
364 : return &get<Tag>(*variant);
365 : } else {
366 : return nullptr;
367 : }
368 : }
369 : template <typename Tag, typename... Tags>
370 1 : constexpr typename Tag::type* get_if(TaggedVariant<Tags...>* variant) {
371 : if (variant != nullptr and holds_alternative<Tag>(*variant)) {
372 : return &get<Tag>(*variant);
373 : } else {
374 : return nullptr;
375 : }
376 : }
377 : /// @}
378 :
379 : template <typename... Tags>
380 0 : constexpr bool operator==(const TaggedVariant<Tags...>& a,
381 : const TaggedVariant<Tags...>& b) {
382 : return a.data_ == b.data_;
383 : }
384 : template <typename... Tags>
385 0 : constexpr bool operator!=(const TaggedVariant<Tags...>& a,
386 : const TaggedVariant<Tags...>& b) {
387 : return not(a == b);
388 : }
389 : template <typename... Tags>
390 0 : constexpr bool operator<(const TaggedVariant<Tags...>& a,
391 : const TaggedVariant<Tags...>& b) {
392 : return a.data_ < b.data_;
393 : }
394 : template <typename... Tags>
395 0 : constexpr bool operator>(const TaggedVariant<Tags...>& a,
396 : const TaggedVariant<Tags...>& b) {
397 : return b < a;
398 : }
399 : template <typename... Tags>
400 0 : constexpr bool operator<=(const TaggedVariant<Tags...>& a,
401 : const TaggedVariant<Tags...>& b) {
402 : return not(b < a);
403 : }
404 : template <typename... Tags>
405 0 : constexpr bool operator>=(const TaggedVariant<Tags...>& a,
406 : const TaggedVariant<Tags...>& b) {
407 : return not(a < b);
408 : }
409 :
410 : template <
411 : typename... Tags,
412 : Requires<(... and (std::is_move_constructible_v<typename Tags::type> and
413 : std::is_swappable_v<typename Tags::type>))> = nullptr>
414 0 : constexpr void swap(TaggedVariant<Tags...>& a,
415 : TaggedVariant<Tags...>& b) noexcept(noexcept(a.swap(b))) {
416 : a.swap(b);
417 : }
418 :
419 : template <typename... Tags>
420 : template <
421 : typename... OtherTags,
422 : Requires<tmpl::size<tmpl::list_difference<TaggedVariant<OtherTags...>,
423 : TaggedVariant<Tags...>>>::value ==
424 : 0>>
425 : constexpr TaggedVariant<Tags...>::TaggedVariant(
426 : TaggedVariant<OtherTags...>&& other)
427 : : TaggedVariant(visit(
428 : []<typename Tag>(
429 : std::pair<tmpl::type_<Tag>, typename Tag::type&&> entry) {
430 : return TaggedVariant(std::in_place_type<Tag>,
431 : std::move(entry.second));
432 : },
433 : std::move(other))) {}
434 :
435 : template <typename... Tags>
436 : template <
437 : typename... OtherTags,
438 : Requires<tmpl::size<tmpl::list_difference<TaggedVariant<OtherTags...>,
439 : TaggedVariant<Tags...>>>::value ==
440 : 0>>
441 0 : constexpr TaggedVariant<Tags...>& TaggedVariant<Tags...>::operator=(
442 : TaggedVariant<OtherTags...>&& other) {
443 : visit(
444 : [&]<typename Tag>(
445 : std::pair<tmpl::type_<Tag>, typename Tag::type&&> entry) {
446 : emplace<Tag>(std::move(entry.second));
447 : },
448 : std::move(other));
449 : return *this;
450 : }
451 : } // namespace variants
452 :
453 : namespace std {
454 : template <typename... Tags>
455 : // https://github.com/llvm/llvm-project/issues/45454
456 : // NOLINTNEXTLINE(cert-dcl58-cpp)
457 : struct hash<::variants::TaggedVariant<Tags...>> {
458 : size_t operator()(const ::variants::TaggedVariant<Tags...>& variant) const {
459 : return std::hash<decltype(variant.data_)>{}(variant.data_);
460 : }
461 : };
462 : } // namespace std
|