Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
give s4u::Host a cname() that returns a char*
[simgrid.git] / include / xbt / future.hpp
1 /* Copyright (c) 2015-2016. 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::invalid:
127       default:
128         throw std::logic_error("Invalid result");
129       case ResultStatus::value: {
130         T value = std::move(value_);
131         value_.~T();
132         status_ = ResultStatus::invalid;
133         return std::move(value);
134       }
135       case ResultStatus::exception: {
136         std::exception_ptr exception = std::move(exception_);
137         exception_.~exception_ptr();
138         status_ = ResultStatus::invalid;
139         std::rethrow_exception(std::move(exception));
140         break;
141       }
142     }
143   }
144 private:
145   ResultStatus status_ = ResultStatus::invalid;
146   union {
147     T value_;
148     std::exception_ptr exception_;
149   };
150 };
151
152 template<>
153 class Result<void> : public Result<std::nullptr_t>
154 {
155 public:
156   void set_value()
157   {
158     Result<std::nullptr_t>::set_value(nullptr);
159   }
160   void get()
161   {
162     Result<std::nullptr_t>::get();
163   }
164 };
165
166 template<class T>
167 class Result<T&> : public Result<std::reference_wrapper<T>>
168 {
169 public:
170   void set_value(T& value)
171   {
172     Result<std::reference_wrapper<T>>::set_value(std::ref(value));
173   }
174   T& get()
175   {
176     return Result<std::reference_wrapper<T>>::get();
177   }
178 };
179
180 /** Execute some code and set a promise or result accordingly
181  *
182  *  Roughly this does:
183  *
184  *  <pre>
185  *  promise.set_value(code());
186  *  </pre>
187  *
188  *  but it takes care of exceptions and works with `void`.
189  *
190  *  We might need this when working with generic code because
191  *  the trivial implementation does not work with `void` (before C++1z).
192  *
193  *  @param    code  What we want to do
194  *  @param  promise Where to want to store the result
195  */
196 template<class R, class F>
197 auto fulfillPromise(R& promise, F&& code)
198 -> decltype(promise.set_value(code()))
199 {
200   try {
201     promise.set_value(std::forward<F>(code)());
202   }
203   catch(...) {
204     promise.set_exception(std::current_exception());
205   }
206 }
207
208 template<class P, class F>
209 auto fulfillPromise(P& promise, F&& code)
210 -> decltype(promise.set_value())
211 {
212   try {
213     std::forward<F>(code)();
214     promise.set_value();
215   }
216   catch(...) {
217     promise.set_exception(std::current_exception());
218   }
219 }
220
221 /** Set a promise/result from a future/result
222  *
223  *  Roughly this does:
224  *
225  *  <pre>promise.set_value(future);</pre>
226  *
227  *  but it takes care of exceptions and works with `void`.
228  *
229  *  We might need this when working with generic code because
230  *  the trivial implementation does not work with `void` (before C++1z).
231  *
232  *  @param promise output (a valid future or a result)
233  *  @param future  input (a ready/waitable future or a valid result)
234  */
235 template<class P, class F> inline
236 void setPromise(P& promise, F&& future)
237 {
238   fulfillPromise(promise, [&]{ return std::forward<F>(future).get(); });
239 }
240
241 }
242 }
243
244 #endif