GCC Code Coverage Report


Directory: include/sumty/
File: include/sumty/error_set.hpp
Date: 2024-04-28 13:27:51
Exec Total Coverage
Lines: 38 38 100.0%
Functions: 46 46 100.0%
Branches: 2 4 50.0%
Decisions: 0 0 -%

Line Branch Decision Exec Source
1 /* Copyright 2024 Jack A Bernard Jr.
2 *
3 * Licensed under the Apache License, Version 2.0 (the License);
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an AS IS BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef SUMTY_ERROR_SET_HPP
17 #define SUMTY_ERROR_SET_HPP
18
19 #include "sumty/detail/fwd.hpp" // IWYU pragma: export
20 #include "sumty/detail/traits.hpp" // IWYU pragma: export
21 #include "sumty/detail/utils.hpp"
22 #include "sumty/utils.hpp"
23 #include "sumty/variant.hpp"
24
25 #include <cstddef>
26 #include <initializer_list>
27 #include <type_traits>
28 #include <utility>
29
30 namespace sumty {
31
32 /// @class error_set error_set.hpp <sumty/error_set.hpp>
33 /// @brief A discriminated union of unique error types
34 ///
35 /// @details
36 /// @ref error_set is very similar to @ref variant in term of API. The key
37 /// difference from @ref variant is that @ref error_set requires all its
38 /// alternatives to be unique, and @ref error_set allows direct conversions
39 /// between different, but compatible, @ref error_set instantiations.
40 ///
41 /// Given distinct types `A`, `B`, and `C`, @ref error_set allows the
42 /// following implicit conversions (this is not exhaustive):
43 /// * `A -> error_set<A, B, C>`
44 /// * `B -> error_set<A, B, C>`
45 /// * `C -> error_set<A, B, C>`
46 /// * `error_set<B> -> error_set<A, B, C>`
47 /// * `error_set<C, A> -> error_set<A, B, C>`
48 /// * `error_set<C, A, B> -> error_set<A, B, C>`
49 ///
50 /// Generally, an @ref error_set can convert into a different error set as long
51 /// as each of its alternatives unambiguously map to an alternative in the
52 /// destination @ref error_set. Additionally, any bare type can convert into an
53 /// @ref error_set if it can unambiguously convert into one of the destination
54 /// alternatives.
55 ///
56 /// The primary purpose of @ref error_set is to improve the convenience of
57 /// error propagation with @ref result. The conversions shown above also apply
58 /// to an @ref error_set nested as the error type of a @ref result. Below is a
59 /// listing of the previous conversions examples, but now wrapped in @ref
60 /// result, which is equally valid.
61 /// * `result<T, A> -> result<T, error_set<A, B, C>>`
62 /// * `result<T, B> -> result<T, error_set<A, B, C>>`
63 /// * `result<T, C> -> result<T, error_set<A, B, C>>`
64 /// * `result<T, error_set<B>> -> result<T, error_set<A, B, C>>`
65 /// * `result<T, error_set<C, A>> -> result<T, error_set<A, B, C>>`
66 /// * `result<T, error_set<C, A, B>> -> result<T, error_set<A, B, C>>`
67 ///
68 /// Below is an example of how @ref error_set might be used in practice.
69 /// ```
70 /// // specific error types
71 /// struct neg_int_error {};
72 /// struct int_parse_error {};
73 /// struct int_overflow_error {};
74 ///
75 /// // union of the above error types
76 /// using my_errors = error_set<
77 /// neg_int_error,
78 /// int_parse_error,
79 /// int_overflow_error
80 /// >;
81 ///
82 /// // parse an integer from a string
83 /// result<int, int_parse_error> parse_int(std::string_view str);
84 ///
85 /// // calculate the absolute value of an integer
86 /// result<int, int_overflow_error> abs_value(int x);
87 ///
88 /// // calculate the integer square root
89 /// result<int, neg_int_error> isqrt(int x);
90 ///
91 /// result<int, my_errors> my_algorithm(std::string_view str) {
92 /// auto parsed = parse_int(str);
93 /// if (parsed.is_error()) { return parsed; }
94 ///
95 /// auto positive = abs_value(*parsed);
96 /// if (positive.is_error()) { return positive; }
97 ///
98 /// return isqrt(*positive);
99 /// }
100 /// ```
101 ///
102 /// One possible pattern when using @ref error_set is to define all errors as
103 /// empty types. Like all other sum types in sumty, an @ref error_set of all
104 /// empty types only needs to actually store the discriminant. In this case,
105 /// @ref error_set behaves like a flexible enum partially mapped into the type
106 /// system. That is, a specific (empty) error type uniquely identifies an
107 /// alternative in any @ref error_set that contains that error type, similar to
108 /// how an enum value name identifies a value of that enum. So it is possible
109 /// to create a set of error codes as types without giving them specific values
110 /// and define subsets of those error codes for different use cases. A function
111 /// might emit some, but not all, of the errors in your set of error codes, so
112 /// that function need only declare that it can return those specific errors
113 /// instead of all codes in the set.
114 ///
115 /// That said, it is also perfectly valid for each error type to contain
116 /// additional error information as needed.
117 template <typename... T>
118 class error_set : variant<T...> {
119 private:
120 static_assert(detail::all_unique_v<T...>,
121 "All error types in an error_set must be unqiue");
122
123 26 [[nodiscard]] constexpr variant<T...>& set_() & noexcept {
124 26 return *static_cast<variant<T...>*>(this);
125 }
126
127 50 [[nodiscard]] constexpr const variant<T...>& set_() const& noexcept {
128 50 return *static_cast<const variant<T...>*>(this);
129 }
130
131 [[nodiscard]] constexpr variant<T...>&& set_() && {
132 return std::move(*static_cast<variant<T...>*>(this));
133 }
134
135 [[nodiscard]] constexpr const variant<T...>&& set_() const&& {
136 return std::move(*static_cast<const variant<T...>*>(this));
137 }
138
139 template <typename...>
140 friend class error_set;
141
142 public:
143 /// @brief Default constructor
144 ///
145 /// @details
146 /// Initializes the @ref error_set such that it contains a default
147 /// constructed value of the first (index 0) alternative.
148 ///
149 /// The first alternative *must* be default constructible for this
150 /// constructor to participate in overload resoltuion, but no other
151 /// alternatives need be default constructible.
152 ///
153 /// ## Example
154 /// ```cpp
155 /// error_set<error1, error2> set;
156 ///
157 /// assert(holds_alternative<error1>(set));
158 ///
159 /// assert(set.index() == 0);
160 /// ```
161 10 constexpr error_set()
162 #ifndef DOXYGEN
163 noexcept(std::is_nothrow_default_constructible_v<variant<T...>>)
164 requires(std::is_default_constructible_v<variant<T...>>)
165 = default;
166 #else
167 CONDITIONALLY_NOEXCEPT;
168 #endif
169
170 /// @brief Copy constructor
171 ///
172 /// @details
173 /// A new @ref error_set is initialized such that it contains a copy
174 /// constructed instance of the alternative contained in the source @ref
175 /// error_set. If the source @ref error_set has more than one alternative of
176 /// the same type, the new @ref error_set will contain the alternative of the
177 /// same index.
178 ///
179 /// All alternative types *must* be copy constructible for this constructor
180 /// to participate in overload resolution.
181 ///
182 /// ## Example
183 /// ```cpp
184 /// error_set<error1, error2> e1{std::in_place_index<1>};
185 ///
186 /// error_set<error1, error2> e2{e1};
187 ///
188 /// assert(holds_alternative<error2>(e2));
189 ///
190 /// assert(e2.index() == 1);
191 /// ```
192 constexpr error_set(const error_set&)
193 #ifndef DOXYGEN
194 noexcept(std::is_nothrow_copy_constructible_v<variant<T...>>)
195 requires(std::is_copy_constructible_v<variant<T...>>)
196 = default;
197 #else
198 CONDITIONALLY_NOEXCEPT;
199 #endif
200
201 /// @brief Move constructor
202 ///
203 /// @details
204 /// A new @ref error_set is initialized such that it contains a move
205 /// constructed instance of the alternative contained in the source @ref
206 /// error_set. If the source @ref error_set has more than one alternative of
207 /// the same type, the new @ref error_set will contain the alternative of the
208 /// same index.
209 ///
210 /// All alternative types *must* be move constructible for this constructor
211 /// to participate in overload resolution.
212 ///
213 /// The source @ref error_set will continue to contain an instance of the same
214 /// alternative, but the value of the alternative after being moved depends
215 /// on the move constructor of the type. In general, moved values are said
216 /// to be in a valid, but unspecified, state.
217 ///
218 /// ## Example
219 /// ```cpp
220 /// error_set<error1, error2> e1{std::in_place_index<1>};
221 ///
222 /// error_set<error1, error2> e2{std::move(e1)};
223 ///
224 /// assert(holds_alternative<error2>(e2));
225 ///
226 /// assert(e2.index() == 1);
227 /// ```
228 constexpr error_set(error_set&&)
229 #ifndef DOXYGEN
230 noexcept(std::is_nothrow_move_constructible_v<variant<T...>>)
231 requires(std::is_move_constructible_v<variant<T...>>)
232 = default;
233 #else
234 CONDITIONALLY_NOEXCEPT;
235 #endif
236
237 /// @brief Index emplacement constructor
238 ///
239 /// @details
240 /// A new @ref error_set is initialized such that it contains a newly
241 /// constructed instance of the alternative with the specified index.
242 /// The arguments following `inplace` are forwarded directly to the
243 /// constructor of the alternative type.
244 ///
245 /// Given that `U` is the type of the alternative at the specified index,
246 /// this constructor is always valid as long as `U` is constructible with
247 /// the arguments, `std::forward<Args>(args)...`.
248 ///
249 /// ## Example
250 /// ```cpp
251 /// error_set<error, std::string> e{std::in_place_index<1>, 5, 'a'};
252 ///
253 /// assert(holds_alternative<std::string>(e));
254 ///
255 /// assert(e.index() == 1);
256 ///
257 /// assert(get<1>(e) == "aaaaa");
258 /// ```
259 ///
260 /// @param inplace Constructor tag that specifies the alternative index.
261 /// @param args Arguments used to construct the alternative.
262 template <size_t IDX, typename... Args>
263 #ifndef DOXYGEN
264 explicit(sizeof...(Args) == 0)
265 #else
266 CONDITIONALLY_EXPLICIT
267 #endif
268 // NOLINTNEXTLINE(hicpp-explicit-conversions)
269 1 constexpr error_set(std::in_place_index_t<IDX> inplace, Args&&... args)
270 1 : variant<T...>(inplace, std::forward<Args>(args)...) {
271 1 }
272
273 /// @brief Index emplacement constructor with `std::initializer_list`
274 ///
275 /// @details
276 /// A new @ref error_set is initialized such that it contains a newly
277 /// constructed instance of the alternative with the specified index.
278 /// The arguments following `inplace` are forwarded directly to the
279 /// constructor of the alternative type.
280 ///
281 /// Given that `U` is the type of the alternative at the specified index,
282 /// this constructor is always valid as long as `U` is constructible with
283 /// the arguments, `init, std::forward<Args>(args)...`.
284 ///
285 /// ## Example
286 /// ```cpp
287 /// error_set<error, std::vector<int>> e{
288 /// std::in_place_index<1>,
289 /// {1, 2, 3, 4, 5}};
290 ///
291 /// assert(holds_alternative<std::vector<int>>(e));
292 ///
293 /// assert(e.index() == 1);
294 ///
295 /// assert(get<1>(e).size() == 5);
296 /// ```
297 ///
298 /// @param inplace Constructor tag that specifies the alternative index.
299 /// @param init Initializer list forwarded to the alternative constructor
300 /// @param args Additional arguments used to construct the alternative.
301 template <size_t IDX, typename U, typename... Args>
302 constexpr error_set(std::in_place_index_t<IDX> inplace,
303 std::initializer_list<U> init,
304 Args&&... args)
305 : variant<T...>(inplace, init, std::forward<Args>(args)...) {}
306
307 /// @brief Type emplacement constructor
308 ///
309 /// @details
310 /// A new @ref error_set is initialized such that it contains a newly
311 /// constructed instance of the alternative with the specified type.
312 /// The arguments following `inplace` are forwarded directory to the
313 /// constructor of the alternative type.
314 ///
315 /// ## Example
316 /// ```cpp
317 /// error_set<error, std::string> e{
318 /// std::in_place_type<std::string>,
319 /// 5, 'a'};
320 ///
321 /// assert(holds_alternative<std::string>(e));
322 ///
323 /// assert(e.index() == 1);
324 ///
325 /// assert(get<1>(e) == "aaaaa");
326 /// ```
327 template <typename U, typename... Args>
328 #ifndef DOXYGEN
329 explicit(sizeof...(Args) == 0)
330 #else
331 CONDITIONALLY_EXPLICIT
332 #endif
333 // NOLINTNEXTLINE(hicpp-explicit-conversions)
334 constexpr error_set([[maybe_unused]] std::in_place_type_t<U> inplace,
335 Args&&... args)
336 : variant<T...>(inplace, std::forward<Args>(args)...) {
337 }
338
339 /// @brief Type emplacement constructor with `std::initializer_list`
340 ///
341 /// @details
342 /// A new @ref error_set is initialized such that it contains a newly
343 /// constructed instance of the alternative with the specified type.
344 /// The arguments following `inplace` are forwarded directory to the
345 /// constructor of the alternative type.
346 ///
347 /// ## Example
348 /// ```cpp
349 /// error_set<error, std::vector<int>> e{
350 /// std::in_place_type<std::vector<int>>,
351 /// {1, 2, 3, 4, 5}};
352 ///
353 /// assert(holds_alternative<std::vector<int>>(e));
354 ///
355 /// assert(e.index() == 1);
356 ///
357 /// assert(get<1>(e).size() == 5);
358 /// ```
359 template <typename U, typename V, typename... Args>
360 constexpr error_set([[maybe_unused]] std::in_place_type_t<U> inplace,
361 std::initializer_list<V> init,
362 Args&&... args)
363 : variant<T...>(inplace, init, std::forward<Args>(args)...) {}
364
365 /// @brief Forwarding constructor
366 ///
367 /// @details
368 /// A new @ref error_set is initialized such that an alternative is
369 /// constructed in place with the provided value as the constructor
370 /// argument.
371 ///
372 /// To avoid ambiguity to the reader, this constructor only participates
373 /// in overload resolution when there is only one alternative that could
374 /// possibly be constructed from the value.
375 ///
376 /// This constructor is `explicit` if the value is not implicitly
377 /// convertible to the target alternative type.
378 ///
379 /// ## Example
380 /// ```cpp
381 /// error_set<error, std::string> e{"oh no"};
382 ///
383 /// assert(e.index() == 1);
384 ///
385 /// assert(get<1>(e) == "oh no");
386 /// ```
387 ///
388 /// @param value The value that is used to construct the alternative
389 template <typename U>
390 #ifndef DOXYGEN
391 requires(!detail::is_error_set_v<std::remove_cvref_t<U>> &&
392 detail::is_uniquely_constructible_v<U, T...>)
393 explicit(detail::is_uniquely_explicitly_constructible_v<U, T...>)
394 #else
395 CONDITIONALLY_EXPLICIT
396 #endif
397 // NOLINTNEXTLINE(hicpp-explicit-conversions)
398 12 constexpr error_set(U&& value)
399 12 : variant<T...>(std::forward<U>(value)) {
400 12 }
401
402 /// @brief Forwarding constructor with initializer list
403 ///
404 /// @details
405 /// A new @ref error_set is initialized such that an alternative is
406 /// constructed in place with the provided `std::initializer_list`
407 /// as the constructor argument.
408 ///
409 /// To avoid ambiguity to the reader, this constructor only participates
410 /// in overload resulotion when there is only one alternative that could
411 /// possible be constructed from the initializer list.
412 ///
413 /// This constructor is `explicit` if the `std::initializer_list` is not
414 /// implicitly convertible to the target alternative type.
415 ///
416 /// ## Example
417 /// ```cpp
418 /// error_set<error, std::vector<int>> e({1, 2, 3, 4, 5});
419 ///
420 /// assert(e.index() == 1);
421 ///
422 /// assert(get<1>(e).size() == 5);
423 /// ```
424 ///
425 /// @param init The `std::initializer_list` that is used to construct the
426 /// alternative
427 template <typename U>
428 #ifndef DOXYGEN
429 requires(detail::is_uniquely_constructible_v<std::initializer_list<U>, T...>)
430 explicit(detail::is_uniquely_explicitly_constructible_v<std::initializer_list<U>, T...>)
431 #else
432 CONDITIONALLY_EXPLICIT
433 #endif
434 constexpr error_set(std::initializer_list<U> init)
435 : variant<T...>(init) {
436 }
437
438 /// @brief Copy conversion constructor
439 ///
440 /// @details
441 /// A new @ref error_set is initialized from the value contained in another
442 /// @ref error_set that is a non-strict subset of the destination @ref
443 /// error_set. Each of the alternative types in the source @ref error_set
444 /// must also be an alternative type in the destination @ref error_set, but
445 /// they need not have the same index.
446 ///
447 /// This constructor allows for values of @ref error_set to be easily
448 /// propagated, whether returned directly, or as an alternative of any
449 /// other sum type. Most commonly, this will be used to implicitly convert
450 /// from a @ref result to another result with a superset of the errors as
451 /// the source, such as when returning from a function.
452 ///
453 /// ## Example
454 /// ```
455 /// const error_set<error1, error2> e1{std::in_place_index<1>};
456 ///
457 /// const error_set<error2, error3, error1> e2{e1};
458 ///
459 /// assert(holds_alternative<error2>(e2));
460 ///
461 /// assert(e2.index() == 0);
462 /// ```
463 template <typename... U>
464 #ifndef DOXYGEN
465 requires(!std::is_same_v<error_set<U...>, error_set<T...>> &&
466 detail::is_subset_of_impl_v<error_set<U...>, error_set<T...>>)
467 #endif
468 // NOLINTNEXTLINE(hicpp-explicit-conversions)
469 4 constexpr error_set(const error_set<U...>& other) : variant<T...>(detail::uninit) {
470
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
8 other.set_().visit_informed([this](const auto& value, auto info) {
471 2 set_()
472 .template uninit_emplace<
473 detail::index_of_v<typename decltype(info)::type, T...>>(value);
474 });
475 4 }
476
477 /// @brief Move conversion constructor
478 ///
479 /// @details
480 /// A new @ref error_set is initialized from the value contained in another
481 /// @ref error_set that is a non-strict subset of the destination @ref
482 /// error_set. Each of the alternative types in the source @ref error_set
483 /// must also be an alternative type in the destination @ref error_set, but
484 /// they need not have the same index.
485 ///
486 /// This constructor allows for values of @ref error_set to be easily
487 /// propagated, whether returned directly, or as an alternative of any
488 /// other sum type. Most commonly, this will be used to implicitly convert
489 /// from a @ref result to another result with a superset of the errors as
490 /// the source, such as when returning from a function.
491 ///
492 /// ## Example
493 /// ```
494 /// error_set<error1, error2> e1{std::in_place_index<1>};
495 ///
496 /// error_set<error2, error3, error1> e2{std::move(e1)};
497 ///
498 /// assert(holds_alternative<error2>(e2));
499 ///
500 /// assert(e2.index() == 0);
501 /// ```
502 template <typename... U>
503 #ifndef DOXYGEN
504 requires(!std::is_same_v<error_set<U...>, error_set<T...>> &&
505 detail::is_subset_of_impl_v<error_set<U...>, error_set<T...>>)
506 #endif
507 // clang-format off
508 // NOLINTNEXTLINE(hicpp-explicit-conversions,cppcoreguidelines-rvalue-reference-param-not-moved)
509 constexpr error_set(error_set<U...>&& other) : variant<T...>(detail::uninit) {
510 // clang-format on
511 std::move(other.set_()).visit_informed([this](auto&& value, auto info) {
512 set_()
513 .template uninit_emplace<
514 detail::index_of_v<typename decltype(info)::type, T...>>(
515 info.forward(value));
516 });
517 }
518
519 /// @brief Destructor
520 ///
521 /// @details
522 /// The contained alternative of the @ref error_set will is destroyed in
523 /// place.
524 ///
525 /// The destructor is `noexcept` if all alternative types are nothrow
526 /// destructible.
527 28 constexpr ~error_set()
528 #ifndef DOXYGEN
529 noexcept(std::is_nothrow_destructible_v<variant<T...>>) = default;
530 #else
531 CONDITIONALLY_NOEXCEPT;
532 #endif
533
534 /// @brief Copy assignment operator
535 ///
536 /// @details
537 /// Copy assignment of an @ref error_set can take one of two possible code
538 /// paths.
539 ///
540 /// If the source and destination @ref error_set hold the same alternative
541 /// (same index), the alternative value is copied via copy assignment.
542 ///
543 /// Otherwise, if the source and destination hold different alternatives
544 /// (different indices, but possibly the same type), the alternative of
545 /// the destination @ref error_set is destroyed in place, and the new
546 /// alternative is copy constructed.
547 ///
548 /// All alternatives *must* be both copy assignable and copy constructible
549 /// for this function to participate in overload resolution.
550 ///
551 /// ## Example
552 /// ```cpp
553 /// error_set<error1, error2> e1{std::in_place_index<0>};
554 ///
555 /// variant<error2, error2> e2{std::in_place_index<1>};
556 ///
557 /// e1 = e2;
558 ///
559 /// assert(holds_alternative<error2>(e1));
560 ///
561 /// assert(e1.index() == 1);
562 /// ```
563 constexpr error_set& operator=(const error_set&)
564 #ifndef DOXYGEN
565 noexcept(std::is_nothrow_copy_assignable_v<variant<T...>>)
566 requires(std::is_copy_assignable_v<variant<T...>>)
567 = default;
568 #else
569 CONDITIONALLY_NOEXCEPT;
570 #endif
571
572 /// @brief Move assignment operator
573 ///
574 /// @details
575 /// Move assignment of an @ref error_set can take one of two possible code
576 /// paths.
577 ///
578 /// If the source and destination @ref error_set hold the same alternative
579 /// (same index), the alternative value is moved from the source to the
580 /// destination via move assignment.
581 ///
582 /// Otherwise, if the source and destination hold different alternatives
583 /// (different indices, but possibly the same type), the alternative of the
584 /// destination @ref error_set is destroyed in place, and the new alternative
585 /// is move constructed.
586 ///
587 /// The source @ref error_set will still contain the same alternative, but
588 /// the value of the alternative depends on the move assignment or move
589 /// constructor of the alternative's type. In general, moved values are
590 /// said to be in a valid, but unspecified, state.
591 ///
592 /// All alternatives *must* be both move assignable and move constructible
593 /// for this function to participate in overload resolution.
594 ///
595 /// ## Example
596 /// ```cpp
597 /// error_set<error1, error2> e1{std::in_place_index<0>};
598 ///
599 /// error_set<error1, error2> e2{std::in_place_index<1>};
600 ///
601 /// e1 = std::move(e2);
602 ///
603 /// assert(holds_alternative<error2>(e1));
604 ///
605 /// assert(e1.index() == 1);
606 /// ```
607 constexpr error_set& operator=(error_set&&)
608 #ifndef DOXYGEN
609 noexcept(std::is_nothrow_move_assignable_v<variant<T...>>)
610 requires(std::is_move_assignable_v<variant<T...>>)
611 = default;
612 #else
613 CONDITIONALLY_NOEXCEPT;
614 #endif
615
616 /// @brief Forwarding assignment operator
617 ///
618 /// @details
619 /// This function assigns a value directly to an alternative. If the @ref
620 /// error_set already contains the target alternative, the value is assigned
621 /// using the alternative type's assignemnt operator. Otherwise, the target
622 /// alternative is constructed with the value.
623 ///
624 /// To avoid ambiguity to the reader, this function only participates in
625 /// overload resolution when there is only one alternative that could
626 /// possibly be assigned from the value.
627 ///
628 /// ## Example
629 /// ```cpp
630 /// error_set<error1, error2> e{};
631 ///
632 /// e = error2{};
633 ///
634 /// assert(holds_alternative<error2>(e));
635 ///
636 /// assert(e.index() == 1);
637 /// ```
638 ///
639 /// @param rhs The value to be assigned to the alternative
640 template <typename U>
641 #ifndef DOXYGEN
642 requires(!detail::is_error_set_v<std::remove_cvref_t<U>> &&
643 detail::is_uniquely_assignable_v<U, T...>)
644 #endif
645 1 constexpr error_set& operator=(U&& rhs) {
646 1 set_() = std::forward<U>(rhs);
647 1 return *this;
648 }
649
650 /// @brief Forwarding assignment operator with initializer list
651 ///
652 /// @details
653 /// This function assigns a `std::initializer_list` directoy to an
654 /// alternative. If the @ref error_set already contains the target
655 /// alternative, the `std::initializer_list` is assigned uting the
656 /// alternative type's assignment operator. Otherwise, the target
657 /// alternative is constructed with the `std::initializer_list`.
658 ///
659 /// To avoid ambiguity to the reader, this function only participates in
660 /// overload resolution when there is only one alternative that could
661 /// possibly be assigned from the `std::initializer_list`.
662 ///
663 /// ## Example
664 /// ```cpp
665 /// variant<error, std::vector<int>> e{};
666 ///
667 /// e = {1, 2, 3, 4, 5};
668 ///
669 /// assert(holds_alternative<std::vector<int>>(e));
670 ///
671 /// assert(e.index() == 1);
672 /// ```
673 ///
674 /// @param rhs The `std::initializer_list` to be assigned to the
675 /// alternative
676 template <typename U>
677 #ifndef DOXYGEN
678 requires(detail::is_uniquely_assignable_v<std::initializer_list<U>, T...>)
679 #endif
680 constexpr error_set& operator=(std::initializer_list<U> rhs) {
681 set_() = rhs;
682 return *this;
683 }
684
685 /// @brief Copy conversion assignment operator
686 ///
687 /// @details
688 /// This function assigns the value contained in another
689 /// @ref error_set that is a non-strict subset of the destination @ref
690 /// error_set. Each of the alternative types in the source @ref error_set
691 /// must also be an alternative type in the destination @ref error_set, but
692 /// they need not have the same index.
693 ///
694 /// ## Example
695 /// ```
696 /// const error_set<error1, error2> e1{std::in_place_index<1>};
697 ///
698 /// error_set<error2, error3, error1> e2{};
699 ///
700 /// e2 = e1;
701 ///
702 /// assert(holds_alternative<error2>(e2));
703 ///
704 /// assert(e2.index() == 0);
705 /// ```
706 template <typename... U>
707 #ifndef DOXYGEN
708 requires(!std::is_same_v<error_set<U...>, error_set<T...>> &&
709 detail::is_subset_of_impl_v<error_set<U...>, error_set<T...>>)
710 #endif
711 4 constexpr error_set& operator=(const error_set<U...>& rhs) {
712
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
8 rhs.set_().visit_informed([this](auto&& value, auto info) {
713 2 set_()
714 .template emplace<detail::index_of_v<typename decltype(info)::type, T...>>(
715 value);
716 });
717 4 return *this;
718 }
719
720 /// @brief Move conversion assignment operator
721 ///
722 /// @details
723 /// This function assigns the value contained in another
724 /// @ref error_set that is a non-strict subset of the destination @ref
725 /// error_set. Each of the alternative types in the source @ref error_set
726 /// must also be an alternative type in the destination @ref error_set, but
727 /// they need not have the same index.
728 ///
729 /// ## Example
730 /// ```
731 /// error_set<error1, error2> e1{std::in_place_index<1>};
732 ///
733 /// error_set<error2, error3, error1> e2{};
734 ///
735 /// e2 = std::move(e1);
736 ///
737 /// assert(holds_alternative<error2>(e2));
738 ///
739 /// assert(e2.index() == 0);
740 /// ```
741 template <typename... U>
742 #ifndef DOXYGEN
743 requires(!std::is_same_v<error_set<U...>, error_set<T...>> &&
744 detail::is_subset_of_impl_v<error_set<U...>, error_set<T...>>)
745 #endif
746 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
747 constexpr error_set& operator=(error_set<U...>&& rhs) {
748 std::move(rhs.set_()).visit_informed([this](auto&& value, auto info) {
749 set_()
750 .template emplace<detail::index_of_v<typename decltype(info)::type, T...>>(
751 info.forward(value));
752 });
753 return *this;
754 }
755
756 /// @brief Gets the index of the contained alternative
757 ///
758 /// @details
759 /// The set of alternatives of a @ref error_set has a zero-based index based
760 /// on the order in which they are specified in the @ref error_set template
761 /// arguments.
762 ///
763 /// This index is the normalized discriminant of the @ref error_set. The
764 /// discriminant may be represented differently internally, depending on
765 /// the alternative types, so this function normalizes the discriminant by
766 /// converting it to a zero-based index in order to provide a common
767 /// interface for all @ref error_set instantiations.
768 ///
769 /// ## Example
770 /// ```cpp
771 /// error_set<error1, error2, error3, error4> e{std::in_place_index<2>};
772 ///
773 /// assert(e.index() == 2);
774 /// ```
775 ///
776 /// @return The index of the contained alternative.
777 20 [[nodiscard]] constexpr size_t index() const noexcept { return set_().index(); }
778
779 /// @brief Constructs a new alternative in place by index
780 ///
781 /// @details
782 /// This function destroys the alternative that the @ref error_set contains
783 /// before the call, and constructs a new alternative with the specified
784 /// index in place.
785 ///
786 /// ## Example
787 /// ```cpp
788 /// error_set<error, std::string> e;
789 ///
790 /// e.emplace<1>(5, 'a');
791 ///
792 /// assert(holds_alternative<std::string>(e));
793 ///
794 /// assert(e.index() == 1);
795 ///
796 /// assert(get<1>(e) == "aaaaa");
797 /// ```
798 ///
799 /// @param args Constructor arguments forwarded to the new alternative
800 /// @return A reference to the new alternative, if applicable
801 template <size_t I, typename... Args>
802 constexpr
803 #ifndef DOXYGEN
804 typename detail::traits<detail::select_t<I, T...>>::reference
805 #else
806 REFERENCE
807 #endif
808 emplace(Args&&... args) {
809 return set_().template emplace<I>(std::forward<Args>(args)...);
810 }
811
812 /// @brief Constructs a new alternative in place by index
813 ///
814 /// @details
815 /// This function destroys the alternative that the @ref error_set contains
816 /// before the call, and constructs a new alternative with the specified
817 /// index in place.
818 ///
819 /// ## Example
820 /// ```cpp
821 /// error_set<error, std::vector<int>> e;
822 ///
823 /// e.emplace<1>({1, 2, 3, 4, 5});
824 ///
825 /// assert(holds_alternative<std::vector<int>>(e));
826 ///
827 /// assert(e.index() == 1);
828 ///
829 /// assert(get<1>(e).size() == 5);
830 /// ```
831 ///
832 /// @param ilist Initializer list forward to the new alternative
833 /// @param args Constructor arguments forwarded to the new alternative
834 /// @return A reference to the new alternative, if applicable
835 template <size_t I, typename U, typename... Args>
836 constexpr
837 #ifndef DOXYGEN
838 typename detail::traits<detail::select_t<I, T...>>::reference
839 #else
840 REFERENCE
841 #endif
842 emplace(std::initializer_list<U> ilist, Args&&... args) {
843 return set_().template emplace<I>(ilist, std::forward<Args>(args)...);
844 }
845
846 /// @brief Constructs a new alternative in place by type
847 ///
848 /// @details
849 /// This function destroy the alternative that the @ref error_set contains
850 /// before the call, and constructs a new alternative with the specified
851 /// type in place.
852 ///
853 /// ## Example
854 /// ```cpp
855 /// error_set<error, std::string> e;
856 ///
857 /// e.emplace<std::string>(5, 'a');
858 ///
859 /// assert(holds_alternative<std::string>(e));
860 ///
861 /// assert(e.index() == 1);
862 ///
863 /// assert(get<1>(e) == "aaaaa");
864 /// ```
865 ///
866 /// @param args Constructor arguments forwarded to the new alternative
867 /// @return A reference to the new alternative, if applicable
868 template <typename U, typename... Args>
869 constexpr
870 #ifndef DOXYGEN
871 typename detail::traits<U>::reference
872 #else
873 REFERENCE
874 #endif
875 emplace(Args&&... args) {
876 return set_().template emplace<U>(std::forward<Args>(args)...);
877 }
878
879 /// @brief Constructs a new alternative in place by type
880 ///
881 /// @details
882 /// This function destroy the alternative that the @ref error_set contains
883 /// before the call, and constructs a new alternative with the specified
884 /// type in place.
885 ///
886 /// ## Example
887 /// ```cpp
888 /// error_set<error, std::vector<int>> e;
889 ///
890 /// e.emplace<std::vector<int>>({1, 2, 3, 4, 5});
891 ///
892 /// assert(holds_alternative<std::vector<int>>(e));
893 ///
894 /// assert(e.index() == 1);
895 ///
896 /// assert(get<1>(e).size() == 5);
897 /// ```
898 ///
899 /// @param ilist Initializer list forward to the new alternative
900 /// @param args Constructor arguments forwarded to the new alternative
901 /// @return A reference to the new alternative, if applicable
902 template <typename U, typename V, typename... Args>
903 constexpr
904 #ifndef DOXYGEN
905 typename detail::traits<U>::reference
906 #else
907 REFERENCE
908 #endif
909 emplace(std::initializer_list<V> ilist, Args&&... args) {
910 return set_().template emplace<U>(ilist, std::forward<Args>(args)...);
911 }
912
913 /// @brief Alternative access operator by index
914 ///
915 /// @details
916 /// This function allows accessing alternatives by index using the square
917 /// bracket operator. Because the index must be a compile time value,
918 /// instead of passing the index directly, the index is provided as an
919 /// instance of @ref index_t.
920 ///
921 /// This operator is unchecked and does not throw an exception. Passing an
922 /// index that does not correspond to the currently contained alternative
923 /// results in undefined behavior.
924 ///
925 /// ## Example
926 /// ```cpp
927 /// error_set<error1, std::string, error2> e{std::in_place_index<1>, "oh no"};
928 ///
929 /// assert(e[index<1>] == "oh no");
930 ///
931 /// v[index<1>] = "no problem";
932 ///
933 /// assert(get<1>(v) == "no problem");
934 /// ```
935 ///
936 /// @param index A tag value that communicates a compile time index
937 /// @return A reference to the accessed alternative, if applicable
938 template <size_t I>
939 [[nodiscard]] constexpr
940 #ifndef DOXYGEN
941 typename detail::traits<detail::select_t<I, T...>>::reference
942 #else
943 REFERENCE
944 #endif
945 operator[]([[maybe_unused]] index_t<I> index) & noexcept {
946 return set_()[index];
947 }
948
949 /// @brief Alternative access operator by index
950 ///
951 /// @details
952 /// This function allows accessing alternatives by index using the square
953 /// bracket operator. Because the index must be a compile time value,
954 /// instead of passing the index directly, the index is provided as an
955 /// instance of @ref index_t.
956 ///
957 /// This operator is unchecked and does not throw an exception. Passing an
958 /// index that does not correspond to the currently contained alternative
959 /// results in undefined behavior.
960 ///
961 /// ## Example
962 /// ```cpp
963 /// const error_set<error1, std::string, error2> e{
964 /// std::in_place_index<1>, "oh no"};
965 ///
966 /// assert(e[index<1>] == "oh no");
967 /// ```
968 ///
969 /// @param index A tag value that communicates a compile time index
970 /// @return A reference to the accessed alternative, if applicable
971 template <size_t I>
972 [[nodiscard]] constexpr
973 #ifndef DOXYGEN
974 typename detail::traits<detail::select_t<I, T...>>::const_reference
975 #else
976 CONST_REFERENCE
977 #endif
978 operator[]([[maybe_unused]] index_t<I> index) const& noexcept {
979 return set_()[index];
980 }
981
982 /// @brief Alternative access operator by index
983 ///
984 /// @details
985 /// This function allows accessing alternatives by index using the square
986 /// bracket operator. Because the index must be a compile time value,
987 /// instead of passing the index directly, the index is provided as an
988 /// instance of @ref index_t.
989 ///
990 /// This operator is unchecked and does not throw an exception. Passing an
991 /// index that does not correspond to the currently contained alternative
992 /// results in undefined behavior.
993 ///
994 /// ## Example
995 /// ```cpp
996 /// error_set<error1, std::string, error2> e{std::in_place_index<1>, "oh no"};
997 ///
998 /// assert(std::move(e)[index<1>] == "oh no");
999 /// ```
1000 ///
1001 /// @param index A tag value that communicates a compile time index
1002 /// @return A reference to the accessed alternative, if applicable
1003 template <size_t I>
1004 [[nodiscard]] constexpr
1005 #ifndef DOXYGEN
1006 typename detail::traits<detail::select_t<I, T...>>::rvalue_reference
1007 #else
1008 RVALUE_REFERENCE
1009 #endif
1010 operator[]([[maybe_unused]] index_t<I> index) && {
1011 return std::move(set_())[index];
1012 }
1013
1014 /// @brief Alternative access operator by index
1015 ///
1016 /// @details
1017 /// This function allows accessing alternatives by index using the square
1018 /// bracket operator. Because the index must be a compile time value,
1019 /// instead of passing the index directly, the index is provided as an
1020 /// instance of @ref index_t.
1021 ///
1022 /// This operator is unchecked and does not throw an exception. Passing an
1023 /// index that does not correspond to the currently contained alternative
1024 /// results in undefined behavior.
1025 ///
1026 /// ## Example
1027 /// ```cpp
1028 /// const error_set<error1, std::string, error2> e{
1029 /// std::in_place_index<1>, "oh no"};
1030 ///
1031 /// assert(std::move(e)[index<1>] == "oh no");
1032 /// ```
1033 ///
1034 /// @param index A tag value that communicates a compile time index
1035 /// @return A reference to the accessed alternative, if applicable
1036 template <size_t I>
1037 [[nodiscard]] constexpr
1038 #ifndef DOXYGEN
1039 typename detail::traits<detail::select_t<I, T...>>::const_rvalue_reference
1040 #else
1041 CONST_RVALUE_REFERENCE
1042 #endif
1043 operator[]([[maybe_unused]] index_t<I> index) const&& {
1044 return std::move(set_())[index];
1045 }
1046
1047 /// @brief Alternative access operator by type
1048 ///
1049 /// @details
1050 /// This function allows accessing alternatives by type using the square
1051 /// bracket operator. Because the index must be a compile time value,
1052 /// the index is provided as an instance of @ref type_t.
1053 ///
1054 /// This operator is unchecked and does not throw an exception. Passing an
1055 /// index that does not correspond to the currently contained alternative
1056 /// results in undefined behavior.
1057 ///
1058 /// ## Example
1059 /// ```cpp
1060 /// error_set<error1, std::string, error2> e{
1061 /// std::in_place_index<1>, "oh no"};
1062 ///
1063 /// assert(e[type<std::string>] == "oh no");
1064 ///
1065 /// v[type<std::string>] = "no problem";
1066 ///
1067 /// assert(get<std::string>(v) == "no problem");
1068 /// ```
1069 ///
1070 /// @param index A tag value that communicates a compile time index
1071 /// @return The accessed alternative value, if applicable
1072 template <typename U>
1073 [[nodiscard]] constexpr
1074 #ifndef DOXYGEN
1075 typename detail::traits<U>::reference
1076 #else
1077 REFERENCE
1078 #endif
1079 operator[]([[maybe_unused]] type_t<U> type) & noexcept {
1080 return set_()[type];
1081 }
1082
1083 /// @brief Alternative access operator by type
1084 ///
1085 /// @details
1086 /// This function allows accessing alternatives by type using the square
1087 /// bracket operator. Because the index must be a compile time value,
1088 /// the index is provided as an instance of @ref type_t.
1089 ///
1090 /// This operator is unchecked and does not throw an exception. Passing an
1091 /// index that does not correspond to the currently contained alternative
1092 /// results in undefined behavior.
1093 ///
1094 /// ## Example
1095 /// ```cpp
1096 /// const error_set<error1, std::string, error2> e{
1097 /// std::in_place_index<1>, "oh no"};
1098 ///
1099 /// assert(e[type<std::string>] == "oh no");
1100 /// ```
1101 ///
1102 /// @param index A tag value that communicates a compile time index
1103 /// @return The accessed alternative value, if applicable
1104 template <typename U>
1105 [[nodiscard]] constexpr
1106 #ifndef DOXYGEN
1107 typename detail::traits<U>::const_reference
1108 #else
1109 CONST_REFERENCE
1110 #endif
1111 operator[]([[maybe_unused]] type_t<U> type) const& noexcept {
1112 return set_()[type];
1113 }
1114
1115 /// @brief Alternative access operator by type
1116 ///
1117 /// @details
1118 /// This function allows accessing alternatives by type using the square
1119 /// bracket operator. Because the index must be a compile time value,
1120 /// the index is provided as an instance of @ref type_t.
1121 ///
1122 /// This operator is unchecked and does not throw an exception. Passing an
1123 /// index that does not correspond to the currently contained alternative
1124 /// results in undefined behavior.
1125 ///
1126 /// ## Example
1127 /// ```cpp
1128 /// error_set<error1, std::string, error2> e{
1129 /// std::in_place_index<1>, "oh no"};
1130 ///
1131 /// assert(std::move(e)[type<std::string>] == "oh no");
1132 /// ```
1133 ///
1134 /// @param index A tag value that communicates a compile time index
1135 /// @return The accessed alternative value, if applicable
1136 template <typename U>
1137 [[nodiscard]] constexpr
1138 #ifndef DOXYGEN
1139 typename detail::traits<U>::rvalue_reference
1140 #else
1141 RVALUE_REFERENCE
1142 #endif
1143 operator[]([[maybe_unused]] type_t<U> type) && {
1144 return std::move(set_())[type];
1145 }
1146
1147 /// @brief Alternative access operator by type
1148 ///
1149 /// @details
1150 /// This function allows accessing alternatives by type using the square
1151 /// bracket operator. Because the index must be a compile time value,
1152 /// the index is provided as an instance of @ref type_t.
1153 ///
1154 /// This operator is unchecked and does not throw an exception. Passing an
1155 /// index that does not correspond to the currently contained alternative
1156 /// results in undefined behavior.
1157 ///
1158 /// ## Example
1159 /// ```cpp
1160 /// const error_set<error1, std::string, error2> e{
1161 /// std::in_place_index<1>, "oh no"};
1162 ///
1163 /// assert(std::move(e)[type<std::string>] == "oh no");
1164 /// ```
1165 ///
1166 /// @param index A tag value that communicates a compile time index
1167 /// @return The accessed alternative value, if applicable
1168 template <typename U>
1169 [[nodiscard]] constexpr
1170 #ifndef DOXYGEN
1171 typename detail::traits<U>::const_rvalue_reference
1172 #else
1173 CONST_RVALUE_REFERENCE
1174 #endif
1175 operator[]([[maybe_unused]] type_t<U> type) const&& {
1176 return std::move(set_())[type];
1177 }
1178
1179 /// @brief Gets an alternative by index
1180 ///
1181 /// @details
1182 /// This function allows accessing alternatives by index, which is provided
1183 /// as a template argument.
1184 ///
1185 /// ## Example
1186 /// ```cpp
1187 /// error_set<error1, std::string, error2> e{
1188 /// std::in_place_index<1>, "oh no"};
1189 ///
1190 /// assert(e.get<1>() == "oh no");
1191 ///
1192 /// e.get<1>() = "no problem";
1193 ///
1194 /// assert(e.get<1>() == "no problem");
1195 /// ```
1196 ///
1197 /// @tparam I The index of the alternative to access.
1198 ///
1199 /// @return A reference to the accessed alternative, if applicable.
1200 ///
1201 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1202 /// the alternative with the corresponding index.
1203 template <size_t I>
1204 [[nodiscard]] constexpr
1205 #ifndef DOXYGEN
1206 typename detail::traits<detail::select_t<I, T...>>::reference
1207 #else
1208 REFERENCE
1209 #endif
1210 12 get() & {
1211 12 return set_().template get<I>();
1212 }
1213
1214 /// @brief Gets an alternative by index
1215 ///
1216 /// @details
1217 /// This function allows accessing alternatives by index, which is provided
1218 /// as a template argument.
1219 ///
1220 /// ## Example
1221 /// ```cpp
1222 /// const error_set<error1, std::string, error2> e{
1223 /// std::in_place_index<1>, "oh no"};
1224 ///
1225 /// assert(e.get<1>() == "oh no");
1226 /// ```
1227 ///
1228 /// @tparam I The index of the alternative to access.
1229 ///
1230 /// @return A const reference to the accessed alternative, if applicable.
1231 ///
1232 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1233 /// the alternative with the corresponding index.
1234 template <size_t I>
1235 [[nodiscard]] constexpr
1236 #ifndef DOXYGEN
1237 typename detail::traits<detail::select_t<I, T...>>::const_reference
1238 #else
1239 CONST_REFERENCE
1240 #endif
1241 8 get() const& {
1242 8 return set_().template get<I>();
1243 }
1244
1245 /// @brief Gets an alternative by index
1246 ///
1247 /// @details
1248 /// This function allows accessing alternatives by index, which is provided
1249 /// as a template argument.
1250 ///
1251 /// ## Example
1252 /// ```cpp
1253 /// error_set<error1, std::string, error2> e{
1254 /// std::in_place_index<1>, "oh no"};
1255 ///
1256 /// assert(std::move(e).get<1>() == "oh no");
1257 /// ```
1258 ///
1259 /// @tparam I The index of the alternative to access.
1260 ///
1261 /// @return An rvalue reference to the accessed alternative, if applicable.
1262 ///
1263 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1264 /// the alternative with the corresponding index.
1265 template <size_t I>
1266 [[nodiscard]] constexpr
1267 #ifndef DOXYGEN
1268 typename detail::traits<detail::select_t<I, T...>>::rvalue_reference
1269 #else
1270 RVALUE_REFERENCE
1271 #endif
1272 get() && {
1273 return std::move(set_()).template get<I>();
1274 }
1275
1276 /// @brief Gets an alternative by index
1277 ///
1278 /// @details
1279 /// This function allows accessing alternatives by index, which is provided
1280 /// as a template argument.
1281 ///
1282 /// ## Example
1283 /// ```cpp
1284 /// const error_set<error1, std::string, error2> e{
1285 /// std::in_place_index<1>, "oh no"};
1286 ///
1287 /// assert(std::move(e).get<1>() == "oh no");
1288 /// ```
1289 ///
1290 /// @tparam I The index of the alternative to access.
1291 ///
1292 /// @return A const rvalue reference to the accessed alternative, if applicable.
1293 ///
1294 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1295 /// the alternative with the corresponding index.
1296 template <size_t I>
1297 [[nodiscard]] constexpr
1298 #ifndef DOXYGEN
1299 typename detail::traits<detail::select_t<I, T...>>::const_rvalue_reference
1300 #else
1301 CONST_RVALUE_REFERENCE
1302 #endif
1303 get() const&& {
1304 return std::move(set_()).template get<I>();
1305 }
1306
1307 /// @brief Gets an alternative by type
1308 ///
1309 /// @details
1310 /// This function allows accessing alternatives by type, which is provided
1311 /// as a template argument.
1312 ///
1313 /// ## Example
1314 /// ```cpp
1315 /// error_set<error1, std::string, error2> e{
1316 /// std::in_place_index<1>, "oh no"};
1317 ///
1318 /// assert(e.get<std::string>() == "oh no");
1319 ///
1320 /// e.get<std::string>() = "no problem";
1321 ///
1322 /// assert(e.get<std::string>() == "no problem");
1323 /// ```
1324 ///
1325 /// @tparam U The type of the alternative to access.
1326 ///
1327 /// @return A reference to the accessed alternative, if applicable.
1328 ///
1329 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1330 /// the alternative with the corresponding type.
1331 template <typename U>
1332 [[nodiscard]] constexpr
1333 #ifndef DOXYGEN
1334 typename detail::traits<U>::reference
1335 #else
1336 REFERENCE
1337 #endif
1338 get() & {
1339 return set_().template get<U>();
1340 }
1341
1342 /// @brief Gets an alternative by type
1343 ///
1344 /// @details
1345 /// This function allows accessing alternatives by type, which is provided
1346 /// as a template argument.
1347 ///
1348 /// ## Example
1349 /// ```cpp
1350 /// const error_set<error1, std::string, error2> e{
1351 /// std::in_place_index<1>, "oh no"};
1352 ///
1353 /// assert(e.get<std::string>() == "oh no");
1354 /// ```
1355 ///
1356 /// @tparam U The type of the alternative to access.
1357 ///
1358 /// @return A const reference to the accessed alternative, if applicable.
1359 ///
1360 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1361 /// the alternative with the corresponding type.
1362 template <typename U>
1363 [[nodiscard]] constexpr
1364 #ifndef DOXYGEN
1365 typename detail::traits<U>::const_reference
1366 #else
1367 CONST_REFERENCE
1368 #endif
1369 get() const& {
1370 return set_().template get<U>();
1371 }
1372
1373 /// @brief Gets an alternative by type
1374 ///
1375 /// @details
1376 /// This function allows accessing alternatives by type, which is provided
1377 /// as a template argument.
1378 ///
1379 /// ## Example
1380 /// ```cpp
1381 /// error_set<error1, std::string, error2> e{
1382 /// std::in_place_index<1>, "oh no"};
1383 ///
1384 /// assert(std::move(e).get<std::string>() == "oh no");
1385 /// ```
1386 ///
1387 /// @tparam U The type of the alternative to access.
1388 ///
1389 /// @return An rvalue reference to the accessed alternative, if applicable.
1390 ///
1391 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1392 /// the alternative with the corresponding type.
1393 template <typename U>
1394 [[nodiscard]] constexpr
1395 #ifndef DOXYGEN
1396 typename detail::traits<U>::rvalue_reference
1397 #else
1398 RVALUE_REFERENCE
1399 #endif
1400 get() && {
1401 return std::move(set_()).template get<U>();
1402 }
1403
1404 /// @brief Gets an alternative by type
1405 ///
1406 /// @details
1407 /// This function allows accessing alternatives by type, which is provided
1408 /// as a template argument.
1409 ///
1410 /// ## Example
1411 /// ```cpp
1412 /// const error_set<error1, std::string, error2> e{
1413 /// std::in_place_index<1>, "oh no"};
1414 ///
1415 /// assert(std::move(e).get<std::string>() == "oh no");
1416 /// ```
1417 ///
1418 /// @tparam U The type of the alternative to access.
1419 ///
1420 /// @return A const rvalue reference to the accessed alternative, if applicable.
1421 ///
1422 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1423 /// the alternative with the corresponding type.
1424 template <typename U>
1425 [[nodiscard]] constexpr
1426 #ifndef DOXYGEN
1427 typename detail::traits<U>::const_rvalue_reference
1428 #else
1429 CONST_RVALUE_REFERENCE
1430 #endif
1431 get() const&& {
1432 return std::move(set_()).template get<U>();
1433 }
1434
1435 /// @brief Gets an alternative pointer by index if the @ref error_set holds it
1436 ///
1437 /// @details
1438 /// This functions tries to access an alternative by index. If the @ref
1439 /// error_set contains the alternative, this function returns a pointer to
1440 /// the alternative, if applicable. If the @ref error_set does not contain
1441 /// the alternative, this function returns null. In the case where the
1442 /// alternative is of type `void`, this function does nothing.
1443 ///
1444 /// ## Example
1445 /// ```cpp
1446 /// error_set<error1, error2, error3> e{std::in_place_index<1>};
1447 ///
1448 /// assert(e.get_if<1>() != nullptr);
1449 ///
1450 /// assert(e.get_if<0>() == nullptr);
1451 /// ```
1452 ///
1453 /// @tparam I The index of the alternative to access.
1454 ///
1455 /// @return A pointer to the accessed alternative, if applicable, or null
1456 template <size_t I>
1457 [[nodiscard]] constexpr
1458 #ifndef DOXYGEN
1459 typename detail::traits<detail::select_t<I, T...>>::pointer
1460 #else
1461 POINTER
1462 #endif
1463 get_if() noexcept {
1464 return set_().template get_if<I>();
1465 }
1466
1467 /// @brief Gets an alternative pointer by index if the @ref error_set holds it
1468 ///
1469 /// @details
1470 /// This functions tries to access an alternative by index. If the @ref
1471 /// error_set contains the alternative, this function returns a pointer to
1472 /// the alternative, if applicable. If the @ref error_set does not contain
1473 /// the alternative, this function returns null. In the case where the
1474 /// alternative is of type `void`, this function does nothing.
1475 ///
1476 /// ## Example
1477 /// ```cpp
1478 /// const error_set<error1, error2, error3> e{std::in_place_index<1>};
1479 ///
1480 /// assert(e.get_if<1>() != nullptr);
1481 ///
1482 /// assert(e.get_if<0>() == nullptr);
1483 /// ```
1484 ///
1485 /// @tparam I The index of the alternative to access.
1486 ///
1487 /// @return A const pointer to the accessed alternative, if applicable, or null
1488 template <size_t I>
1489 [[nodiscard]] constexpr
1490 #ifndef DOXYGEN
1491 typename detail::traits<detail::select_t<I, T...>>::const_pointer
1492 #else
1493 CONST_POINTER
1494 #endif
1495 get_if() const noexcept {
1496 return set_().template get_if<I>();
1497 }
1498
1499 /// @brief Gets an alternative pointer by type if the @ref error_set holds it
1500 ///
1501 /// @details
1502 /// This functions tries to access an alternative by type. If the @ref
1503 /// error_set contains the alternative, this function returns a pointer to
1504 /// the alternative, if applicable. If the @ref error_set does not contain
1505 /// the alternative, this function returns null. In the case where the
1506 /// alternative is of type `void`, this function does nothing.
1507 ///
1508 /// ## Example
1509 /// ```cpp
1510 /// error_set<error1, error2, error3> e{std::in_place_index<1>};
1511 ///
1512 /// assert(e.get_if<error2>() != nullptr);
1513 ///
1514 /// assert(e.get_if<error1>() == nullptr);
1515 /// ```
1516 ///
1517 /// @tparam T The type of the alternative to access.
1518 ///
1519 /// @return A pointer to the accessed alternative, if applicable, or null
1520 template <typename U>
1521 #ifndef DOXYGEN
1522 requires detail::is_unique_v<U, T...>
1523 #endif
1524 [[nodiscard]] constexpr
1525 #ifndef DOXYGEN
1526 typename detail::traits<U>::pointer
1527 #else
1528 POINTER
1529 #endif
1530 get_if() noexcept {
1531 return set_().template get_if<U>();
1532 }
1533
1534 /// @brief Gets an alternative pointer by type if the @ref error_set holds it
1535 ///
1536 /// @details
1537 /// This functions tries to access an alternative by type. If the @ref
1538 /// error_set contains the alternative, this function returns a pointer to
1539 /// the alternative, if applicable. If the @ref error_set does not contain
1540 /// the alternative, this function returns null. In the case where the
1541 /// alternative is of type `void`, this function does nothing.
1542 ///
1543 /// ## Example
1544 /// ```cpp
1545 /// const error_set<error1, error2, error3> e{std::in_place_index<1>};
1546 ///
1547 /// assert(e.get_if<error2>() != nullptr);
1548 ///
1549 /// assert(e.get_if<error1>() == nullptr);
1550 /// ```
1551 ///
1552 /// @tparam T The type of the alternative to access.
1553 ///
1554 /// @return A const pointer to the accessed alternative, if applicable, or null
1555 template <typename U>
1556 [[nodiscard]] constexpr
1557 #ifndef DOXYGEN
1558 typename detail::traits<U>::const_pointer
1559 #else
1560 CONST_POINTER
1561 #endif
1562 get_if() const noexcept {
1563 return set_().template get_if<U>();
1564 }
1565
1566 /// @brief Checks if an @ref error_set contains a particular alternative.
1567 ///
1568 /// @details
1569 /// Given a type parameter, this function checks if the @ref error_set currently
1570 /// holds an alternative that has the exact same type.
1571 ///
1572 /// ## Example
1573 /// ```cpp
1574 /// error_set<error1, error2, error3> e1{std::in_place_index<0>};
1575 ///
1576 /// assert(e1.holds_alternative<error1>());
1577 ///
1578 /// error_set<error1, error2, error3> e2{std::in_place_index<2>};
1579 ///
1580 /// assert(e2.holds_alternative<error1>());
1581 /// ```
1582 ///
1583 /// @return `true` if the @ref error_set holds an alternative of the given type.
1584 template <typename U>
1585 14 [[nodiscard]] constexpr bool holds_alternative() const noexcept {
1586 14 return set_().template holds_alternative<U>();
1587 }
1588
1589 /// @brief Calls a visitor callable with the contained alternative
1590 ///
1591 /// @details
1592 /// This function calls the visitor as `std::invoke(visitor, alternative)`
1593 /// and returns the result of that call, if any. As such, `visitor` *must*
1594 /// be able to accecpt any alternative type as an argument. In the case of
1595 /// an alternative of type `void`, the visitor must be callable as
1596 /// `std::invoke(visitor, void_v)`.
1597 ///
1598 /// Note that the @ref overload function can be helpful for defining a
1599 /// visitor inline.
1600 ///
1601 /// Also note that this function is implemented as a compile-time-defined
1602 /// jump table (array of function pointers). In performance critical
1603 /// applications, be wary of any assumptions about how well or poorly your
1604 /// compiler will optimize a call to this function.
1605 ///
1606 /// ## Example
1607 /// ```cpp
1608 /// error_set<error1, error2, error3> e1{std::in_place_index<1>};
1609 ///
1610 /// e1.visit(overload(
1611 /// [](error1& e) { assert(false); },
1612 /// [](error2& e) { assert(true); },
1613 /// [](error3& e) { assert(false); }
1614 /// ));
1615 /// ```
1616 ///
1617 /// @param visitor The callable object that will be passed an alternative.
1618 /// @return The return value of the visitor, if any.
1619 template <typename V>
1620 constexpr
1621 #ifndef DOXYGEN
1622 detail::invoke_result_t<
1623 V&&,
1624 typename detail::traits<detail::select_t<0, T...>>::reference>
1625 #else
1626 DEDUCED
1627 #endif
1628 4 visit(V&& visitor) & {
1629 4 return set_().visit(std::forward<V>(visitor));
1630 }
1631
1632 /// @brief Calls a visitor callable with the contained alternative
1633 ///
1634 /// @details
1635 /// This function calls the visitor as `std::invoke(visitor, alternative)`
1636 /// and returns the result of that call, if any. As such, `visitor` *must*
1637 /// be able to accecpt any alternative type as an argument. In the case of
1638 /// an alternative of type `void`, the visitor must be callable as
1639 /// `std::invoke(visitor, void_v)`.
1640 ///
1641 /// Note that the @ref overload function can be helpful for defining a
1642 /// visitor inline.
1643 ///
1644 /// Also note that this function is implemented as a compile-time-defined
1645 /// jump table (array of function pointers). In performance critical
1646 /// applications, be wary of any assumptions about how well or poorly your
1647 /// compiler will optimize a call to this function.
1648 ///
1649 /// ## Example
1650 /// ```cpp
1651 /// const error_set<error1, error2, error3> e1{std::in_place_index<1>};
1652 ///
1653 /// e1.visit(overload(
1654 /// [](const error1& e) { assert(false); },
1655 /// [](const error2& e) { assert(true); },
1656 /// [](const error3& e) { assert(false); }
1657 /// ));
1658 /// ```
1659 ///
1660 /// @param visitor The callable object that will be passed an alternative.
1661 /// @return The return value of the visitor, if any.
1662 template <typename V>
1663 constexpr
1664 #ifndef DOXYGEN
1665 detail::invoke_result_t<
1666 V&&,
1667 typename detail::traits<detail::select_t<0, T...>>::const_reference>
1668 #else
1669 DEDUCED
1670 #endif
1671 visit(V&& visitor) const& {
1672 return set_().visit(std::forward<V>(visitor));
1673 }
1674
1675 /// @brief Calls a visitor callable with the contained alternative
1676 ///
1677 /// @details
1678 /// This function calls the visitor as `std::invoke(visitor, alternative)`
1679 /// and returns the result of that call, if any. As such, `visitor` *must*
1680 /// be able to accecpt any alternative type as an argument. In the case of
1681 /// an alternative of type `void`, the visitor must be callable as
1682 /// `std::invoke(visitor, void_v)`.
1683 ///
1684 /// Note that the @ref overload function can be helpful for defining a
1685 /// visitor inline.
1686 ///
1687 /// Also note that this function is implemented as a compile-time-defined
1688 /// jump table (array of function pointers). In performance critical
1689 /// applications, be wary of any assumptions about how well or poorly your
1690 /// compiler will optimize a call to this function.
1691 ///
1692 /// ## Example
1693 /// ```cpp
1694 /// error_set<error1, error2, error3> e1{std::in_place_index<1>};
1695 ///
1696 /// std::move(e1).visit(overload(
1697 /// [](error1&& e) { assert(false); },
1698 /// [](error2&& e) { assert(true); },
1699 /// [](error3&& e) { assert(false); }
1700 /// ));
1701 /// ```
1702 ///
1703 /// @param visitor The callable object that will be passed an alternative.
1704 /// @return The return value of the visitor, if any.
1705 template <typename V>
1706 constexpr
1707 #ifndef DOXYGEN
1708 detail::invoke_result_t<
1709 V&&,
1710 typename detail::traits<detail::select_t<0, T...>>::rvalue_reference>
1711 #else
1712 DEDUCED
1713 #endif
1714 visit(V&& visitor) && {
1715 return std::move(set_()).visit(std::forward<V>(visitor));
1716 }
1717
1718 /// @brief Calls a visitor callable with the contained alternative
1719 ///
1720 /// @details
1721 /// This function calls the visitor as `std::invoke(visitor, alternative)`
1722 /// and returns the result of that call, if any. As such, `visitor` *must*
1723 /// be able to accecpt any alternative type as an argument. In the case of
1724 /// an alternative of type `void`, the visitor must be callable as
1725 /// `std::invoke(visitor, void_v)`.
1726 ///
1727 /// Note that the @ref overload function can be helpful for defining a
1728 /// visitor inline.
1729 ///
1730 /// Also note that this function is implemented as a compile-time-defined
1731 /// jump table (array of function pointers). In performance critical
1732 /// applications, be wary of any assumptions about how well or poorly your
1733 /// compiler will optimize a call to this function.
1734 ///
1735 /// ## Example
1736 /// ```cpp
1737 /// const error_set<error1, error2, error3> e1{std::in_place_index<1>};
1738 ///
1739 /// std::move(e1).visit(overload(
1740 /// [](const error1&& e) { assert(false); },
1741 /// [](const error2&& e) { assert(true); },
1742 /// [](const error3&& e) { assert(false); }
1743 /// ));
1744 /// ```
1745 ///
1746 /// @param visitor The callable object that will be passed an alternative.
1747 /// @return The return value of the visitor, if any.
1748 template <typename V>
1749 constexpr
1750 #ifndef DOXYGEN
1751 detail::invoke_result_t<
1752 V&&,
1753 typename detail::traits<detail::select_t<0, T...>>::const_rvalue_reference>
1754 #else
1755 DEDUCED
1756 #endif
1757 visit(V&& visitor) const&& {
1758 return std::move(set_()).visit(std::forward<V>(visitor));
1759 }
1760
1761 /// @brief Calls a visitor callable with the contained alternative and metadata
1762 ///
1763 /// @details
1764 /// This function calls the visitor as `std::invoke(visitor, alternative, info)`,
1765 /// and returns the result of that call, if any. As such, `visitor` *must* be
1766 /// able to accept any alternative type as an argument. In the case of an
1767 /// alternative of type `void`, the visitor must be callable as
1768 /// `std::invoke(visitor, void_v, info)`.
1769 ///
1770 /// The `info` argument passed to the visitor, which differentiates this
1771 /// function from `.visit(...)`, communicates `constexpr` information about
1772 /// the alternative being visited. The type of the `info` object is not
1773 /// meant to be named, but it has the API shown below. Note that `info` is
1774 /// always an empty type.
1775 ///
1776 /// ```
1777 /// struct alternative_info {
1778 /// // index of the alternative in the source error_set
1779 /// static inline constexpr size_t index = ...;
1780 ///
1781 /// // type of the alternative as declared in the source error_set
1782 /// using type = ...;
1783 ///
1784 /// // helper function for forwarding non-const alternative values
1785 /// // without needing to provide a template argument.
1786 /// static constexpr decltype(auto) forward(...);
1787 /// };
1788 /// ```
1789 ///
1790 /// Note that the @ref overload function can be helpful for defining a
1791 /// visitor inline.
1792 ///
1793 /// Also note that this function is implemented as a compile-time-defined
1794 /// jump table (array of function pointers). In performance critical
1795 /// applications, be wary of any assumptions about how well or poorly your
1796 /// compiler will optimize a call to this function.
1797 ///
1798 /// ## Example
1799 /// ```
1800 /// error_set<error1, error2, error3> e1{std::in_place_index<1>};
1801 ///
1802 /// e1.visit_informed([](auto& value, auto info) {
1803 /// if constexpr (info.index == 0) {
1804 /// assert(false);
1805 /// } else if constexpr (info.index == 1) {
1806 /// assert(true);
1807 /// } else if constexpr (info.index == 2) {
1808 /// assert(false);
1809 /// }
1810 /// });
1811 /// ```
1812 ///
1813 /// @param visitor The callable object that will be passed an alternative.
1814 /// @return The return value of the visitor, if any.
1815 template <typename V>
1816 constexpr
1817 #ifndef DOXYGEN
1818 detail::invoke_result_t<
1819 V&&,
1820 typename detail::traits<detail::select_t<0, T...>>::reference,
1821 detail::alternative_info<0, variant<T...>>>
1822 #else
1823 DEDUCED
1824 #endif
1825 visit_informed(V&& visitor) & {
1826 return set_().visit_informed(std::forward<V>(visitor));
1827 }
1828
1829 /// @brief Calls a visitor callable with the contained alternative and metadata
1830 ///
1831 /// @details
1832 /// This function calls the visitor as `std::invoke(visitor, alternative, info)`,
1833 /// and returns the result of that call, if any. As such, `visitor` *must* be
1834 /// able to accept any alternative type as an argument. In the case of an
1835 /// alternative of type `void`, the visitor must be callable as
1836 /// `std::invoke(visitor, void_v, info)`.
1837 ///
1838 /// The `info` argument passed to the visitor, which differentiates this
1839 /// function from `.visit(...)`, communicates `constexpr` information about
1840 /// the alternative being visited. The type of the `info` object is not
1841 /// meant to be named, but it has the API shown below. Note that `info` is
1842 /// always an empty type.
1843 ///
1844 /// ```
1845 /// struct alternative_info {
1846 /// // index of the alternative in the source error_set
1847 /// static inline constexpr size_t index = ...;
1848 ///
1849 /// // type of the alternative as declared in the source error_set
1850 /// using type = ...;
1851 ///
1852 /// // helper function for forwarding non-const alternative values
1853 /// // without needing to provide a template argument.
1854 /// static constexpr decltype(auto) forward(...);
1855 /// };
1856 /// ```
1857 ///
1858 /// Note that the @ref overload function can be helpful for defining a
1859 /// visitor inline.
1860 ///
1861 /// Also note that this function is implemented as a compile-time-defined
1862 /// jump table (array of function pointers). In performance critical
1863 /// applications, be wary of any assumptions about how well or poorly your
1864 /// compiler will optimize a call to this function.
1865 ///
1866 /// ## Example
1867 /// ```
1868 /// const error_set<error1, error2, error3> e1{std::in_place_index<1>};
1869 ///
1870 /// e1.visit_informed([](const auto& value, auto info) {
1871 /// if constexpr (info.index == 0) {
1872 /// assert(false);
1873 /// } else if constexpr (info.index == 1) {
1874 /// assert(true);
1875 /// } else if constexpr (info.index == 2) {
1876 /// assert(false);
1877 /// }
1878 /// });
1879 /// ```
1880 ///
1881 /// @param visitor The callable object that will be passed an alternative.
1882 /// @return The return value of the visitor, if any.
1883 template <typename V>
1884 constexpr
1885 #ifndef DOXYGEN
1886 detail::invoke_result_t<
1887 V&&,
1888 typename detail::traits<detail::select_t<0, T...>>::const_reference,
1889 detail::alternative_info<0, variant<T...>>>
1890 #else
1891 DEDUCED
1892 #endif
1893 visit_informed(V&& visitor) const& {
1894 return set_().visit_informed(std::forward<V>(visitor));
1895 }
1896
1897 /// @brief Calls a visitor callable with the contained alternative and metadata
1898 ///
1899 /// @details
1900 /// This function calls the visitor as `std::invoke(visitor, alternative, info)`,
1901 /// and returns the result of that call, if any. As such, `visitor` *must* be
1902 /// able to accept any alternative type as an argument. In the case of an
1903 /// alternative of type `void`, the visitor must be callable as
1904 /// `std::invoke(visitor, void_v, info)`.
1905 ///
1906 /// The `info` argument passed to the visitor, which differentiates this
1907 /// function from `.visit(...)`, communicates `constexpr` information about
1908 /// the alternative being visited. The type of the `info` object is not
1909 /// meant to be named, but it has the API shown below. Note that `info` is
1910 /// always an empty type.
1911 ///
1912 /// ```
1913 /// struct alternative_info {
1914 /// // index of the alternative in the source error_set
1915 /// static inline constexpr size_t index = ...;
1916 ///
1917 /// // type of the alternative as declared in the source error_set
1918 /// using type = ...;
1919 ///
1920 /// // helper function for forwarding non-const alternative values
1921 /// // without needing to provide a template argument.
1922 /// static constexpr decltype(auto) forward(...);
1923 /// };
1924 /// ```
1925 ///
1926 /// Note that the @ref overload function can be helpful for defining a
1927 /// visitor inline.
1928 ///
1929 /// Also note that this function is implemented as a compile-time-defined
1930 /// jump table (array of function pointers). In performance critical
1931 /// applications, be wary of any assumptions about how well or poorly your
1932 /// compiler will optimize a call to this function.
1933 ///
1934 /// ## Example
1935 /// ```
1936 /// error_set<error1, error2, error3> e1{std::in_place_index<1>};
1937 ///
1938 /// std::move(e1).visit_informed([](auto&& value, auto info) {
1939 /// if constexpr (info.index == 0) {
1940 /// assert(false);
1941 /// } else if constexpr (info.index == 1) {
1942 /// assert(true);
1943 /// } else if constexpr (info.index == 2) {
1944 /// assert(false);
1945 /// }
1946 /// });
1947 /// ```
1948 ///
1949 /// @param visitor The callable object that will be passed an alternative.
1950 /// @return The return value of the visitor, if any.
1951 template <typename V>
1952 constexpr
1953 #ifndef DOXYGEN
1954 detail::invoke_result_t<
1955 V&&,
1956 typename detail::traits<detail::select_t<0, T...>>::rvalue_reference,
1957 detail::alternative_info<0, variant<T...>>>
1958 #else
1959 DEDUCED
1960 #endif
1961 visit_informed(V&& visitor) && {
1962 return std::move(set_()).visit_informed(std::forward<V>(visitor));
1963 }
1964
1965 /// @brief Calls a visitor callable with the contained alternative and metadata
1966 ///
1967 /// @details
1968 /// This function calls the visitor as `std::invoke(visitor, alternative, info)`,
1969 /// and returns the result of that call, if any. As such, `visitor` *must* be
1970 /// able to accept any alternative type as an argument. In the case of an
1971 /// alternative of type `void`, the visitor must be callable as
1972 /// `std::invoke(visitor, void_v, info)`.
1973 ///
1974 /// The `info` argument passed to the visitor, which differentiates this
1975 /// function from `.visit(...)`, communicates `constexpr` information about
1976 /// the alternative being visited. The type of the `info` object is not
1977 /// meant to be named, but it has the API shown below. Note that `info` is
1978 /// always an empty type.
1979 ///
1980 /// ```
1981 /// struct alternative_info {
1982 /// // index of the alternative in the source error_set
1983 /// static inline constexpr size_t index = ...;
1984 ///
1985 /// // type of the alternative as declared in the source error_set
1986 /// using type = ...;
1987 ///
1988 /// // helper function for forwarding non-const alternative values
1989 /// // without needing to provide a template argument.
1990 /// static constexpr decltype(auto) forward(...);
1991 /// };
1992 /// ```
1993 ///
1994 /// Note that the @ref overload function can be helpful for defining a
1995 /// visitor inline.
1996 ///
1997 /// Also note that this function is implemented as a compile-time-defined
1998 /// jump table (array of function pointers). In performance critical
1999 /// applications, be wary of any assumptions about how well or poorly your
2000 /// compiler will optimize a call to this function.
2001 ///
2002 /// ## Example
2003 /// ```
2004 /// const error_set<error1, error2, error3> e1{std::in_place_index<1>};
2005 ///
2006 /// std::move(e1).visit_informed([](const auto&& value, auto info) {
2007 /// if constexpr (info.index == 0) {
2008 /// assert(false);
2009 /// } else if constexpr (info.index == 1) {
2010 /// assert(true);
2011 /// } else if constexpr (info.index == 2) {
2012 /// assert(false);
2013 /// }
2014 /// });
2015 /// ```
2016 ///
2017 /// @param visitor The callable object that will be passed an alternative.
2018 /// @return The return value of the visitor, if any.
2019 template <typename V>
2020 constexpr
2021 #ifndef DOXYGEN
2022 detail::invoke_result_t<
2023 V&&,
2024 typename detail::traits<detail::select_t<0, T...>>::const_rvalue_reference,
2025 detail::alternative_info<0, variant<T...>>>
2026 #else
2027 DEDUCED
2028 #endif
2029 visit_informed(V&& visitor) const&& {
2030 return std::move(set_()).visit_informed(std::forward<V>(visitor));
2031 }
2032
2033 /// @brief Swaps two @ref error_set instances
2034 ///
2035 /// @details
2036 /// If the two @ref error_set instances contain the same alternative, the
2037 /// alternative values are swapped directly. Otherwise, the alternatives
2038 /// are swapped by moving out of the variants, destroying the old
2039 /// alternatives, and move constructed into the new alternatives.
2040 ///
2041 /// ## Example
2042 /// ```cpp
2043 /// error_set<error1, error2, error3> e1{std::in_place_index<0>};
2044 ///
2045 /// error_set<error1, error2, error3> e2{std::in_place_index<1>};
2046 ///
2047 /// e1.swap(e2);
2048 ///
2049 /// assert(e1.index() == 1);
2050 ///
2051 /// assert(e2.index() == 0);
2052 /// ```
2053 ///
2054 /// @param other The "other" @ref error_set to swap with this @ref error_set
2055 constexpr void swap(error_set& other)
2056 #ifndef DOXYGEN
2057 noexcept(noexcept(set_().swap(other.set_())))
2058 #else
2059 CONDITIONALLY_NOEXCEPT
2060 #endif
2061 {
2062 set_().swap(other.set_());
2063 }
2064 };
2065
2066 /// @relates error_set
2067 /// @brief Checks if an @ref error_set contains a particular alternative.
2068 ///
2069 /// @details
2070 /// Given a type parameter, this function checks if the @ref error_set currently
2071 /// holds an alternative that has the exact same type.
2072 ///
2073 /// ## Example
2074 /// ```cpp
2075 /// error_set<error1, error2, error3> e1{std::in_place_index<0>};
2076 ///
2077 /// assert(holds_alternative<error1>(e1));
2078 ///
2079 /// error_set<error1, error2, error3> e2{std::in_place_index<2>};
2080 ///
2081 /// assert(holds_alternative<error1>(e2));
2082 /// ```
2083 ///
2084 /// @return `true` if the @ref error_set holds an alternative of the given type.
2085 template <typename T, typename... U>
2086 14 constexpr bool holds_alternative(const error_set<U...>& e) noexcept {
2087 14 return e.template holds_alternative<T>();
2088 }
2089
2090 /// @relates error_set
2091 /// @brief Gets an alternative by index
2092 ///
2093 /// @details
2094 /// This function allows accessing alternatives by index, which is provided
2095 /// as a template argument.
2096 ///
2097 /// ## Example
2098 /// ```cpp
2099 /// error_set<error1, std::string, error2> e{
2100 /// std::in_place_index<1>, "oh no"};
2101 ///
2102 /// assert(get<1>(e) == "oh no");
2103 ///
2104 /// get<1>(e) = "no problem";
2105 ///
2106 /// assert(get<1>(e) == "no problem");
2107 /// ```
2108 ///
2109 /// @tparam I The index of the alternative to access.
2110 ///
2111 /// @return A reference to the accessed alternative, if applicable.
2112 ///
2113 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2114 /// the alternative with the corresponding index.
2115 template <size_t I, typename... T>
2116 constexpr
2117 #ifndef DOXYGEN
2118 typename detail::traits<detail::select_t<I, T...>>::reference
2119 #else
2120 REFERENCE
2121 #endif
2122 12 get(error_set<T...>& e) {
2123 12 return e.template get<I>();
2124 }
2125
2126 /// @relates error_set
2127 /// @brief Gets an alternative by index
2128 ///
2129 /// @details
2130 /// This function allows accessing alternatives by index, which is provided
2131 /// as a template argument.
2132 ///
2133 /// ## Example
2134 /// ```cpp
2135 /// const error_set<error1, std::string, error2> e{
2136 /// std::in_place_index<1>, "oh no"};
2137 ///
2138 /// assert(get<1>(e) == "oh no");
2139 /// ```
2140 ///
2141 /// @tparam I The index of the alternative to access.
2142 ///
2143 /// @return A const reference to the accessed alternative, if applicable.
2144 ///
2145 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2146 /// the alternative with the corresponding index.
2147 template <size_t I, typename... T>
2148 constexpr
2149 #ifndef DOXYGEN
2150 typename detail::traits<detail::select_t<I, T...>>::const_reference
2151 #else
2152 CONST_REFERENCE
2153 #endif
2154 4 get(const error_set<T...>& e) {
2155 4 return e.template get<I>();
2156 }
2157
2158 /// @relates error_set
2159 /// @brief Gets an alternative by index
2160 ///
2161 /// @details
2162 /// This function allows accessing alternatives by index, which is provided
2163 /// as a template argument.
2164 ///
2165 /// ## Example
2166 /// ```cpp
2167 /// error_set<error1, std::string, error2> e{
2168 /// std::in_place_index<1>, "oh no"};
2169 ///
2170 /// assert(get<1>(std::move(e)) == "oh no");
2171 /// ```
2172 ///
2173 /// @tparam I The index of the alternative to access.
2174 ///
2175 /// @return An rvalue reference to the accessed alternative, if applicable.
2176 ///
2177 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2178 /// the alternative with the corresponding index.
2179 template <size_t I, typename... T>
2180 constexpr
2181 #ifndef DOXYGEN
2182 typename detail::traits<detail::select_t<I, T...>>::rvalue_reference
2183 #else
2184 RVALUE_REFERENCE
2185 #endif
2186 get(error_set<T...>&& e) {
2187 return std::move(e).template get<I>();
2188 }
2189
2190 /// @relates error_set
2191 /// @brief Gets an alternative by index
2192 ///
2193 /// @details
2194 /// This function allows accessing alternatives by index, which is provided
2195 /// as a template argument.
2196 ///
2197 /// ## Example
2198 /// ```cpp
2199 /// const error_set<error1, std::string, error2> e{
2200 /// std::in_place_index<1>, "oh no"};
2201 ///
2202 /// assert(get<1>(std::move(e)) == "oh no");
2203 /// ```
2204 ///
2205 /// @tparam I The index of the alternative to access.
2206 ///
2207 /// @return A const rvalue reference to the accessed alternative, if applicable.
2208 ///
2209 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2210 /// the alternative with the corresponding index.
2211 template <size_t I, typename... T>
2212 constexpr
2213 #ifndef DOXYGEN
2214 typename detail::traits<detail::select_t<I, T...>>::const_rvalue_reference
2215 #else
2216 CONST_RVALUE_REFERENCE
2217 #endif
2218 get(const error_set<T...>&& e) {
2219 return std::move(e).template get<I>();
2220 }
2221
2222 /// @relates error_set
2223 /// @brief Gets an alternative by type
2224 ///
2225 /// @details
2226 /// This function allows accessing alternatives by type, which is provided
2227 /// as a template argument.
2228 ///
2229 /// ## Example
2230 /// ```cpp
2231 /// error_set<error1, std::string, error2> e{
2232 /// std::in_place_index<1>, "oh no"};
2233 ///
2234 /// assert(get<std::string>(e) == "oh no");
2235 ///
2236 /// get<std::string>(e) = "no problem";
2237 ///
2238 /// assert(get<std::string>(e) == "no problem");
2239 /// ```
2240 ///
2241 /// @tparam U The type of the alternative to access.
2242 ///
2243 /// @return A reference to the accessed alternative, if applicable.
2244 ///
2245 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2246 /// the alternative with the corresponding type.
2247 template <typename T, typename... U>
2248 constexpr
2249 #ifndef DOXYGEN
2250 typename detail::traits<T>::reference
2251 #else
2252 REFERENCE
2253 #endif
2254 get(error_set<U...>& e) {
2255 return e.template get<T>();
2256 }
2257
2258 /// @relates error_set
2259 /// @brief Gets an alternative by type
2260 ///
2261 /// @details
2262 /// This function allows accessing alternatives by type, which is provided
2263 /// as a template argument.
2264 ///
2265 /// ## Example
2266 /// ```cpp
2267 /// const error_set<error1, std::string, error2> e{
2268 /// std::in_place_index<1>, "oh no"};
2269 ///
2270 /// assert(get<std::string>(e) == "oh no");
2271 /// ```
2272 ///
2273 /// @tparam U The type of the alternative to access.
2274 ///
2275 /// @return A const reference to the accessed alternative, if applicable.
2276 ///
2277 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2278 /// the alternative with the corresponding type.
2279 template <typename T, typename... U>
2280 constexpr
2281 #ifndef DOXYGEN
2282 typename detail::traits<T>::const_reference
2283 #else
2284 CONST_REFERENCE
2285 #endif
2286 get(const error_set<U...>& e) {
2287 return e.template get<T>();
2288 }
2289
2290 /// @relates error_set
2291 /// @brief Gets an alternative by type
2292 ///
2293 /// @details
2294 /// This function allows accessing alternatives by type, which is provided
2295 /// as a template argument.
2296 ///
2297 /// ## Example
2298 /// ```cpp
2299 /// error_set<error1, std::string, error2> e{
2300 /// std::in_place_index<1>, "oh no"};
2301 ///
2302 /// assert(get<std::string>(std::move(e)) == "oh no");
2303 /// ```
2304 ///
2305 /// @tparam U The type of the alternative to access.
2306 ///
2307 /// @return An rvalue reference to the accessed alternative, if applicable.
2308 ///
2309 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2310 /// the alternative with the corresponding type.
2311 template <typename T, typename... U>
2312 constexpr
2313 #ifndef DOXYGEN
2314 typename detail::traits<T>::rvalue_reference
2315 #else
2316 RVALUE_REFERENCE
2317 #endif
2318 get(error_set<U...>&& e) {
2319 return std::move(e).template get<T>();
2320 }
2321
2322 /// @relates error_set
2323 /// @brief Gets an alternative by type
2324 ///
2325 /// @details
2326 /// This function allows accessing alternatives by type, which is provided
2327 /// as a template argument.
2328 ///
2329 /// ## Example
2330 /// ```cpp
2331 /// const error_set<error1, std::string, error2> e{
2332 /// std::in_place_index<1>, "oh no"};
2333 ///
2334 /// assert(std::move(e).get<std::string>() == "oh no");
2335 /// ```
2336 ///
2337 /// @tparam U The type of the alternative to access.
2338 ///
2339 /// @return A const rvalue reference to the accessed alternative, if applicable.
2340 ///
2341 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2342 /// the alternative with the corresponding type.
2343 template <typename T, typename... U>
2344 constexpr
2345 #ifndef DOXYGEN
2346 typename detail::traits<T>::const_rvalue_reference
2347 #else
2348 CONST_RVALUE_REFERENCE
2349 #endif
2350 get(const error_set<U...>&& e) {
2351 return std::move(e).template get<T>();
2352 }
2353
2354 /// @relates error_set
2355 /// @brief Gets an alternative pointer by index if the @ref error_set holds it
2356 ///
2357 /// @details
2358 /// This functions tries to access an alternative by index. If the @ref
2359 /// error_set contains the alternative, this function returns a pointer to
2360 /// the alternative, if applicable. If the @ref error_set does not contain
2361 /// the alternative, this function returns null. In the case where the
2362 /// alternative is of type `void`, this function does nothing.
2363 ///
2364 /// ## Example
2365 /// ```cpp
2366 /// error_set<error1, error2, error3> e{std::in_place_index<1>};
2367 ///
2368 /// assert(get_if<1>(e) != nullptr);
2369 ///
2370 /// assert(get_if<0>(e) == nullptr);
2371 /// ```
2372 ///
2373 /// @tparam I The index of the alternative to access.
2374 ///
2375 /// @return A pointer to the accessed alternative, if applicable, or null
2376 template <size_t I, typename... T>
2377 constexpr
2378 #ifndef DOXYGEN
2379 typename detail::traits<detail::select_t<I, T...>>::pointer
2380 #else
2381 POINTER
2382 #endif
2383 get_if(error_set<T...>& e) noexcept {
2384 return e.template get_if<I>();
2385 }
2386
2387 /// @relates error_set
2388 /// @brief Gets an alternative pointer by index if the @ref error_set holds it
2389 ///
2390 /// @details
2391 /// This functions tries to access an alternative by index. If the @ref
2392 /// error_set contains the alternative, this function returns a pointer to
2393 /// the alternative, if applicable. If the @ref error_set does not contain
2394 /// the alternative, this function returns null. In the case where the
2395 /// alternative is of type `void`, this function does nothing.
2396 ///
2397 /// ## Example
2398 /// ```cpp
2399 /// const error_set<error1, error2, error3> e{std::in_place_index<1>};
2400 ///
2401 /// assert(get_if<1>(e) != nullptr);
2402 ///
2403 /// assert(get_if<0>(e) == nullptr);
2404 /// ```
2405 ///
2406 /// @tparam I The index of the alternative to access.
2407 ///
2408 /// @return A const pointer to the accessed alternative, if applicable, or null
2409 template <size_t I, typename... T>
2410 constexpr
2411 #ifndef DOXYGEN
2412 typename detail::traits<detail::select_t<I, T...>>::const_pointer
2413 #else
2414 CONST_POINTER
2415 #endif
2416 get_if(const error_set<T...>& e) noexcept {
2417 return e.template get_if<I>();
2418 }
2419
2420 /// @relates error_set
2421 /// @brief Gets an alternative pointer by type if the @ref error_set holds it
2422 ///
2423 /// @details
2424 /// This functions tries to access an alternative by type. If the @ref
2425 /// error_set contains the alternative, this function returns a pointer to
2426 /// the alternative, if applicable. If the @ref error_set does not contain
2427 /// the alternative, this function returns null. In the case where the
2428 /// alternative is of type `void`, this function does nothing.
2429 ///
2430 /// ## Example
2431 /// ```cpp
2432 /// error_set<error1, error2, error3> e{std::in_place_index<1>};
2433 ///
2434 /// assert(get_if<error2>(e) != nullptr);
2435 ///
2436 /// assert(get_if<error1>(e) == nullptr);
2437 /// ```
2438 ///
2439 /// @tparam T The type of the alternative to access.
2440 ///
2441 /// @return A pointer to the accessed alternative, if applicable, or null
2442 template <typename T, typename... U>
2443 constexpr
2444 #ifndef DOXYGEN
2445 typename detail::traits<T>::pointer
2446 #else
2447 POINTER
2448 #endif
2449 get_if(error_set<U...>& e) noexcept {
2450 return e.template get_if<T>();
2451 }
2452
2453 /// @relates error_set
2454 /// @brief Gets an alternative pointer by type if the @ref error_set holds it
2455 ///
2456 /// @details
2457 /// This functions tries to access an alternative by type. If the @ref
2458 /// error_set contains the alternative, this function returns a pointer to
2459 /// the alternative, if applicable. If the @ref error_set does not contain
2460 /// the alternative, this function returns null. In the case where the
2461 /// alternative is of type `void`, this function does nothing.
2462 ///
2463 /// ## Example
2464 /// ```cpp
2465 /// const error_set<error1, error2, error3> e{std::in_place_index<1>};
2466 ///
2467 /// assert(get_if<error2>(e) != nullptr);
2468 ///
2469 /// assert(get_if<error1>(e) == nullptr);
2470 /// ```
2471 ///
2472 /// @tparam T The type of the alternative to access.
2473 ///
2474 /// @return A const pointer to the accessed alternative, if applicable, or null
2475 template <typename T, typename... U>
2476 constexpr
2477 #ifndef DOXYGEN
2478 typename detail::traits<T>::const_pointer
2479 #else
2480 CONST_POINTER
2481 #endif
2482 get_if(const error_set<U...>& e) noexcept {
2483 return e.template get_if<T>();
2484 }
2485
2486 /// @relates error_set
2487 /// @brief Swaps two @ref error_set instances
2488 ///
2489 /// @details
2490 /// If the two @ref error_set instances contain the same alternative, the
2491 /// alternative values are swapped directly. Otherwise, the alternatives
2492 /// are swapped by moving out of the variants, destroying the old
2493 /// alternatives, and move constructed into the new alternatives.
2494 ///
2495 /// ## Example
2496 /// ```cpp
2497 /// error_set<error1, error2, error3> e1{std::in_place_index<0>};
2498 ///
2499 /// error_set<error1, error2, error3> e2{std::in_place_index<1>};
2500 ///
2501 /// swap(e1, e2);
2502 ///
2503 /// assert(e1.index() == 1);
2504 ///
2505 /// assert(e2.index() == 0);
2506 /// ```
2507 ///
2508 /// @param other The "other" @ref error_set to swap with this @ref error_set
2509 template <typename... T>
2510 constexpr void swap(error_set<T...>& a, error_set<T...>& b)
2511 #ifndef DOXYGEN
2512 noexcept(noexcept(a.swap(b)))
2513 #else
2514 CONDITIONALLY_NOEXCEPT
2515 #endif
2516 {
2517 a.swap(b);
2518 }
2519
2520 namespace detail {
2521
2522 template <typename T>
2523 struct error_set_size_helper;
2524
2525 template <typename... T>
2526 struct error_set_size_helper<error_set<T...>>
2527 : std::integral_constant<size_t, sizeof...(T)> {};
2528
2529 template <typename... T>
2530 struct error_set_size_helper<const error_set<T...>>
2531 : std::integral_constant<size_t, sizeof...(T)> {};
2532
2533 template <size_t I, typename T>
2534 struct error_set_alternative_helper;
2535
2536 template <size_t I, typename... T>
2537 struct error_set_alternative_helper<I, error_set<T...>> {
2538 using type = detail::select_t<I, T...>;
2539 };
2540
2541 template <size_t I, typename... T>
2542 struct error_set_alternative_helper<I, const error_set<T...>>
2543 : error_set_alternative_helper<I, error_set<T...>> {};
2544
2545 template <typename T, typename U>
2546 struct merge_error_set_helper;
2547
2548 template <typename... T, typename U>
2549 requires(all_unique_v<T..., U>)
2550 struct merge_error_set_helper<error_set<T...>, error_set<U>> {
2551 using type = error_set<T..., U>;
2552 };
2553
2554 template <typename... T, typename U>
2555 requires(!all_unique_v<T..., U>)
2556 struct merge_error_set_helper<error_set<T...>, error_set<U>> {
2557 using type = error_set<T...>;
2558 };
2559
2560 template <typename... T, typename U0, typename... UN>
2561 requires(all_unique_v<T..., U0>)
2562 struct merge_error_set_helper<error_set<T...>, error_set<U0, UN...>>
2563 : merge_error_set_helper<error_set<T..., U0>, error_set<UN...>> {};
2564
2565 template <typename... T, typename U0, typename... UN>
2566 requires(!all_unique_v<T..., U0>)
2567 struct merge_error_set_helper<error_set<T...>, error_set<U0, UN...>>
2568 : merge_error_set_helper<error_set<T...>, error_set<UN...>> {};
2569
2570 template <typename T, typename U>
2571 requires(!is_error_set_v<std::remove_cvref_t<T>> &&
2572 !is_error_set_v<std::remove_cvref_t<U>>)
2573 struct merge_error_set_helper<T, U> : merge_error_set_helper<error_set<T>, error_set<U>> {};
2574
2575 template <typename T, typename U>
2576 requires(is_error_set_v<std::remove_cvref_t<T>> &&
2577 !is_error_set_v<std::remove_cvref_t<U>>)
2578 struct merge_error_set_helper<T, U> : merge_error_set_helper<T, error_set<U>> {};
2579
2580 template <typename T, typename U>
2581 requires(!is_error_set_v<std::remove_cvref_t<T>> &&
2582 is_error_set_v<std::remove_cvref_t<U>>)
2583 struct merge_error_set_helper<T, U> : merge_error_set_helper<error_set<T>, U> {};
2584
2585 template <typename... T>
2586 struct make_error_set_helper;
2587
2588 template <typename... T>
2589 struct make_error_set_helper<error_set<T...>> {
2590 using type = error_set<T...>;
2591 };
2592
2593 template <typename T>
2594 requires(!is_error_set_v<T>)
2595 struct make_error_set_helper<T> {
2596 using type = error_set<T>;
2597 };
2598
2599 template <typename T0, typename T1, typename... TN>
2600 struct make_error_set_helper<T0, T1, TN...>
2601 : make_error_set_helper<typename merge_error_set_helper<T0, T1>::type, TN...> {};
2602
2603 } // namespace detail
2604
2605 /// @relates error_set
2606 /// @class is_subset_of error_set.hpp <sumty/error_set.hpp>
2607 /// @brief Utility to test that one @ref error_set is a subset of another.
2608 ///
2609 /// @details
2610 /// This type is a template metaprogramming utility to test that the @ref
2611 /// error_set `ES1` is a subset of the @ref error_set `ES2`. If every
2612 /// alternative of `ES1` is an alternative of `ES2`, regardless of order or
2613 /// index, then `ES1` is a subset of `ES2`. The static member constant `value`
2614 /// is a `bool` that is `true` if `ES1` is a subset of `ES2`.
2615 ///
2616 /// ## Example
2617 /// ```
2618 /// assert(is_subset_of<
2619 /// error_set<error1>,
2620 /// error_set<error3, error2, error2>
2621 /// >::value);
2622 ///
2623 /// assert(!is_subset_of<
2624 /// error_set<error3, error2, error2>
2625 /// error_set<error1>,
2626 /// >::value);
2627 /// ```
2628 ///
2629 /// @tparam ES1 The proposed subset
2630 /// @tparam ES2 The proposed superset
2631 template <typename ES1, typename ES2>
2632 struct is_subset_of : detail::is_subset_of_impl<ES1, ES2> {};
2633
2634 /// @relates is_subset_of
2635 /// @brief Utility to test that one @ref error_set is a subset of another.
2636 ///
2637 /// @details
2638 /// This is a template metaprogramming utility to test that the @ref
2639 /// error_set, `ES1`, is a subset of the @ref error_set `ES2`. If every
2640 /// alternative of `ES1` is an alternative of `ES2`, regardless of order or
2641 /// index, then `ES1` is a subset of `ES2`.
2642 ///
2643 /// ## Example
2644 /// ```
2645 /// assert(is_subset_of_v<
2646 /// error_set<error1>,
2647 /// error_set<error3, error2, error2>
2648 /// >);
2649 ///
2650 /// assert(!is_subset_of_v<
2651 /// error_set<error3, error2, error2>
2652 /// error_set<error1>,
2653 /// >);
2654 /// ```
2655 ///
2656 /// @tparam ES1 The proposed subset
2657 /// @tparam ES2 The proposed superset
2658 template <typename ES1, typename ES2>
2659 static inline constexpr bool is_subset_of_v = is_subset_of<ES1, ES2>::value;
2660
2661 /// @relates error_set
2662 /// @class is_equivalent error_set.hpp <sumty/error_set.hpp>
2663 /// @brief Utility to test that one @ref error_set is equivalent to another.
2664 ///
2665 /// @details
2666 /// This type is a template metaprogramming utility to test that the @ref
2667 /// error_set `ES1` is equivalent to the @ref error_set `ES2`. Two @ref
2668 /// error_set instantiations are equivalent if each alternative type of
2669 /// one is an alternative type in the other, in both directions. Thus
2670 /// equivalence is commutative. So, reworded, the two are equivalent if
2671 /// the have the same alternative types, but potentially in different
2672 /// orders.
2673 ///
2674 /// @ref is_equivalent has the member constant `value`, which is `true`
2675 /// if the two @ref error_set instantiations are equivalent, and `false`
2676 /// otherwise.
2677 ///
2678 /// ## Example
2679 /// ```
2680 /// assert(is_equivalent<
2681 /// error_set<error1, error2, error3>,
2682 /// error_set<error2, error1, error3>
2683 /// >::value);
2684 ///
2685 /// assert(!is_equivalent<
2686 /// error_set<error1, error2, error3>,
2687 /// error_set<error1, error2>
2688 /// >::value);
2689 /// ```
2690 ///
2691 /// @tparam ES1 An @ref error_set type
2692 /// @tparam ES2 An @ref error_set type
2693 template <typename ES1, typename ES2>
2694 struct is_equivalent : detail::is_equivalent_impl<ES1, ES2> {};
2695
2696 /// @relates is_equivalent
2697 /// @brief Utility to test that one @ref error_set is equivalent to another.
2698 ///
2699 /// @details
2700 /// This is a template metaprogramming utility to test that the @ref
2701 /// error_set `ES1` is equivalent to the @ref error_set `ES2`. Two @ref
2702 /// error_set instantiations are equivalent if each alternative type of
2703 /// one is an alternative type in the other, in both directions. Thus
2704 /// equivalence is commutative. So, reworded, the two are equivalent if
2705 /// the have the same alternative types, but potentially in different
2706 /// orders.
2707 ///
2708 /// ## Example
2709 /// ```
2710 /// assert(is_equivalent_v<
2711 /// error_set<error1, error2, error3>,
2712 /// error_set<error2, error1, error3>
2713 /// >);
2714 ///
2715 /// assert(!is_equivalent_v<
2716 /// error_set<error1, error2, error3>,
2717 /// error_set<error1, error2>
2718 /// >);
2719 /// ```
2720 ///
2721 /// @tparam ES1 An @ref error_set type
2722 /// @tparam ES2 An @ref error_set type
2723 template <typename ES1, typename ES2>
2724 static inline constexpr bool is_equivalent_v = is_equivalent<ES1, ES2>::value;
2725
2726 /// @relates error_set
2727 /// @class error_set_size error_set.hpp <sumty/error_set.hpp>
2728 /// @brief Utility to get the number of alternatives in an @ref error_set
2729 ///
2730 /// @details
2731 /// @ref error_set_size provides the number alternatives in the @ref error_set,
2732 /// `T`, in the static constexpr member `value`.
2733 ///
2734 /// ## Example
2735 /// ```cpp
2736 /// assert(error_set_size<error_set<error1, error2, error3>>::value == 3);
2737 /// ```
2738 ///
2739 /// @tparam T The @ref error_set type to get the size of
2740 template <typename T>
2741 struct error_set_size : detail::error_set_size_helper<T> {};
2742
2743 /// @relates error_set_size
2744 /// @brief Utility to get the number of alternatives in an @ref error_set
2745 ///
2746 /// @details
2747 /// ## Example
2748 /// ```cpp
2749 /// assert(error_set_size_v<error_set<error1, error2, error3>> == 3);
2750 /// ```
2751 ///
2752 /// @tparam T The @ref error_set type to get the size of
2753 template <typename T>
2754 static inline constexpr size_t error_set_size_v = error_set_size<T>::value;
2755
2756 template <typename... T>
2757 struct variant_size<error_set<T...>> : error_set_size<error_set<T...>> {};
2758
2759 template <typename... T>
2760 struct variant_size<const error_set<T...>> : error_set_size<const error_set<T...>> {};
2761
2762 /// @relates error_set
2763 /// @class error_set_alternative error_set.hpp <sumty/error_set.hpp>
2764 /// @brief Utility to get the type of a @ref error_set alternative with some index
2765 ///
2766 /// @details
2767 /// @ref error_set_alternative provides the type of an alternative with the
2768 /// index `I` in the member type, `type`.
2769 ///
2770 /// ## Example
2771 /// ```cpp
2772 /// assert(std::is_same_v<
2773 /// typename error_set_alternative<1, variant<error1, error2, error3>>::type,
2774 /// error2
2775 /// >);
2776 /// ```
2777 ///
2778 /// @tparam I The index of the @ref error_set alternative
2779 ///
2780 /// @tparam ES The @ref error_set type containing the alternative
2781 template <size_t I, typename ES>
2782 struct error_set_alternative : detail::error_set_alternative_helper<I, ES> {};
2783
2784 /// @relates error_set_alternative
2785 /// @brief Utility to get the type of a @ref error_set alternative with some index
2786 ///
2787 /// @details
2788 /// @ref error_set_alternative_t is the type of an alternative with the index `I`.
2789 ///
2790 /// ## Example
2791 /// ```cpp
2792 /// assert(std::is_same_v<
2793 /// error_set_alternative_t<1, variant<error1, error2, error3>>,
2794 /// error2
2795 /// >);
2796 /// ```
2797 ///
2798 /// @tparam I The index of the @ref error_set alternative
2799 ///
2800 /// @tparam ES The @ref error_set type containing the alternative
2801 template <size_t I, typename ES>
2802 using error_set_alternative_t = typename error_set_alternative<I, ES>::type;
2803
2804 template <size_t I, typename... T>
2805 struct variant_alternative<I, error_set<T...>> : error_set_alternative<I, error_set<T...>> {
2806 };
2807
2808 template <size_t I, typename... T>
2809 struct variant_alternative<I, const error_set<T...>>
2810 : error_set_alternative<I, const error_set<T...>> {};
2811
2812 /// @relates error_set
2813 /// @class merge_error_set error_set.hpp <sumty/error_set.hpp>
2814 /// @brief Utility to merge two @ref error_set types
2815 ///
2816 /// @details
2817 /// @ref merge_error_set combines the alternative types of two @ref error_set
2818 /// instantiations into a single @ref error_set instantiation. The two types
2819 /// may or may not have alternatives in common. Since @ref error_set requires
2820 /// that there are no two alternatives with the same type, any alternatives
2821 /// common between the two @ref error_set instantiations are deduplicated.
2822 ///
2823 /// ## Example
2824 /// ```
2825 /// using merged = typename merge_error_set<
2826 /// error_set<error1, error2>,
2827 /// error_set<error3, error1>
2828 /// >::type;
2829 ///
2830 /// assert(is_equivalent_v<merged, error_set<error1, error2, error3>>);
2831 /// ```
2832 ///
2833 /// @tparam ES1 An @ref error_set type
2834 /// @tparam ES2 An @ref error_set type
2835 template <typename ES1, typename ES2>
2836 struct merge_error_set : detail::merge_error_set_helper<ES1, ES2> {};
2837
2838 /// @relates merge_error_set
2839 /// @brief Utility to merge two @ref error_set types
2840 ///
2841 /// @details
2842 /// @ref merge_error_set combines the alternative types of two @ref error_set
2843 /// instantiations into a single @ref error_set instantiation. The two types
2844 /// may or may not have alternatives in common. Since @ref error_set requires
2845 /// that there are no two alternatives with the same type, any alternatives
2846 /// common between the two @ref error_set instantiations are deduplicated.
2847 ///
2848 /// ## Example
2849 /// ```
2850 /// using merged = merge_error_set_t<
2851 /// error_set<error1, error2>,
2852 /// error_set<error3, error1>
2853 /// >;
2854 ///
2855 /// assert(is_equivalent_v<merged, error_set<error1, error2, error3>>);
2856 /// ```
2857 ///
2858 /// @tparam ES1 An @ref error_set type
2859 /// @tparam ES2 An @ref error_set type
2860 template <typename ES1, typename ES2>
2861 using merge_error_set_t = typename merge_error_set<ES1, ES2>::type;
2862
2863 /// @relates error_set
2864 /// @class make_error_set error_set.hpp <sumty/error_set.hpp>
2865 /// @brief Utility to create an @ref error_set type.
2866 ///
2867 /// @details
2868 /// @ref make_error_set is a builder for the @ref error_set type that
2869 /// deduplicates alternatives. @ref error_set requires that each alternative
2870 /// be a unique type, so @ref make_error_set simplifies generically building
2871 /// an @ref error_set by ensuring that condition is met automatically.
2872 ///
2873 /// ## Example
2874 /// ```
2875 /// assert(is_equivalent_v<
2876 /// typename make_error_set<
2877 /// error1, error2, error1, error1, error3, error2
2878 /// >::type,
2879 /// error_set<error1, error2, error3>
2880 /// >);
2881 /// ```
2882 ///
2883 /// @tparam T The alternative types of the resulting @ref error_set
2884 template <typename... T>
2885 struct make_error_set : detail::make_error_set_helper<T...> {};
2886
2887 /// @relates make_error_set
2888 /// @brief Utility to create an @ref error_set type.
2889 ///
2890 /// @details
2891 /// @ref make_error_set is a builder for the @ref error_set type that
2892 /// deduplicates alternatives. @ref error_set requires that each alternative
2893 /// be a unique type, so @ref make_error_set simplifies generically building
2894 /// an @ref error_set by ensuring that condition is met automatically.
2895 ///
2896 /// ## Example
2897 /// ```
2898 /// assert(is_equivalent_v<
2899 /// make_error_set_t<error1, error2, error1, error1, error3, error2>,
2900 /// error_set<error1, error2, error3>
2901 /// >);
2902 /// ```
2903 ///
2904 /// @tparam T The alternative types of the resulting @ref error_set
2905 template <typename... T>
2906 using make_error_set_t = typename make_error_set<T...>::type;
2907
2908 /// @private
2909 template <typename... T>
2910 requires((true && ... && std::is_base_of_v<never, std::remove_cvref_t<T>>))
2911 class error_set<T...> : public never {};
2912
2913 } // namespace sumty
2914
2915 #endif
2916