Logo AND Algorithmique Numérique Distribuée

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