libzypp  17.38.7
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 
26 namespace 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;
38  unexpected &operator=(unexpected &&) = default;
39 
40  const T &error() const {
41  return _err;
42  }
43 
44  T &error() {
45  return _err;
46  }
47 
48  private:
49  T _err;
50  };
51 
52  template<class E>
54 
55  template<typename T, typename E = std::exception_ptr>
57  protected:
58  union {
61  };
62 
63  bool m_isValid;
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> >
137  expected& operator=( U&& v )
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 
236  T &unwrap()
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 
321  bool m_isValid;
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 
408  static expected success()
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 {
528  return detail::expected_make_error<ResultType>(exp.error());
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  >
616  expected<T, E> inspect( expected<T, E> exp, Function &&f )
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 >
651  auto operator()( expected<T, E>&& exp ) {
652  return and_then( std::move(exp), function );
653  }
654  };
655 
656  template <typename Callback>
657  struct or_else_helper {
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 >
666  auto operator()( expected<T, E>&& exp ) {
667  return or_else( std::move(exp), function );
668  }
669  };
670 
671  template <typename Callback>
672  struct inspect_helper {
673  Callback function;
674 
675  template< typename T, typename E >
676  auto operator()( expected<T, E>&& exp ) {
677  return inspect( std::move(exp), function );
678  }
679  };
680 
681  template <typename Callback>
683  Callback function;
684 
685  template< typename T, typename E >
686  auto operator()( expected<T, E>&& exp ) {
687  return inspect_err( std::move(exp), function );
688  }
689  };
690 
691  struct collect_helper {
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 
736  return detail::collect_helper();
737  }
738  }
739 
740 
745  template < template< class, class... > class Container,
746  typename Msg,
747  typename Transformation,
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 
ResultType expected_make_error(E &&error)
Definition: expected.h:503
static expected success()
Definition: expected.h:408
auto operator()(expected< T, E > &&exp)
Definition: expected.h:676
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
auto inspect_err(Fun &&function)
Definition: expected.h:729
expected< T, E > inspect(expected< T, E > exp, Function &&f)
Definition: expected.h:616
static expected error(ConsParams &&...params)
Definition: expected.h:414
const E & error() const
Definition: expected.h:446
const E & error() const
Definition: expected.h:294
Definition: ansi.h:854
static expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
Definition: expected.h:470
void visit(F f)
Definition: expected.h:303
auto mbind(Fun &&function)
Definition: expected.h:701
expected & operator=(U &&v)
Definition: expected.h:137
static expected error(ConsParams &&...params)
Definition: expected.h:189
auto operator()(expected< T, E > &&exp)
Definition: expected.h:651
auto inspect(Fun &&function)
Definition: expected.h:722
std::enable_if< std::is_member_pointer< typename std::decay< Functor >::type >::value, typename std::result_of< Functor &&(Args &&...)>::type >::type invoke(Functor &&f, Args &&... args)
Definition: functional.h:32
#define THROW_MSG_IF_EXCEPTIONS_ARE_ENABLED(WHAT)
Definition: expected.h:212
unexpected(E &&err)
Definition: expected.h:32
auto operator()(const expected< T, E > &exp)
Definition: expected.h:661
~unexpected()=default
bool is_valid() const
Definition: expected.h:204
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition: expected.h:554
static ResultType error(E &&err)
Definition: expected.h:492
expected(const expected &other)
Definition: expected.h:342
void swap(expected &other) noexcept
Definition: expected.h:376
auto or_else(Fun &&function)
Definition: expected.h:715
expected(const expected &other)
Definition: expected.h:103
expected(expected &&other) noexcept
Definition: expected.h:113
auto operator()(expected< T, E > &&exp)
Definition: expected.h:666
typename enable_if< B, T >::type enable_if_t
Definition: TypeTraits.h:45
expected< T, E > inspect_err(expected< T, E > exp, Function &&f)
Definition: expected.h:629
expected(const T &v)
Definition: expected.h:83
unexpected & operator=(const unexpected &)=default
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
bool waitForCanContinueExpected(const expected< T > &value)
Definition: expected.h:485
typename conditional< B, T, F >::type conditional_t
Definition: TypeTraits.h:39
unexpected(E) -> unexpected< E >
expected(unexpected< E > &&err) noexcept
Definition: expected.h:98
static expected success(ConsParams &&...params)
Definition: expected.h:178
detail::collect_helper collect()
Definition: expected.h:735
expected(T &&v)
Definition: expected.h:88
const T & unwrap() const
Definition: expected.h:252
ResultType expected_forward(ResultType &&res)
Definition: expected.h:508
expected(unexpected< G > err) noexcept
Definition: expected.h:328
#define ZYPP_NODISCARD
Definition: zyppglobal.h:219
static ResultType forward(Res &&exp)
Definition: expected.h:497
void swap(expected &other) noexcept
Definition: expected.h:143
typename result_of< T >::type result_of_t
Definition: TypeTraits.h:51
auto transform_collect(Transformation &&f)
Definition: expected.h:778
auto operator()(expected< T, E > &&exp)
Definition: expected.h:686
auto and_then(Fun &&function)
Definition: expected.h:708
auto operator()(const expected< T, E > &exp)
Definition: expected.h:646
expected(const unexpected< E > &err) noexcept
Definition: expected.h:93
expected(expected &&other) noexcept
Definition: expected.h:352
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition: expected.h:520
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
#define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT)
Definition: expected.h:437
const T & error() const
Definition: expected.h:40