Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
take 2 on declare_flag, not sure it's better... a bit weird
[simgrid.git] / include / xbt / promise.hpp
1 /* Copyright (c) 2015-2022. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #ifndef XBT_PROMISE_HPP
7 #define XBT_PROMISE_HPP
8
9 #include <cstddef>
10
11 #include <boost/variant.hpp>
12 #include <exception>
13 #include <functional>
14 #include <future> // std::future_error
15 #include <stdexcept>
16 #include <type_traits>
17 #include <utility>
18 #include <xbt/ex.h>
19
20 namespace simgrid {
21 namespace xbt {
22
23 /** A value or an exception (or nothing)
24  *
25  *  This is similar to `optional<expected<T>>`` but it with a Future/Promise
26  *  like API.
27  *
28  *  Also the name is not so great.
29  **/
30 template <class T> class Result {
31 public:
32   bool is_valid() const { return value_.which() > 0; }
33   void set_exception(std::exception_ptr e) { value_ = std::move(e); }
34   void set_value(T&& value) { value_ = std::move(value); }
35   void set_value(T const& value) { value_ = value; }
36
37   /** Extract the value from the future
38    *
39    *  After this, the value is invalid.
40    **/
41   T get()
42   {
43     switch (value_.which()) {
44       case 1: {
45         T value = std::move(boost::get<T>(value_));
46         value_  = boost::blank();
47         return value;
48       }
49       case 2: {
50         std::exception_ptr exception = std::move(boost::get<std::exception_ptr>(value_));
51         value_                       = boost::blank();
52         std::rethrow_exception(std::move(exception));
53         break;
54       }
55       default:
56         throw std::future_error(std::future_errc::no_state);
57     }
58   }
59
60 private:
61   boost::variant<boost::blank, T, std::exception_ptr> value_;
62 };
63
64 template <> class Result<void> : public Result<std::nullptr_t> {
65 public:
66   void set_value() { Result<std::nullptr_t>::set_value(nullptr); }
67   void get() { Result<std::nullptr_t>::get(); }
68 };
69
70 template <class T> class Result<T&> : public Result<std::reference_wrapper<T>> {
71 public:
72   void set_value(T& value) { Result<std::reference_wrapper<T>>::set_value(std::ref(value)); }
73   T& get() { return Result<std::reference_wrapper<T>>::get(); }
74 };
75
76 /** Execute some code and set a promise or result accordingly
77  *
78  *  Roughly this does:
79  *
80  *  <pre>
81  *  promise.set_value(code());
82  *  </pre>
83  *
84  *  but it takes care of exceptions and works with `void`.
85  *
86  *  We might need this when working with generic code because
87  *  the trivial implementation does not work with `void` (before C++1z).
88  *
89  *  @param    code  What we want to do
90  *  @param  promise Where to want to store the result
91  */
92 template <class R, class F> auto fulfill_promise(R& promise, F&& code) -> decltype(promise.set_value(code()))
93 {
94   try {
95     promise.set_value(std::forward<F>(code)());
96   } catch (...) {
97     promise.set_exception(std::current_exception());
98   }
99 }
100
101 template <class R, class F> auto fulfill_promise(R& promise, F&& code) -> decltype(promise.set_value())
102 {
103   try {
104     std::forward<F>(code)();
105     promise.set_value();
106   } catch (...) {
107     promise.set_exception(std::current_exception());
108   }
109 }
110
111 /** Set a promise/result from a future/result
112  *
113  *  Roughly this does:
114  *
115  *  <pre>promise.set_value(future);</pre>
116  *
117  *  but it takes care of exceptions and works with `void`.
118  *
119  *  We might need this when working with generic code because
120  *  the trivial implementation does not work with `void` (before C++1z).
121  *
122  *  @param promise output (a valid future or a result)
123  *  @param future  input (a ready/waitable future or a valid result)
124  */
125 template <class P, class F> inline void set_promise(P& promise, F&& future)
126 {
127   fulfill_promise(promise, [&future] { return std::forward<F>(future).get(); });
128 }
129 }
130 }
131
132 #endif