libzypp 17.38.5
expected.h
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8----------------------------------------------------------------------/
9*
10* This file contains private API, this might break at any time between releases.
11* You have been warned!
12*
13* Based on code by Ivan Čukić (BSD/MIT licensed) from the functional cpp book
14*/
15
16#ifndef ZYPP_ZYPPNG_MONADIC_EXPECTED_H
17#define ZYPP_ZYPPNG_MONADIC_EXPECTED_H
18
19#include <stdexcept>
21#include <zypp-core/ng/meta/Functional>
23#include <zypp-core/ng/pipelines/operators.h>
24#include <exception>
25
26namespace zyppng {
27
28 template<typename T>
29 class unexpected {
30 public:
31 template <typename E = T>
32 explicit unexpected(E &&err) : _err(std::forward<E>(err)) {}
33 ~unexpected() = default;
34
35 unexpected(const unexpected &) = default;
36 unexpected(unexpected &&) = default;
37 unexpected &operator=(const unexpected &) = default;
39
40 const T &error() const {
41 return _err;
42 }
43
44 T &error() {
45 return _err;
46 }
47
48 private:
50 };
51
52 template<class E>
54
55 template<typename T, typename E = std::exception_ptr>
57 protected:
58 union {
61 };
62
64
65 expected() // used internally
66 {
67 }
68
69 public:
70
71 using value_type = T;
72 using error_type = E;
73
75 {
76 if (m_isValid) {
77 m_value.~T();
78 } else {
79 m_error.~E();
80 }
81 }
82
83 expected( const T& v ) : m_isValid(true)
84 {
85 new (&m_value) T( v );
86 }
87
88 expected( T&& v ) : m_isValid(true)
89 {
90 new (&m_value) T( std::move(v) );
91 }
92
93 expected( const unexpected<E> &err ) noexcept
94 : m_isValid(false) {
95 new(&m_error) E(err.error());
96 }
97
98 expected( unexpected<E> &&err ) noexcept
99 : m_isValid(false) {
100 new(&m_error) E(std::move(err.error()));
101 }
102
103 expected(const expected &other)
104 : m_isValid(other.m_isValid)
105 {
106 if (m_isValid) {
107 new (&m_value) T(other.m_value);
108 } else {
109 new (&m_error) E(other.m_error);
110 }
111 }
112
113 expected(expected &&other) noexcept
114 : m_isValid(other.m_isValid)
115 {
116 if (m_isValid) {
117 new (&m_value) T( std::move(other.m_value) );
118 } else {
119 new (&m_error) E( std::move(other.m_error) );
120 }
121 }
122
123 template < class G = E >
124 expected &operator= (unexpected<G> other)
125 {
126 swap(expected::error(std::move(other.error())));
127 return *this;
128 }
129
130 expected &operator= (expected other)
131 {
132 swap(other);
133 return *this;
134 }
135
136 template< class U = std::remove_cv_t<T> >
138 {
139 swap( expected::success (std::forward<U>(v) ) );
140 return *this;
141 }
142
143 void swap(expected &other) noexcept
144 {
145 using std::swap;
146 if (m_isValid) {
147 if (other.m_isValid) {
148 // Both are valid, just swap the values
149 swap(m_value, other.m_value);
150
151 } else {
152 // We are valid, but the other one is not
153 // we need to do the whole dance
154 auto temp = std::move(other.m_error); // moving the error into the temp
155 other.m_error.~E(); // destroying the original error object
156 new (&other.m_value) T(std::move(m_value)); // moving our value into the other
157 m_value.~T(); // destroying our value object
158 new (&m_error) E(std::move(temp)); // moving the error saved to the temp into us
159 std::swap(m_isValid, other.m_isValid); // swap the isValid flags
160 }
161
162 } else {
163 if (other.m_isValid) {
164 // We are not valid, but the other one is,
165 // just call swap on other and rely on the
166 // implementation in the previous case
167 other.swap(*this);
168
169 } else {
170 // Everything is rotten, just swap the errors
171 swap(m_error, other.m_error);
172 std::swap(m_isValid, other.m_isValid);
173 }
174 }
175 }
176
177 template <typename... ConsParams>
178 static expected success(ConsParams && ...params)
179 {
180 // silence clang-tidy about uninitialized class members, we manually intialize them.
181 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
182 expected result;
183 result.m_isValid = true;
184 new(&result.m_value) T(std::forward<ConsParams>(params)...);
185 return result;
186 }
187
188 template <typename... ConsParams>
189 static expected error(ConsParams && ...params)
190 {
191 // silence clang-tidy about uninitialized class members, we manually intialize them.
192 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
193 expected result;
194 result.m_isValid = false;
195 new(&result.m_error) E(std::forward<ConsParams>(params)...);
196 return result;
197 }
198
199 operator bool() const
200 {
201 return m_isValid;
202 }
203
204 bool is_valid() const
205 {
206 return m_isValid;
207 }
208
209 #ifdef NO_EXCEPTIONS
210 # define THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED(WHAT) std::terminate()
211 #else
212 # define THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED(WHAT) throw std::logic_error(WHAT)
213 #endif
214
215 T &get()
216 {
217 if (!m_isValid) THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED("expected<T, E> contains no value");
218 return m_value;
219 }
220
221 const T &get() const
222 {
223 if (!m_isValid) THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED("expected<T, E> contains no value");
224 return m_value;
225 }
226
237 {
238 if (!m_isValid) {
239#ifdef NO_EXCEPTIONS
240 std::terminate();
241#else
242 if constexpr ( std::is_same_v<E, std::exception_ptr> ) {
243 std::rethrow_exception ( error() );
244 } else {
245 throw error();
246 }
247#endif
248 }
249 return m_value;
250 }
251
252 const T &unwrap() const
253 {
254 if (!m_isValid) {
255#ifdef NO_EXCEPTIONS
256 std::terminate();
257#else
258 if constexpr ( std::is_same_v<E, std::exception_ptr>() ) {
259 std::rethrow_exception ( error() );
260 } else {
261 throw error();
262 }
263#endif
264 }
265 return m_value;
266 }
267
268 T &operator* ()
269 {
270 return get();
271 }
272
273 const T &operator* () const
274 {
275 return get();
276 }
277
278 T *operator-> ()
279 {
280 return &get();
281 }
282
283 const T *operator-> () const
284 {
285 return &get();
286 }
287
288 E &error()
289 {
290 if (m_isValid) THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
291 return m_error;
292 }
293
294 const E &error() const
295 {
296 if (m_isValid) THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
297 return m_error;
298 }
299
300 #undef THROW_IF_EXCEPTIONS_ARE_ENABLED
301
302 template <typename F>
303 void visit(F f) {
304 if (m_isValid) {
305 f(m_value);
306 } else {
307 f(m_error);
308 }
309 }
310 };
311
312
313 template<typename E>
314 class ZYPP_NODISCARD expected<void, E> {
315 private:
316 union {
317 void* m_value;
319 };
320
322
323 public:
324
325 expected() : m_value(nullptr), m_isValid(true) {}
326
327 template< class G >
328 expected( unexpected<G> err ) noexcept
329 : m_isValid(false) {
330 new(&m_error) E(std::move(err.error()));
331 }
332
334 {
335 if (m_isValid) {
336 // m_value.~T();
337 } else {
338 m_error.~E();
339 }
340 }
341
342 expected(const expected &other)
343 : m_isValid(other.m_isValid)
344 {
345 if (m_isValid) {
346 // new (&m_value) T(other.m_value);
347 } else {
348 new (&m_error) E(other.m_error);
349 }
350 }
351
352 expected(expected &&other) noexcept
353 : m_isValid(other.m_isValid)
354 {
355 if (m_isValid) {
356 // new (&m_value) T(std::move(other.m_value));
357 } else {
358 new (&m_error) E(std::move(other.m_error));
359 }
360 }
361
362 expected &operator= (expected other)
363 {
364 swap(other);
365 return *this;
366 }
367
368 template < class G = E >
369 expected &operator= (unexpected<G> other)
370 {
371 swap(expected(std::move(other)));
372 return *this;
373 }
374
375
376 void swap(expected &other) noexcept
377 {
378 using std::swap;
379 if (m_isValid) {
380 if (other.m_isValid) {
381 // Both are valid, we do not have any values
382 // to swap
383
384 } else {
385 // We are valid, but the other one is not.
386 // We need to move the error into us
387 auto temp = std::move(other.m_error); // moving the error into the temp
388 other.m_error.~E(); // destroying the original error object
389 new (&m_error) E(std::move(temp)); // moving the error into us
390 std::swap(m_isValid, other.m_isValid); // swapping the isValid flags
391 }
392
393 } else {
394 if (other.m_isValid) {
395 // We are not valid, but the other one is,
396 // just call swap on other and rely on the
397 // implementation in the previous case
398 other.swap(*this);
399
400 } else {
401 // Everything is rotten, just swap the errors
402 swap(m_error, other.m_error);
403 std::swap(m_isValid, other.m_isValid);
404 }
405 }
406 }
407
409 {
410 return expected();
411 }
412
413 template <typename... ConsParams>
414 static expected error(ConsParams && ...params)
415 {
416 // silence clang-tidy about uninitialized class members, we manually intialize them.
417 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
418 expected result;
419 result.m_isValid = false;
420 new(&result.m_error) E(std::forward<ConsParams>(params)...);
421 return result;
422 }
423
424 operator bool() const
425 {
426 return m_isValid;
427 }
428
429 bool is_valid() const
430 {
431 return m_isValid;
432 };
433
434 #ifdef NO_EXCEPTIONS
435 # define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) std::terminate()
436 #else
437 # define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) throw std::logic_error(WHAT)
438 #endif
439
440 E &error()
441 {
442 if (m_isValid) THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
443 return m_error;
444 }
445
446 const E &error() const
447 {
448 if (m_isValid) THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
449 return m_error;
450 }
451
452 void unwrap() const
453 {
454 if (!m_isValid) {
455#ifdef NO_EXCEPTIONS
456 std::terminate();
457#else
458 if constexpr ( std::is_same_v<E, std::exception_ptr> ) {
459 std::rethrow_exception ( error() );
460 } else {
461 throw error();
462 }
463#endif
464 }
465 }
466
467 };
468
469 template <typename Type, typename Err = std::exception_ptr >
471 {
472 return expected<std::decay_t<Type>,Err>::success( std::forward<Type>(t) );
473 }
474
475 namespace detail {
476
477 // helper to figure out the return type for a mbind callback, if the ArgType is void the callback is considered to take no argument.
478 // Due to how std::conditional works, we cannot pass std::invoke_result_t but instead use the template type std::invoke_result, since
479 // one of the two options have no "::type" because the substitution fails, this breaks the std::conditional_t since it can only work with two well formed
480 // types. Instead we pass in the template types and evaluate the ::type in the end, when the correct invoke_result was chosen.
481 template < typename Function, typename ArgType>
482 using mbind_cb_result_t = typename std::conditional_t< std::is_same_v<ArgType,void>, std::invoke_result<Function>,std::invoke_result<Function, ArgType> >::type;
483
484 template <typename T>
486 return value.is_valid();
487 }
488
489 template<typename ResultType>
491 template <typename E>
492 static ResultType error ( E &&err ) {
493 return ResultType::error( std::forward<E>(err) );
494 }
495
496 template <typename Res>
497 static ResultType forward ( Res &&exp ) {
498 return std::forward<Res>(exp);
499 }
500 };
501
502 template <typename ResultType, typename E>
503 ResultType expected_make_error( E && error) {
504 return expected_result_helper<ResultType>::error( std::forward<E>(error) );
505 }
506
507 template <typename ResultType>
508 ResultType expected_forward( ResultType &&res ) {
509 return expected_result_helper<ResultType>::forward( std::forward<ResultType>(res) );
510 }
511
512 }
513
514
515 template < typename T
516 , typename E
517 , typename Function
518 , typename ResultType = detail::mbind_cb_result_t<Function, T>
519 >
520 ResultType and_then( const expected<T, E>& exp, Function &&f)
521 {
522 if (exp) {
523 if constexpr ( std::is_same_v<T,void> )
524 return std::invoke( std::forward<Function>(f) );
525 else
526 return std::invoke( std::forward<Function>(f), exp.get() );
527 } else {
529 }
530 }
531
532 template < typename T
533 , typename E
534 , typename Function
535 , typename ResultType = detail::mbind_cb_result_t<Function, T>
536 >
537 ResultType and_then( expected<T, E> &&exp, Function &&f)
538 {
539 if (exp) {
540 if constexpr ( std::is_same_v<T,void> )
541 return std::invoke( std::forward<Function>(f) );
542 else
543 return std::invoke( std::forward<Function>(f), std::move(exp.get()) );
544 } else {
545 return detail::expected_make_error<ResultType>(exp.error());
546 }
547 }
548
549 template < typename T
550 , typename E
551 , typename Function
552 , typename ResultType = detail::mbind_cb_result_t<Function, E>
553 >
554 ResultType or_else( const expected<T, E>& exp, Function &&f)
555 {
556 if (!exp) {
557 return std::invoke( std::forward<Function>(f), exp.error() );
558 } else {
559 return detail::expected_forward(exp);
560 }
561 }
562
563 template < typename T
564 , typename E
565 , typename Function
566 , typename ResultType = detail::mbind_cb_result_t<Function, E>
567 >
568 ResultType or_else( expected<T, E>&& exp, Function &&f)
569 {
570 if (!exp) {
571 return std::invoke( std::forward<Function>(f), std::move(exp.error()) );
572 } else {
573 return detail::expected_forward( std::move(exp) );
574 }
575 }
576
577
582 template < template< class, class... > class Container,
583 typename T,
584 typename E,
585 typename ...CArgs >
586 std::enable_if_t<!std::is_same_v<void, T>, expected<Container<T>,E>> collect( Container<expected<T, E>, CArgs...>&& in ) {
587 Container<T> res;
588 for( auto &v : in ) {
589 if ( !v )
590 return expected<Container<T>,E>::error( std::move(v.error()) );
591 res.push_back( std::move(v.get()) );
592 }
593 return expected<Container<T>,E>::success( std::move(res) );
594 }
595
600 template < template< class, class... > class Container,
601 typename T,
602 typename E,
603 typename ...CArgs >
604 std::enable_if_t<std::is_same_v<void, T>, expected<T, E>> collect( Container<expected<T, E>, CArgs...>&& in ) {
605 for( auto &v : in ) {
606 if ( !v )
607 return expected<T,E>::error( std::move(v.error()) );
608 }
609 return expected<T,E>::success( );
610 }
611
612 template < typename T
613 , typename E
614 , typename Function
615 >
617 {
618 if (exp) {
619 const auto &val = exp.get();
620 std::invoke( std::forward<Function>(f), val );
621 }
622 return exp;
623 }
624
625 template < typename T
626 , typename E
627 , typename Function
628 >
630 {
631 if (!exp) {
632 const auto &err = exp.error();
633 std::invoke( std::forward<Function>(f), err );
634 }
635 return exp;
636 }
637
638
639 namespace detail {
640
641 template <typename Callback>
643 Callback function;
644
645 template< typename T, typename E >
646 auto operator()( const expected<T, E>& exp ) {
647 return and_then( exp, function );
648 }
649
650 template< typename T, typename E >
652 return and_then( std::move(exp), function );
653 }
654 };
655
656 template <typename Callback>
658 Callback function;
659
660 template< typename T, typename E >
661 auto operator()( const expected<T, E>& exp ) {
662 return or_else( exp, function );
663 }
664
665 template< typename T, typename E >
667 return or_else( std::move(exp), function );
668 }
669 };
670
671 template <typename Callback>
673 Callback function;
674
675 template< typename T, typename E >
677 return inspect( std::move(exp), function );
678 }
679 };
680
681 template <typename Callback>
683 Callback function;
684
685 template< typename T, typename E >
687 return inspect_err( std::move(exp), function );
688 }
689 };
690
692 template < typename T >
693 inline auto operator()( T&& in ) {
694 return collect( std::forward<T>(in) );
695 }
696 };
697 }
698
699 namespace operators {
700 template <typename Fun>
701 auto mbind ( Fun && function ) {
703 std::forward<Fun>(function)
704 };
705 }
706
707 template <typename Fun>
708 auto and_then ( Fun && function ) {
710 std::forward<Fun>(function)
711 };
712 }
713
714 template <typename Fun>
715 auto or_else ( Fun && function ) {
717 std::forward<Fun>(function)
718 };
719 }
720
721 template <typename Fun>
722 auto inspect ( Fun && function ) {
724 std::forward<Fun>(function)
725 };
726 }
727
728 template <typename Fun>
729 auto inspect_err ( Fun && function ) {
731 std::forward<Fun>(function)
732 };
733 }
734
738 }
739
740
745 template < template< class, class... > class Container,
746 typename Msg,
747 typename Transformation,
748 typename Ret = std::result_of_t<Transformation(Msg)>,
749 typename ...CArgs
750 >
752 {
753 Container<typename Ret::value_type> results;
754 for ( auto &v : in ) {
755 auto res = f(std::move(v));
756 if ( res ) {
757 results.push_back( std::move(res.get()) );
758 } else {
759 return expected<Container<typename Ret::value_type>>::error( res.error() );
760 }
761 }
762 return expected<Container<typename Ret::value_type>>::success( std::move(results) );
763 }
764
765 namespace detail {
766 template <typename Fun>
769 template <typename T>
770 auto operator() ( T &&in ) {
771 return transform_collect( std::forward<T>(in), _callback );
772 }
773 };
774 }
775
776 namespace operators {
777 template <typename Transformation>
778 auto transform_collect( Transformation &&f ) {
779 return detail::transform_collect_helper{ std::forward<Transformation>(f)};
780 }
781 }
782}
783
784#ifdef ZYPP_ENABLE_ASYNC
785#include <zypp-core/ng/async/pipelines/expected.hpp>
786#endif
787
788#endif
789
const E & error() const
Definition expected.h:446
static expected success()
Definition expected.h:408
expected(expected &&other) noexcept
Definition expected.h:352
expected(unexpected< G > err) noexcept
Definition expected.h:328
expected(const expected &other)
Definition expected.h:342
static expected error(ConsParams &&...params)
Definition expected.h:414
void swap(expected &other) noexcept
Definition expected.h:376
expected(expected &&other) noexcept
Definition expected.h:113
static expected success(ConsParams &&...params)
Definition expected.h:178
void visit(F f)
Definition expected.h:303
const E & error() const
Definition expected.h:294
expected(const T &v)
Definition expected.h:83
static expected error(ConsParams &&...params)
Definition expected.h:189
expected(const unexpected< E > &err) noexcept
Definition expected.h:93
expected(T &&v)
Definition expected.h:88
std::pair< zypp::ServiceInfo, RepoInfoList > m_value
Definition expected.h:59
const T & unwrap() const
Definition expected.h:252
expected(const expected &other)
Definition expected.h:103
std::pair< zypp::ServiceInfo, RepoInfoList > value_type
Definition expected.h:71
expected(unexpected< E > &&err) noexcept
Definition expected.h:98
const T & get() const
Definition expected.h:221
expected & operator=(U &&v)
Definition expected.h:137
bool is_valid() const
Definition expected.h:204
const T & error() const
Definition expected.h:40
unexpected(unexpected &&)=default
unexpected & operator=(const unexpected &)=default
unexpected(E &&err)
Definition expected.h:32
unexpected & operator=(unexpected &&)=default
~unexpected()=default
unexpected(const unexpected &)=default
#define THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED(WHAT)
Definition expected.h:212
#define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT)
Definition expected.h:437
Definition ansi.h:855
typename enable_if< B, T >::type enable_if_t
Definition TypeTraits.h:45
typename conditional< B, T, F >::type conditional_t
Definition TypeTraits.h:39
typename result_of< T >::type result_of_t
Definition TypeTraits.h:51
std::enable_if< std::is_member_pointer< typenamestd::decay< Functor >::type >::value, typenamestd::result_of< Functor &&(Args &&...)>::type >::type invoke(Functor &&f, Args &&... args)
Definition functional.h:32
ResultType expected_forward(ResultType &&res)
Definition expected.h:508
bool waitForCanContinueExpected(const expected< T > &value)
Definition expected.h:485
ResultType expected_make_error(E &&error)
Definition expected.h:503
typename std::conditional_t< std::is_same_v< ArgType, void >, std::invoke_result< Function >, std::invoke_result< Function, ArgType > >::type mbind_cb_result_t
Definition expected.h:482
auto or_else(Fun &&function)
Definition expected.h:715
auto transform_collect(Transformation &&f)
Definition expected.h:778
auto and_then(Fun &&function)
Definition expected.h:708
auto inspect_err(Fun &&function)
Definition expected.h:729
detail::collect_helper collect()
Definition expected.h:735
auto inspect(Fun &&function)
Definition expected.h:722
auto mbind(Fun &&function)
Definition expected.h:701
std::enable_if_t< is_instance_of_v< expected, Ret >, expected< Container< typename Ret::value_type > > > transform_collect(Container< Msg, CArgs... > &&in, Transformation &&f)
Definition expected.h:751
static expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
Definition expected.h:470
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition expected.h:554
unexpected(E) -> unexpected< E >
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition expected.h:520
std::enable_if_t<!std::is_same_v< void, T >, expected< Container< T >, E > > collect(Container< expected< T, E >, CArgs... > &&in)
Definition expected.h:586
expected< T, E > inspect(expected< T, E > exp, Function &&f)
Definition expected.h:616
expected< T, E > inspect_err(expected< T, E > exp, Function &&f)
Definition expected.h:629
auto operator()(expected< T, E > &&exp)
Definition expected.h:651
auto operator()(const expected< T, E > &exp)
Definition expected.h:646
static ResultType forward(Res &&exp)
Definition expected.h:497
static ResultType error(E &&err)
Definition expected.h:492
auto operator()(expected< T, E > &&exp)
Definition expected.h:686
auto operator()(expected< T, E > &&exp)
Definition expected.h:676
auto operator()(const expected< T, E > &exp)
Definition expected.h:661
auto operator()(expected< T, E > &&exp)
Definition expected.h:666
#define ZYPP_NODISCARD
Definition zyppglobal.h:226