Logo AND Algorithmique Numérique Distribuée

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