libzypp 17.34.0
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>
27 class [[nodiscard]] expected {
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
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
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>
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>
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
165 {
166 return get();
167 }
168
169 const T &operator* () const
170 {
171 return get();
172 }
173
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
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
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
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
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>
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 >
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 {
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 >
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 >
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 >
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 {
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 ) {
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 >
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 >
493 return and_then( exp, function );
494 }
495
496 template< typename T, typename E >
498 return and_then( std::move(exp), function );
499 }
500 };
501
502 template <typename Callback>
504 Callback function;
505
506 template< typename T, typename E >
508 return or_else( exp, function );
509 }
510
511 template< typename T, typename E >
513 return or_else( std::move(exp), function );
514 }
515 };
516
517 template <typename Callback>
519 Callback function;
520
521 template< typename T, typename E >
523 return inspect( std::move(exp), function );
524 }
525 };
526
527 template <typename Callback>
529 Callback function;
530
531 template< typename T, typename E >
533 return inspect_err( std::move(exp), function );
534 }
535 };
536
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
584 }
585
586
591 template < template< class, class... > class Container,
592 typename Msg,
593 typename Transformation,
594 typename Ret = std::result_of_t<Transformation(Msg)>,
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> ) {
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" );
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
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition AutoDispose.h:95
value_type * operator->() const
Pointer to the Tp object (asserted to be != NULL).
void swap(AutoDispose &rhs) noexcept
Exchange the contents of two AutoDispose objects.
AutoDispose()
Default Ctor using default constructed value and no dispose function.
reference operator*() const
Reference to the Tp object.
const E & error() const
Definition expected.h:332
static expected success()
Definition expected.h:289
expected(expected &&other) noexcept
Definition expected.h:241
expected(const expected &other)
Definition expected.h:231
static expected error(ConsParams &&...params)
Definition expected.h:300
void swap(expected &other) noexcept
Definition expected.h:257
expected(expected &&other) noexcept
Definition expected.h:64
static expected success(ConsParams &&...params)
Definition expected.h:115
void visit(F f)
Definition expected.h:199
const E & error() const
Definition expected.h:190
static expected error(ConsParams &&...params)
Definition expected.h:126
expected(const expected &other)
Definition expected.h:54
void swap(expected &other) noexcept
Definition expected.h:80
const T & get() const
Definition expected.h:158
bool is_valid() const
Definition expected.h:141
#define THROW_IF_EXCEPTIONS_ARE_ENABLED(WHAT)
Definition expected.h:149
std::enable_if< std::is_member_pointer< typenamestd::decay< Functor >::type >::value, typenamestd::result_of< Functor &&(Args &&...)>::type >::typ invoke)(Functor &&f, Args &&... args)
Definition functional.h:32
typename conditional< B, T, F >::type conditional_t
Definition TypeTraits.h:39
typename result_of< T >::type result_of_t
Definition TypeTraits.h:48
bool waitForCanContinueExpected(const expected< T > &value)
Definition expected.h:356
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
auto or_else(Fun &&function)
Definition expected.h:561
auto transform_collect(Transformation &&f)
Definition expected.h:637
auto and_then(Fun &&function)
Definition expected.h:554
auto inspect_err(Fun &&function)
Definition expected.h:575
detail::collect_helper collect()
Definition expected.h:581
auto inspect(Fun &&function)
Definition expected.h:568
auto mbind(Fun &&function)
Definition expected.h:547
auto transform_collect(Container< Msg, CArgs... > &&in, Transformation &&f)
Definition expected.h:597
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:341
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition expected.h:407
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition expected.h:367
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:462
expected< T, E > inspect_err(expected< T, E > exp, Function &&f)
Definition expected.h:475
auto operator()(expected< T, E > &&exp)
Definition expected.h:497
auto operator()(const expected< T, E > &exp)
Definition expected.h:492
auto operator()(expected< T, E > &&exp)
Definition expected.h:532
auto operator()(expected< T, E > &&exp)
Definition expected.h:522
auto operator()(const expected< T, E > &exp)
Definition expected.h:507
auto operator()(expected< T, E > &&exp)
Definition expected.h:512