Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
partial revert of e16e0233
[simgrid.git] / include / xbt / future.hpp
1 /* Copyright (c) 2015-2019. 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 <boost/variant.hpp>
13 #include <exception>
14 #include <functional>
15 #include <future> // std::future_error
16 #include <stdexcept>
17 #include <type_traits>
18 #include <utility>
19 #include <xbt/ex.h>
20
21 namespace simgrid {
22 namespace xbt {
23
24 /** A value or an exception (or nothing)
25  *
26  *  This is similar to `optional<expected<T>>`` but it with a Future/Promise
27  *  like API.
28  *
29  *  Also the name is not so great.
30  **/
31 template<class T>
32 class Result {
33 public:
34   bool is_valid() const
35   {
36     return value_.which() > 0;
37   }
38   void set_exception(std::exception_ptr e)
39   {
40     value_ = std::move(e);
41   }
42   void set_value(T&& value)
43   {
44     value_ = std::move(value);
45   }
46   void set_value(T const& value)
47   {
48     value_ = value;
49   }
50
51   /** Extract the value from the future
52    *
53    *  After this, the value is invalid.
54    **/
55   T get()
56   {
57     switch (value_.which()) {
58       case 1: {
59         T value = std::move(boost::get<T>(value_));
60         value_  = boost::blank();
61         return value;
62       }
63       case 2: {
64         std::exception_ptr exception = std::move(boost::get<std::exception_ptr>(value_));
65         value_                       = boost::blank();
66         std::rethrow_exception(std::move(exception));
67         break;
68       }
69       default:
70         throw std::future_error(std::future_errc::no_state);
71     }
72   }
73 private:
74   boost::variant<boost::blank, T, std::exception_ptr> value_;
75 };
76
77 template<>
78 class Result<void> : public Result<std::nullptr_t>
79 {
80 public:
81   void set_value()
82   {
83     Result<std::nullptr_t>::set_value(nullptr);
84   }
85   void get()
86   {
87     Result<std::nullptr_t>::get();
88   }
89 };
90
91 template<class T>
92 class Result<T&> : public Result<std::reference_wrapper<T>>
93 {
94 public:
95   void set_value(T& value)
96   {
97     Result<std::reference_wrapper<T>>::set_value(std::ref(value));
98   }
99   T& get()
100   {
101     return Result<std::reference_wrapper<T>>::get();
102   }
103 };
104
105 /** Execute some code and set a promise or result accordingly
106  *
107  *  Roughly this does:
108  *
109  *  <pre>
110  *  promise.set_value(code());
111  *  </pre>
112  *
113  *  but it takes care of exceptions and works with `void`.
114  *
115  *  We might need this when working with generic code because
116  *  the trivial implementation does not work with `void` (before C++1z).
117  *
118  *  @param    code  What we want to do
119  *  @param  promise Where to want to store the result
120  */
121 template <class R, class F> auto fulfill_promise(R& promise, F&& code) -> decltype(promise.set_value(code()))
122 {
123   try {
124     promise.set_value(std::forward<F>(code)());
125   } catch (...) {
126     promise.set_exception(std::current_exception());
127   }
128 }
129
130 template <class R, class F> auto fulfill_promise(R& promise, F&& code) -> decltype(promise.set_value())
131 {
132   try {
133     std::forward<F>(code)();
134     promise.set_value();
135   } catch (...) {
136     promise.set_exception(std::current_exception());
137   }
138 }
139
140 /** Set a promise/result from a future/result
141  *
142  *  Roughly this does:
143  *
144  *  <pre>promise.set_value(future);</pre>
145  *
146  *  but it takes care of exceptions and works with `void`.
147  *
148  *  We might need this when working with generic code because
149  *  the trivial implementation does not work with `void` (before C++1z).
150  *
151  *  @param promise output (a valid future or a result)
152  *  @param future  input (a ready/waitable future or a valid result)
153  */
154 template <class P, class F> inline void set_promise(P& promise, F&& future)
155 {
156   fulfill_promise(promise, [&future] { return std::forward<F>(future).get(); });
157 }
158
159 }
160 }
161
162 #endif