libzypp  17.35.15
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 
24 namespace zyppng {
25 
26  template<typename T, typename E = std::exception_ptr>
28  protected:
29  union {
32  };
33 
34  bool m_isValid;
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 
173  T &unwrap()
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 
258  bool m_isValid;
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 
330  static expected success()
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 {
431  if constexpr ( !detail::is_async_op< remove_smart_ptr_t<ResultType> >::value )
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 {
468  if constexpr ( !detail::is_async_op< remove_smart_ptr_t<ResultType> >::value )
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 {
485  if constexpr ( !detail::is_async_op< remove_smart_ptr_t<ResultType> >::value )
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  >
531  expected<T, E> inspect( expected<T, E> exp, Function &&f )
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 >
566  auto operator()( expected<T, E>&& exp ) {
567  return and_then( std::move(exp), function );
568  }
569  };
570 
571  template <typename Callback>
572  struct or_else_helper {
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 >
581  auto operator()( expected<T, E>&& exp ) {
582  return or_else( std::move(exp), function );
583  }
584  };
585 
586  template <typename Callback>
587  struct inspect_helper {
588  Callback function;
589 
590  template< typename T, typename E >
591  auto operator()( expected<T, E>&& exp ) {
592  return inspect( std::move(exp), function );
593  }
594  };
595 
596  template <typename Callback>
598  Callback function;
599 
600  template< typename T, typename E >
601  auto operator()( expected<T, E>&& exp ) {
602  return inspect_err( std::move(exp), function );
603  }
604  };
605 
606  struct collect_helper {
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 
651  return detail::collect_helper();
652  }
653  }
654 
655 
660  template < template< class, class... > class Container,
661  typename Msg,
662  typename Transformation,
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
675  | detail::WaitForHelperExt<AsyncRet>( detail::waitForCanContinueExpected<typename AsyncRet::value_type> )
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 
auto transform(Transformation &&transformation)
Definition: transform.h:70
static expected success()
Definition: expected.h:330
auto operator()(expected< T, E > &&exp)
Definition: expected.h:591
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
auto inspect_err(Fun &&function)
Definition: expected.h:644
expected< T, E > inspect(expected< T, E > exp, Function &&f)
Definition: expected.h:531
static expected error(ConsParams &&...params)
Definition: expected.h:341
const E & error() const
Definition: expected.h:373
const E & error() const
Definition: expected.h:231
Definition: Arch.h:363
static expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
Definition: expected.h:397
void visit(F f)
Definition: expected.h:240
auto mbind(Fun &&function)
Definition: expected.h:616
static expected error(ConsParams &&...params)
Definition: expected.h:126
auto operator()(expected< T, E > &&exp)
Definition: expected.h:566
auto inspect(Fun &&function)
Definition: expected.h:637
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:149
auto operator()(const expected< T, E > &exp)
Definition: expected.h:576
bool is_valid() const
Definition: expected.h:141
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition: expected.h:463
expected(const expected &other)
Definition: expected.h:272
void swap(expected &other) noexcept
Definition: expected.h:298
auto or_else(Fun &&function)
Definition: expected.h:630
expected(const expected &other)
Definition: expected.h:54
expected(expected &&other) noexcept
Definition: expected.h:64
auto operator()(expected< T, E > &&exp)
Definition: expected.h:581
typename enable_if< B, T >::type enable_if_t
Definition: TypeTraits.h:45
auto transform_collect(Container< Msg, CArgs... > &&in, Transformation &&f)
Definition: expected.h:666
expected< T, E > inspect_err(expected< T, E > exp, Function &&f)
Definition: expected.h:544
bool waitForCanContinueExpected(const expected< T > &value)
Definition: expected.h:412
typename conditional< B, T, F >::type conditional_t
Definition: TypeTraits.h:39
std::conditional_t< isAsync, AsyncOpRef< T >, T > makeReadyResult(T &&result)
Definition: asyncop.h:297
static expected success(ConsParams &&...params)
Definition: expected.h:115
detail::collect_helper collect()
Definition: expected.h:650
const T & unwrap() const
Definition: expected.h:189
#define ZYPP_NODISCARD
Definition: zyppglobal.h:226
void swap(expected &other) noexcept
Definition: expected.h:80
typename result_of< T >::type result_of_t
Definition: TypeTraits.h:51
auto transform_collect(Transformation &&f)
Definition: expected.h:706
auto operator()(expected< T, E > &&exp)
Definition: expected.h:601
auto and_then(Fun &&function)
Definition: expected.h:623
auto operator()(const expected< T, E > &exp)
Definition: expected.h:561
expected(expected &&other) noexcept
Definition: expected.h:282
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition: expected.h:423
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
#define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT)
Definition: expected.h:364
typename remove_smart_ptr< T >::type remove_smart_ptr_t
Definition: type_traits.h:128