libzypp  17.34.1
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>
27  class [[nodiscard]] expected {
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_IF_EXCEPTIONS_ARE_ENABLED(WHAT) std::terminate()
148  #else
149  # define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) throw std::logic_error(WHAT)
150  #endif
151 
152  T &get()
153  {
154  if (!m_isValid) THROW_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_IF_EXCEPTIONS_ARE_ENABLED("expected<T, E> contains no value");
161  return m_value;
162  }
163 
164  T &operator* ()
165  {
166  return get();
167  }
168 
169  const T &operator* () const
170  {
171  return get();
172  }
173 
174  T *operator-> ()
175  {
176  return &get();
177  }
178 
179  const T *operator-> () const
180  {
181  return &get();
182  }
183 
184  E &error()
185  {
186  if (m_isValid) THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
187  return m_error;
188  }
189 
190  const E &error() const
191  {
192  if (m_isValid) THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
193  return m_error;
194  }
195 
196  #undef THROW_IF_EXCEPTIONS_ARE_ENABLED
197 
198  template <typename F>
199  void visit(F f) {
200  if (m_isValid) {
201  f(m_value);
202  } else {
203  f(m_error);
204  }
205  }
206  };
207 
208 
209  template<typename E>
210  class [[nodiscard]] expected<void, E> {
211  private:
212  union {
213  void* m_value;
215  };
216 
217  bool m_isValid;
218 
219  expected() {} //used internally
220 
221  public:
223  {
224  if (m_isValid) {
225  // m_value.~T();
226  } else {
227  m_error.~E();
228  }
229  }
230 
231  expected(const expected &other)
232  : m_isValid(other.m_isValid)
233  {
234  if (m_isValid) {
235  // new (&m_value) T(other.m_value);
236  } else {
237  new (&m_error) E(other.m_error);
238  }
239  }
240 
241  expected(expected &&other) noexcept
242  : m_isValid(other.m_isValid)
243  {
244  if (m_isValid) {
245  // new (&m_value) T(std::move(other.m_value));
246  } else {
247  new (&m_error) E(std::move(other.m_error));
248  }
249  }
250 
251  expected &operator= (expected other)
252  {
253  swap(other);
254  return *this;
255  }
256 
257  void swap(expected &other) noexcept
258  {
259  using std::swap;
260  if (m_isValid) {
261  if (other.m_isValid) {
262  // Both are valid, we do not have any values
263  // to swap
264 
265  } else {
266  // We are valid, but the other one is not.
267  // We need to move the error into us
268  auto temp = std::move(other.m_error); // moving the error into the temp
269  other.m_error.~E(); // destroying the original error object
270  new (&m_error) E(std::move(temp)); // moving the error into us
271  std::swap(m_isValid, other.m_isValid); // swapping the isValid flags
272  }
273 
274  } else {
275  if (other.m_isValid) {
276  // We are not valid, but the other one is,
277  // just call swap on other and rely on the
278  // implementation in the previous case
279  other.swap(*this);
280 
281  } else {
282  // Everything is rotten, just swap the errors
283  swap(m_error, other.m_error);
284  std::swap(m_isValid, other.m_isValid);
285  }
286  }
287  }
288 
289  static expected success()
290  {
291  // silence clang-tidy about uninitialized class members, we manually intialize them.
292  // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
293  expected result;
294  result.m_isValid = true;
295  result.m_value = nullptr;
296  return result;
297  }
298 
299  template <typename... ConsParams>
300  static expected error(ConsParams && ...params)
301  {
302  // silence clang-tidy about uninitialized class members, we manually intialize them.
303  // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
304  expected result;
305  result.m_isValid = false;
306  new(&result.m_error) E(std::forward<ConsParams>(params)...);
307  return result;
308  }
309 
310  operator bool() const
311  {
312  return m_isValid;
313  }
314 
315  bool is_valid() const
316  {
317  return m_isValid;
318  };
319 
320  #ifdef NO_EXCEPTIONS
321  # define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) std::terminate()
322  #else
323  # define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT) throw std::logic_error(WHAT)
324  #endif
325 
326  E &error()
327  {
328  if (m_isValid) THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
329  return m_error;
330  }
331 
332  const E &error() const
333  {
334  if (m_isValid) THROW_IF_EXCEPTIONS_ARE_ENABLED("There is no error in this expected<T, E>");
335  return m_error;
336  }
337 
338  };
339 
340  template <typename Type, typename Err = std::exception_ptr >
342  {
343  return expected<std::decay_t<Type>,Err>::success( std::forward<Type>(t) );
344  }
345 
346  namespace detail {
347 
348  // helper to figure out the return type for a mbind callback, if the ArgType is void the callback is considered to take no argument.
349  // Due to how std::conditional works, we cannot pass std::invoke_result_t but instead use the template type std::invoke_result, since
350  // 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
351  // types. Instead we pass in the template types and evaluate the ::type in the end, when the correct invoke_result was chosen.
352  template < typename Function, typename ArgType>
353  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;
354 
355  template <typename T>
357  return value.is_valid();
358  }
359  }
360 
361 
362  template < typename T
363  , typename E
364  , typename Function
365  , typename ResultType = detail::mbind_cb_result_t<Function, T>
366  >
367  ResultType and_then( const expected<T, E>& exp, Function &&f)
368  {
369  if (exp) {
370  if constexpr ( std::is_same_v<T,void> )
371  return std::invoke( std::forward<Function>(f) );
372  else
373  return std::invoke( std::forward<Function>(f), exp.get() );
374  } else {
375  if constexpr ( !detail::is_async_op< remove_smart_ptr_t<ResultType> >::value )
376  return ResultType::error(exp.error());
377  else
379  }
380  }
381 
382  template < typename T
383  , typename E
384  , typename Function
385  , typename ResultType = detail::mbind_cb_result_t<Function, T>
386  >
387  ResultType and_then( expected<T, E> &&exp, Function &&f)
388  {
389  if (exp) {
390  if constexpr ( std::is_same_v<T,void> )
391  return std::invoke( std::forward<Function>(f) );
392  else
393  return std::invoke( std::forward<Function>(f), std::move(exp.get()) );
394  } else {
396  return ResultType::error( std::move(exp.error()) );
397  else
399  }
400  }
401 
402  template < typename T
403  , typename E
404  , typename Function
405  , typename ResultType = detail::mbind_cb_result_t<Function, E>
406  >
407  ResultType or_else( const expected<T, E>& exp, Function &&f)
408  {
409  if (!exp) {
410  if constexpr ( std::is_same_v<T,void> )
411  return std::invoke( std::forward<Function>(f) );
412  else
413  return std::invoke( std::forward<Function>(f), exp.error() );
414  } else {
415  return exp;
416  }
417  }
418 
419  template < typename T
420  , typename E
421  , typename Function
422  , typename ResultType = detail::mbind_cb_result_t<Function, E>
423  >
424  ResultType or_else( expected<T, E>&& exp, Function &&f)
425  {
426  if (!exp) {
427  if constexpr ( std::is_same_v<T,void> )
428  return std::invoke( std::forward<Function>(f) );
429  else
430  return std::invoke( std::forward<Function>(f), std::move(exp.error()) );
431  } else {
432  if constexpr ( !detail::is_async_op< remove_smart_ptr_t<ResultType> >::value )
433  return exp;
434  else
435  return makeReadyResult( std::move(exp) );
436  }
437  }
438 
439 
444  template < template< class, class... > class Container,
445  typename T,
446  typename E,
447  typename ...CArgs >
448  expected<Container<T>, E> collect( Container<expected<T, E>, CArgs...>&& in ) {
449  Container<T> res;
450  for( auto &v : in ) {
451  if ( !v )
452  return expected<Container<T>,E>::error( std::move(v.error()) );
453  res.push_back( std::move(v.get()) );
454  }
455  return expected<Container<T>,E>::success( std::move(res) );
456  }
457 
458  template < typename T
459  , typename E
460  , typename Function
461  >
462  expected<T, E> inspect( expected<T, E> exp, Function &&f )
463  {
464  if (exp) {
465  const auto &val = exp.get();
466  std::invoke( std::forward<Function>(f), val );
467  }
468  return exp;
469  }
470 
471  template < typename T
472  , typename E
473  , typename Function
474  >
476  {
477  if (!exp) {
478  const auto &err = exp.error();
479  std::invoke( std::forward<Function>(f), err );
480  }
481  return exp;
482  }
483 
484 
485  namespace detail {
486 
487  template <typename Callback>
489  Callback function;
490 
491  template< typename T, typename E >
492  auto operator()( const expected<T, E>& exp ) {
493  return and_then( exp, function );
494  }
495 
496  template< typename T, typename E >
497  auto operator()( expected<T, E>&& exp ) {
498  return and_then( std::move(exp), function );
499  }
500  };
501 
502  template <typename Callback>
503  struct or_else_helper {
504  Callback function;
505 
506  template< typename T, typename E >
507  auto operator()( const expected<T, E>& exp ) {
508  return or_else( exp, function );
509  }
510 
511  template< typename T, typename E >
512  auto operator()( expected<T, E>&& exp ) {
513  return or_else( std::move(exp), function );
514  }
515  };
516 
517  template <typename Callback>
518  struct inspect_helper {
519  Callback function;
520 
521  template< typename T, typename E >
522  auto operator()( expected<T, E>&& exp ) {
523  return inspect( std::move(exp), function );
524  }
525  };
526 
527  template <typename Callback>
529  Callback function;
530 
531  template< typename T, typename E >
532  auto operator()( expected<T, E>&& exp ) {
533  return inspect_err( std::move(exp), function );
534  }
535  };
536 
537  struct collect_helper {
538  template < typename T >
539  inline auto operator()( T&& in ) {
540  return collect( std::forward<T>(in) );
541  }
542  };
543  }
544 
545  namespace operators {
546  template <typename Fun>
547  auto mbind ( Fun && function ) {
549  std::forward<Fun>(function)
550  };
551  }
552 
553  template <typename Fun>
554  auto and_then ( Fun && function ) {
556  std::forward<Fun>(function)
557  };
558  }
559 
560  template <typename Fun>
561  auto or_else ( Fun && function ) {
563  std::forward<Fun>(function)
564  };
565  }
566 
567  template <typename Fun>
568  auto inspect ( Fun && function ) {
570  std::forward<Fun>(function)
571  };
572  }
573 
574  template <typename Fun>
575  auto inspect_err ( Fun && function ) {
577  std::forward<Fun>(function)
578  };
579  }
580 
582  return detail::collect_helper();
583  }
584  }
585 
586 
591  template < template< class, class... > class Container,
592  typename Msg,
593  typename Transformation,
595  typename ...CArgs
596  >
597  auto transform_collect( Container<Msg, CArgs...>&& in, Transformation &&f )
598  {
599  using namespace zyppng::operators;
600  if constexpr ( detail::is_async_op_v<Ret> ) {
601  using AsyncRet = typename remove_smart_ptr_t<Ret>::value_type;
602  static_assert( is_instance_of<expected, AsyncRet>::value, "Transformation function must return a expected type" );
603 
604  return transform( std::move(in), f )
605  // cancel WaitFor if one of the async ops returns a error
606  | detail::WaitForHelperExt<AsyncRet>( detail::waitForCanContinueExpected<typename AsyncRet::value_type> )
607  | collect();
608 
609  } else {
610  static_assert( is_instance_of<expected, Ret>::value, "Transformation function must return a expected type" );
611  Container<typename Ret::value_type> results;
612  for ( auto &v : in ) {
613  auto res = f(std::move(v));
614  if ( res ) {
615  results.push_back( std::move(res.get()) );
616  } else {
617  return expected<Container<typename Ret::value_type>>::error( res.error() );
618  }
619  }
620  return expected<Container<typename Ret::value_type>>::success( std::move(results) );
621  }
622  }
623 
624  namespace detail {
625  template <typename Fun>
628  template <typename T>
629  auto operator() ( T &&in ) {
630  return transform_collect( std::forward<T>(in), _callback );
631  }
632  };
633  }
634 
635  namespace operators {
636  template <typename Transformation>
637  auto transform_collect( Transformation &&f ) {
638  return detail::transform_collect_helper{ std::forward<Transformation>(f)};
639  }
640  }
641 
642 
643 
644 }
645 
646 #endif
647 
auto transform(Transformation &&transformation)
Definition: transform.h:70
static expected success()
Definition: expected.h:289
auto operator()(expected< T, E > &&exp)
Definition: expected.h:522
auto inspect_err(Fun &&function)
Definition: expected.h:575
expected< T, E > inspect(expected< T, E > exp, Function &&f)
Definition: expected.h:462
static expected error(ConsParams &&...params)
Definition: expected.h:300
const E & error() const
Definition: expected.h:332
const E & error() const
Definition: expected.h:190
static expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
Definition: expected.h:341
void visit(F f)
Definition: expected.h:199
auto mbind(Fun &&function)
Definition: expected.h:547
static expected error(ConsParams &&...params)
Definition: expected.h:126
auto operator()(expected< T, E > &&exp)
Definition: expected.h:497
auto inspect(Fun &&function)
Definition: expected.h:568
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
auto operator()(const expected< T, E > &exp)
Definition: expected.h:507
bool is_valid() const
Definition: expected.h:141
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition: expected.h:407
expected(const expected &other)
Definition: expected.h:231
void swap(expected &other) noexcept
Definition: expected.h:257
auto or_else(Fun &&function)
Definition: expected.h:561
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:512
auto transform_collect(Container< Msg, CArgs... > &&in, Transformation &&f)
Definition: expected.h:597
expected< T, E > inspect_err(expected< T, E > exp, Function &&f)
Definition: expected.h:475
bool waitForCanContinueExpected(const expected< T > &value)
Definition: expected.h:356
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
expected< Container< T >, E > collect(Container< expected< T, E >, CArgs... > &&in)
Definition: expected.h:448
static expected success(ConsParams &&...params)
Definition: expected.h:115
detail::collect_helper collect()
Definition: expected.h:581
void swap(expected &other) noexcept
Definition: expected.h:80
typename result_of< T >::type result_of_t
Definition: TypeTraits.h:48
auto transform_collect(Transformation &&f)
Definition: expected.h:637
auto operator()(expected< T, E > &&exp)
Definition: expected.h:532
auto and_then(Fun &&function)
Definition: expected.h:554
auto operator()(const expected< T, E > &exp)
Definition: expected.h:492
expected(expected &&other) noexcept
Definition: expected.h:241
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition: expected.h:367
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:353
#define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT)
Definition: expected.h:323
typename remove_smart_ptr< T >::type remove_smart_ptr_t
Definition: type_traits.h:128