libzypp 17.37.17
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 <zypp-core/zyppng/meta/Functional>
20#include <zypp-core/zyppng/pipelines/AsyncResult>
21#include <zypp-core/zyppng/pipelines/Wait>
22#include <zypp-core/zyppng/pipelines/Transform>
23
24namespace zyppng {
25
26 template<typename T, typename E = std::exception_ptr>
28 protected:
29 union {
32 };
33
35
36 expected() // used internally
37 {
38 }
39
40 public:
41
42 using value_type = T;
43 using error_type = E;
44
46 {
47 if (m_isValid) {
48 m_value.~T();
49 } else {
50 m_error.~E();
51 }
52 }
53
54 expected(const expected &other)
55 : m_isValid(other.m_isValid)
56 {
57 if (m_isValid) {
58 new (&m_value) T(other.m_value);
59 } else {
60 new (&m_error) E(other.m_error);
61 }
62 }
63
64 expected(expected &&other) noexcept
65 : m_isValid(other.m_isValid)
66 {
67 if (m_isValid) {
68 new (&m_value) T( std::move(other.m_value) );
69 } else {
70 new (&m_error) E( std::move(other.m_error) );
71 }
72 }
73
74 expected &operator= (expected other)
75 {
76 swap(other);
77 return *this;
78 }
79
80 void swap(expected &other) noexcept
81 {
82 using std::swap;
83 if (m_isValid) {
84 if (other.m_isValid) {
85 // Both are valid, just swap the values
86 swap(m_value, other.m_value);
87
88 } else {
89 // We are valid, but the other one is not
90 // we need to do the whole dance
91 auto temp = std::move(other.m_error); // moving the error into the temp
92 other.m_error.~E(); // destroying the original error object
93 new (&other.m_value) T(std::move(m_value)); // moving our value into the other
94 m_value.~T(); // destroying our value object
95 new (&m_error) E(std::move(temp)); // moving the error saved to the temp into us
96 std::swap(m_isValid, other.m_isValid); // swap the isValid flags
97 }
98
99 } else {
100 if (other.m_isValid) {
101 // We are not valid, but the other one is,
102 // just call swap on other and rely on the
103 // implementation in the previous case
104 other.swap(*this);
105
106 } else {
107 // Everything is rotten, just swap the errors
108 swap(m_error, other.m_error);
109 std::swap(m_isValid, other.m_isValid);
110 }
111 }
112 }
113
114 template <typename... ConsParams>
115 static expected success(ConsParams && ...params)
116 {
117 // silence clang-tidy about uninitialized class members, we manually intialize them.
118 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
119 expected result;
120 result.m_isValid = true;
121 new(&result.m_value) T(std::forward<ConsParams>(params)...);
122 return result;
123 }
124
125 template <typename... ConsParams>
126 static expected error(ConsParams && ...params)
127 {
128 // silence clang-tidy about uninitialized class members, we manually intialize them.
129 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
130 expected result;
131 result.m_isValid = false;
132 new(&result.m_error) E(std::forward<ConsParams>(params)...);
133 return result;
134 }
135
136 operator bool() const
137 {
138 return m_isValid;
139 }
140
141 bool is_valid() const
142 {
143 return m_isValid;
144 }
145
146 #ifdef NO_EXCEPTIONS
147 # define THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED(WHAT) std::terminate()
148 #else
149 # define THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED(WHAT) throw std::logic_error(WHAT)
150 #endif
151
152 T &get()
153 {
154 if (!m_isValid) THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED("expected<T, E> contains no value");
155 return m_value;
156 }
157
158 const T &get() const
159 {
160 if (!m_isValid) THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED("expected<T, E> contains no value");
161 return m_value;
162 }
163
174 {
175 if (!m_isValid) {
176#ifdef NO_EXCEPTIONS
177 std::terminate();
178#else
179 if constexpr ( std::is_same_v<E, std::exception_ptr> ) {
180 std::rethrow_exception ( error() );
181 } else {
182 throw error();
183 }
184#endif
185 }
186 return m_value;
187 }
188
189 const T &unwrap() const
190 {
191 if (!m_isValid) {
192#ifdef NO_EXCEPTIONS
193 std::terminate();
194#else
195 if constexpr ( std::is_same_v<E, std::exception_ptr>() ) {
196 std::rethrow_exception ( error() );
197 } else {
198 throw error();
199 }
200#endif
201 }
202 return m_value;
203 }
204
205 T &operator* ()
206 {
207 return get();
208 }
209
210 const T &operator* () const
211 {
212 return get();
213 }
214
215 T *operator-> ()
216 {
217 return &get();
218 }
219
220 const T *operator-> () const
221 {
222 return &get();
223 }
224
225 E &error()
226 {
227 if (m_isValid) THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
228 return m_error;
229 }
230
231 const E &error() const
232 {
233 if (m_isValid) THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
234 return m_error;
235 }
236
237 #undef THROW_IF_EXCEPTIONS_ARE_ENABLED
238
239 template <typename F>
240 void visit(F f) {
241 if (m_isValid) {
242 f(m_value);
243 } else {
244 f(m_error);
245 }
246 }
247 };
248
249
250 template<typename E>
251 class ZYPP_NODISCARD expected<void, E> {
252 private:
253 union {
254 void* m_value;
256 };
257
259
260 expected() {} //used internally
261
262 public:
264 {
265 if (m_isValid) {
266 // m_value.~T();
267 } else {
268 m_error.~E();
269 }
270 }
271
272 expected(const expected &other)
273 : m_isValid(other.m_isValid)
274 {
275 if (m_isValid) {
276 // new (&m_value) T(other.m_value);
277 } else {
278 new (&m_error) E(other.m_error);
279 }
280 }
281
282 expected(expected &&other) noexcept
283 : m_isValid(other.m_isValid)
284 {
285 if (m_isValid) {
286 // new (&m_value) T(std::move(other.m_value));
287 } else {
288 new (&m_error) E(std::move(other.m_error));
289 }
290 }
291
292 expected &operator= (expected other)
293 {
294 swap(other);
295 return *this;
296 }
297
298 void swap(expected &other) noexcept
299 {
300 using std::swap;
301 if (m_isValid) {
302 if (other.m_isValid) {
303 // Both are valid, we do not have any values
304 // to swap
305
306 } else {
307 // We are valid, but the other one is not.
308 // We need to move the error into us
309 auto temp = std::move(other.m_error); // moving the error into the temp
310 other.m_error.~E(); // destroying the original error object
311 new (&m_error) E(std::move(temp)); // moving the error into us
312 std::swap(m_isValid, other.m_isValid); // swapping the isValid flags
313 }
314
315 } else {
316 if (other.m_isValid) {
317 // We are not valid, but the other one is,
318 // just call swap on other and rely on the
319 // implementation in the previous case
320 other.swap(*this);
321
322 } else {
323 // Everything is rotten, just swap the errors
324 swap(m_error, other.m_error);
325 std::swap(m_isValid, other.m_isValid);
326 }
327 }
328 }
329
331 {
332 // silence clang-tidy about uninitialized class members, we manually intialize them.
333 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
334 expected result;
335 result.m_isValid = true;
336 result.m_value = nullptr;
337 return result;
338 }
339
340 template <typename... ConsParams>
341 static expected error(ConsParams && ...params)
342 {
343 // silence clang-tidy about uninitialized class members, we manually intialize them.
344 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
345 expected result;
346 result.m_isValid = false;
347 new(&result.m_error) E(std::forward<ConsParams>(params)...);
348 return result;
349 }
350
351 operator bool() const
352 {
353 return m_isValid;
354 }
355
356 bool is_valid() const
357 {
358 return m_isValid;
359 };
360
361 #ifdef NO_EXCEPTIONS
362 # define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) std::terminate()
363 #else
364 # define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) throw std::logic_error(WHAT)
365 #endif
366
367 E &error()
368 {
369 if (m_isValid) THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
370 return m_error;
371 }
372
373 const E &error() const
374 {
375 if (m_isValid) THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
376 return m_error;
377 }
378
379 void unwrap() const
380 {
381 if (!m_isValid) {
382#ifdef NO_EXCEPTIONS
383 std::terminate();
384#else
385 if constexpr ( std::is_same_v<E, std::exception_ptr> ) {
386 std::rethrow_exception ( error() );
387 } else {
388 throw error();
389 }
390#endif
391 }
392 }
393
394 };
395
396 template <typename Type, typename Err = std::exception_ptr >
398 {
399 return expected<std::decay_t<Type>,Err>::success( std::forward<Type>(t) );
400 }
401
402 namespace detail {
403
404 // helper to figure out the return type for a mbind callback, if the ArgType is void the callback is considered to take no argument.
405 // Due to how std::conditional works, we cannot pass std::invoke_result_t but instead use the template type std::invoke_result, since
406 // 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
407 // types. Instead we pass in the template types and evaluate the ::type in the end, when the correct invoke_result was chosen.
408 template < typename Function, typename ArgType>
409 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;
410
411 template <typename T>
413 return value.is_valid();
414 }
415 }
416
417
418 template < typename T
419 , typename E
420 , typename Function
421 , typename ResultType = detail::mbind_cb_result_t<Function, T>
422 >
423 ResultType and_then( const expected<T, E>& exp, Function &&f)
424 {
425 if (exp) {
426 if constexpr ( std::is_same_v<T,void> )
427 return std::invoke( std::forward<Function>(f) );
428 else
429 return std::invoke( std::forward<Function>(f), exp.get() );
430 } else {
432 return ResultType::error(exp.error());
433 else
435 }
436 }
437
438 template < typename T
439 , typename E
440 , typename Function
441 , typename ResultType = detail::mbind_cb_result_t<Function, T>
442 >
443 ResultType and_then( expected<T, E> &&exp, Function &&f)
444 {
445 if (exp) {
446 if constexpr ( std::is_same_v<T,void> )
447 return std::invoke( std::forward<Function>(f) );
448 else
449 return std::invoke( std::forward<Function>(f), std::move(exp.get()) );
450 } else {
452 return ResultType::error( std::move(exp.error()) );
453 else
455 }
456 }
457
458 template < typename T
459 , typename E
460 , typename Function
461 , typename ResultType = detail::mbind_cb_result_t<Function, E>
462 >
463 ResultType or_else( const expected<T, E>& exp, Function &&f)
464 {
465 if (!exp) {
466 return std::invoke( std::forward<Function>(f), exp.error() );
467 } else {
469 return exp;
470 else
471 return makeReadyResult( std::move(exp) );
472 }
473 }
474
475 template < typename T
476 , typename E
477 , typename Function
478 , typename ResultType = detail::mbind_cb_result_t<Function, E>
479 >
480 ResultType or_else( expected<T, E>&& exp, Function &&f)
481 {
482 if (!exp) {
483 return std::invoke( std::forward<Function>(f), std::move(exp.error()) );
484 } else {
486 return exp;
487 else
488 return makeReadyResult( std::move(exp) );
489 }
490 }
491
492
497 template < template< class, class... > class Container,
498 typename T,
499 typename E,
500 typename ...CArgs >
501 std::enable_if_t<!std::is_same_v<void, T>, expected<Container<T>,E>> collect( Container<expected<T, E>, CArgs...>&& in ) {
502 Container<T> res;
503 for( auto &v : in ) {
504 if ( !v )
505 return expected<Container<T>,E>::error( std::move(v.error()) );
506 res.push_back( std::move(v.get()) );
507 }
508 return expected<Container<T>,E>::success( std::move(res) );
509 }
510
515 template < template< class, class... > class Container,
516 typename T,
517 typename E,
518 typename ...CArgs >
519 std::enable_if_t<std::is_same_v<void, T>, expected<T, E>> collect( Container<expected<T, E>, CArgs...>&& in ) {
520 for( auto &v : in ) {
521 if ( !v )
522 return expected<T,E>::error( std::move(v.error()) );
523 }
524 return expected<T,E>::success( );
525 }
526
527 template < typename T
528 , typename E
529 , typename Function
530 >
532 {
533 if (exp) {
534 const auto &val = exp.get();
535 std::invoke( std::forward<Function>(f), val );
536 }
537 return exp;
538 }
539
540 template < typename T
541 , typename E
542 , typename Function
543 >
545 {
546 if (!exp) {
547 const auto &err = exp.error();
548 std::invoke( std::forward<Function>(f), err );
549 }
550 return exp;
551 }
552
553
554 namespace detail {
555
556 template <typename Callback>
558 Callback function;
559
560 template< typename T, typename E >
561 auto operator()( const expected<T, E>& exp ) {
562 return and_then( exp, function );
563 }
564
565 template< typename T, typename E >
567 return and_then( std::move(exp), function );
568 }
569 };
570
571 template <typename Callback>
573 Callback function;
574
575 template< typename T, typename E >
576 auto operator()( const expected<T, E>& exp ) {
577 return or_else( exp, function );
578 }
579
580 template< typename T, typename E >
582 return or_else( std::move(exp), function );
583 }
584 };
585
586 template <typename Callback>
588 Callback function;
589
590 template< typename T, typename E >
592 return inspect( std::move(exp), function );
593 }
594 };
595
596 template <typename Callback>
598 Callback function;
599
600 template< typename T, typename E >
602 return inspect_err( std::move(exp), function );
603 }
604 };
605
607 template < typename T >
608 inline auto operator()( T&& in ) {
609 return collect( std::forward<T>(in) );
610 }
611 };
612 }
613
614 namespace operators {
615 template <typename Fun>
616 auto mbind ( Fun && function ) {
618 std::forward<Fun>(function)
619 };
620 }
621
622 template <typename Fun>
623 auto and_then ( Fun && function ) {
625 std::forward<Fun>(function)
626 };
627 }
628
629 template <typename Fun>
630 auto or_else ( Fun && function ) {
632 std::forward<Fun>(function)
633 };
634 }
635
636 template <typename Fun>
637 auto inspect ( Fun && function ) {
639 std::forward<Fun>(function)
640 };
641 }
642
643 template <typename Fun>
644 auto inspect_err ( Fun && function ) {
646 std::forward<Fun>(function)
647 };
648 }
649
653 }
654
655
660 template < template< class, class... > class Container,
661 typename Msg,
662 typename Transformation,
663 typename Ret = std::result_of_t<Transformation(Msg)>,
664 typename ...CArgs
665 >
666 auto transform_collect( Container<Msg, CArgs...>&& in, Transformation &&f )
667 {
668 using namespace zyppng::operators;
669 if constexpr ( detail::is_async_op_v<Ret> ) {
670 using AsyncRet = typename remove_smart_ptr_t<Ret>::value_type;
671 static_assert( is_instance_of<expected, AsyncRet>::value, "Transformation function must return a expected type" );
672
673 return transform( std::move(in), f )
674 // cancel WaitFor if one of the async ops returns a error
676 | collect();
677
678 } else {
679 static_assert( is_instance_of<expected, Ret>::value, "Transformation function must return a expected type" );
680 Container<typename Ret::value_type> results;
681 for ( auto &v : in ) {
682 auto res = f(std::move(v));
683 if ( res ) {
684 results.push_back( std::move(res.get()) );
685 } else {
686 return expected<Container<typename Ret::value_type>>::error( res.error() );
687 }
688 }
689 return expected<Container<typename Ret::value_type>>::success( std::move(results) );
690 }
691 }
692
693 namespace detail {
694 template <typename Fun>
697 template <typename T>
698 auto operator() ( T &&in ) {
699 return transform_collect( std::forward<T>(in), _callback );
700 }
701 };
702 }
703
704 namespace operators {
705 template <typename Transformation>
706 auto transform_collect( Transformation &&f ) {
707 return detail::transform_collect_helper{ std::forward<Transformation>(f)};
708 }
709 }
710
711
712
713}
714
715#endif
716
const E & error() const
Definition expected.h:373
static expected success()
Definition expected.h:330
expected(expected &&other) noexcept
Definition expected.h:282
expected(const expected &other)
Definition expected.h:272
static expected error(ConsParams &&...params)
Definition expected.h:341
void swap(expected &other) noexcept
Definition expected.h:298
expected(expected &&other) noexcept
Definition expected.h:64
static expected success(ConsParams &&...params)
Definition expected.h:115
void visit(F f)
Definition expected.h:240
const E & error() const
Definition expected.h:231
static expected error(ConsParams &&...params)
Definition expected.h:126
std::pair< zypp::ServiceInfo, RepoInfoList > m_value
Definition expected.h:30
const T & unwrap() const
Definition expected.h:189
expected(const expected &other)
Definition expected.h:54
std::pair< zypp::ServiceInfo, RepoInfoList > value_type
Definition expected.h:42
const T & get() const
Definition expected.h:158
bool is_valid() const
Definition expected.h:141
#define THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED(WHAT)
Definition expected.h:149
#define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT)
Definition expected.h:364
Definition Arch.h:364
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
constexpr bool is_async_op_v
Definition asyncop.h:61
std::conjunction< has_value_type< remove_smart_ptr_t< T > >, is_asyncop_type< remove_smart_ptr_t< T > > > is_async_op
Definition asyncop.h:55
bool waitForCanContinueExpected(const expected< T > &value)
Definition expected.h:412
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:409
auto or_else(Fun &&function)
Definition expected.h:630
auto transform_collect(Transformation &&f)
Definition expected.h:706
auto and_then(Fun &&function)
Definition expected.h:623
auto inspect_err(Fun &&function)
Definition expected.h:644
detail::collect_helper collect()
Definition expected.h:650
auto inspect(Fun &&function)
Definition expected.h:637
auto mbind(Fun &&function)
Definition expected.h:616
auto transform_collect(Container< Msg, CArgs... > &&in, Transformation &&f)
Definition expected.h:666
std::conditional_t< isAsync, AsyncOpRef< T >, T > makeReadyResult(T &&result)
Definition asyncop.h:297
typename remove_smart_ptr< T >::type remove_smart_ptr_t
static expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
Definition expected.h:397
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition expected.h:463
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition expected.h:423
std::enable_if_t<!std::is_same_v< void, T >, expected< Container< T >, E > > collect(Container< expected< T, E >, CArgs... > &&in)
Definition expected.h:501
Container< Ret > transform(Container< Msg, CArgs... > &&val, Transformation &&transformation)
Definition transform.h:31
expected< T, E > inspect(expected< T, E > exp, Function &&f)
Definition expected.h:531
expected< T, E > inspect_err(expected< T, E > exp, Function &&f)
Definition expected.h:544
auto operator()(expected< T, E > &&exp)
Definition expected.h:566
auto operator()(const expected< T, E > &exp)
Definition expected.h:561
auto operator()(expected< T, E > &&exp)
Definition expected.h:601
auto operator()(expected< T, E > &&exp)
Definition expected.h:591
auto operator()(const expected< T, E > &exp)
Definition expected.h:576
auto operator()(expected< T, E > &&exp)
Definition expected.h:581
#define ZYPP_NODISCARD
Definition zyppglobal.h:226