Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Sonar: dont't duplicate declarations.
[simgrid.git] / include / xbt / future.hpp
1 /* Copyright (c) 2015-2018. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #ifndef XBT_FUTURE_HPP
8 #define XBT_FUTURE_HPP
9
10 #include <cstddef>
11
12 #include <exception>
13 #include <functional>
14 #include <stdexcept>
15 #include <type_traits>
16 #include <utility>
17 #include <xbt/ex.h>
18
19 namespace simgrid {
20 namespace xbt {
21
22 /** A value or an exception (or nothing)
23  *
24  *  This is similar to `optional<expected<T>>`` but it with a Future/Promise
25  *  like API.
26  *
27  *  Also the name is not so great.
28  **/
29 template<class T>
30 class Result {
31   enum class ResultStatus {
32     invalid,
33     value,
34     exception,
35   };
36 public:
37   Result() { /* Nothing to do */}
38   ~Result() { this->reset(); }
39
40   // Copy (if T is copyable) and move:
41   Result(Result const& that)
42   {
43     (*this) = that;
44   }
45   Result& operator=(Result const& that)
46   {
47     this->reset();
48     switch (that.status_) {
49       case ResultStatus::invalid:
50         break;
51       case ResultStatus::value:
52         new (&value_) T(that.value);
53         break;
54       case ResultStatus::exception:
55         new (&exception_) T(that.exception);
56         break;
57       default:
58         THROW_IMPOSSIBLE;
59     }
60     return *this;
61   }
62   Result(Result&& that)
63   {
64     *this = std::move(that);
65   }
66   Result& operator=(Result&& that)
67   {
68     this->reset();
69     switch (that.status_) {
70       case ResultStatus::invalid:
71         break;
72       case ResultStatus::value:
73         new (&value_) T(std::move(that.value));
74         that.value.~T();
75         break;
76       case ResultStatus::exception:
77         new (&exception_) T(std::move(that.exception));
78         that.exception.~exception_ptr();
79         break;
80       default:
81         THROW_IMPOSSIBLE;
82     }
83     that.status_ = ResultStatus::invalid;
84     return *this;
85   }
86
87   bool is_valid() const
88   {
89     return status_ != ResultStatus::invalid;
90   }
91   void reset()
92   {
93     switch (status_) {
94       case ResultStatus::invalid:
95         break;
96       case ResultStatus::value:
97         value_.~T();
98         break;
99       case ResultStatus::exception:
100         exception_.~exception_ptr();
101         break;
102       default:
103         THROW_IMPOSSIBLE;
104     }
105     status_ = ResultStatus::invalid;
106   }
107   void set_exception(std::exception_ptr e)
108   {
109     this->reset();
110     new (&exception_) std::exception_ptr(std::move(e));
111     status_ = ResultStatus::exception;
112   }
113   void set_value(T&& value)
114   {
115     this->reset();
116     new (&value_) T(std::move(value));
117     status_ = ResultStatus::value;
118   }
119   void set_value(T const& value)
120   {
121     this->reset();
122     new (&value_) T(value);
123     status_ = ResultStatus::value;
124   }
125
126   /** Extract the value from the future
127    *
128    *  After this, the value is invalid.
129    **/
130   T get()
131   {
132     switch (status_) {
133       case ResultStatus::value: {
134         T value = std::move(value_);
135         value_.~T();
136         status_ = ResultStatus::invalid;
137         return std::move(value);
138       }
139       case ResultStatus::exception: {
140         std::exception_ptr exception = std::move(exception_);
141         exception_.~exception_ptr();
142         status_ = ResultStatus::invalid;
143         std::rethrow_exception(std::move(exception));
144         break;
145       }
146       default:
147         throw std::logic_error("Invalid result");
148     }
149   }
150 private:
151   ResultStatus status_ = ResultStatus::invalid;
152   union {
153     T value_;
154     std::exception_ptr exception_;
155   };
156 };
157
158 template<>
159 class Result<void> : public Result<std::nullptr_t>
160 {
161 public:
162   void set_value()
163   {
164     Result<std::nullptr_t>::set_value(nullptr);
165   }
166   void get()
167   {
168     Result<std::nullptr_t>::get();
169   }
170 };
171
172 template<class T>
173 class Result<T&> : public Result<std::reference_wrapper<T>>
174 {
175 public:
176   void set_value(T& value)
177   {
178     Result<std::reference_wrapper<T>>::set_value(std::ref(value));
179   }
180   T& get()
181   {
182     return Result<std::reference_wrapper<T>>::get();
183   }
184 };
185
186 /** Execute some code and set a promise or result accordingly
187  *
188  *  Roughly this does:
189  *
190  *  <pre>
191  *  promise.set_value(code());
192  *  </pre>
193  *
194  *  but it takes care of exceptions and works with `void`.
195  *
196  *  We might need this when working with generic code because
197  *  the trivial implementation does not work with `void` (before C++1z).
198  *
199  *  @param    code  What we want to do
200  *  @param  promise Where to want to store the result
201  */
202 template<class R, class F>
203 auto fulfillPromise(R& promise, F&& code)
204 -> decltype(promise.set_value(code()))
205 {
206   try {
207     promise.set_value(std::forward<F>(code)());
208   }
209   catch(...) {
210     promise.set_exception(std::current_exception());
211   }
212 }
213
214 template<class P, class F>
215 auto fulfillPromise(P& promise, F&& code)
216 -> decltype(promise.set_value())
217 {
218   try {
219     std::forward<F>(code)();
220     promise.set_value();
221   }
222   catch(...) {
223     promise.set_exception(std::current_exception());
224   }
225 }
226
227 /** Set a promise/result from a future/result
228  *
229  *  Roughly this does:
230  *
231  *  <pre>promise.set_value(future);</pre>
232  *
233  *  but it takes care of exceptions and works with `void`.
234  *
235  *  We might need this when working with generic code because
236  *  the trivial implementation does not work with `void` (before C++1z).
237  *
238  *  @param promise output (a valid future or a result)
239  *  @param future  input (a ready/waitable future or a valid result)
240  */
241 template<class P, class F> inline
242 void setPromise(P& promise, F&& future)
243 {
244   fulfillPromise(promise, [&]{ return std::forward<F>(future).get(); });
245 }
246
247 }
248 }
249
250 #endif