Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'Adrien.Gougeon/simgrid-master'
[simgrid.git] / include / xbt / future.hpp
1 /* Copyright (c) 2015-2020. 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
74 private:
75   boost::variant<boost::blank, T, std::exception_ptr> value_;
76 };
77
78 template<>
79 class Result<void> : public Result<std::nullptr_t>
80 {
81 public:
82   void set_value()
83   {
84     Result<std::nullptr_t>::set_value(nullptr);
85   }
86   void get()
87   {
88     Result<std::nullptr_t>::get();
89   }
90 };
91
92 template<class T>
93 class Result<T&> : public Result<std::reference_wrapper<T>>
94 {
95 public:
96   void set_value(T& value)
97   {
98     Result<std::reference_wrapper<T>>::set_value(std::ref(value));
99   }
100   T& get()
101   {
102     return Result<std::reference_wrapper<T>>::get();
103   }
104 };
105
106 /** Execute some code and set a promise or result accordingly
107  *
108  *  Roughly this does:
109  *
110  *  <pre>
111  *  promise.set_value(code());
112  *  </pre>
113  *
114  *  but it takes care of exceptions and works with `void`.
115  *
116  *  We might need this when working with generic code because
117  *  the trivial implementation does not work with `void` (before C++1z).
118  *
119  *  @param    code  What we want to do
120  *  @param  promise Where to want to store the result
121  */
122 template <class R, class F> auto fulfill_promise(R& promise, F&& code) -> decltype(promise.set_value(code()))
123 {
124   try {
125     promise.set_value(std::forward<F>(code)());
126   } catch (...) {
127     promise.set_exception(std::current_exception());
128   }
129 }
130
131 template <class R, class F> auto fulfill_promise(R& promise, F&& code) -> decltype(promise.set_value())
132 {
133   try {
134     std::forward<F>(code)();
135     promise.set_value();
136   } catch (...) {
137     promise.set_exception(std::current_exception());
138   }
139 }
140
141 /** Set a promise/result from a future/result
142  *
143  *  Roughly this does:
144  *
145  *  <pre>promise.set_value(future);</pre>
146  *
147  *  but it takes care of exceptions and works with `void`.
148  *
149  *  We might need this when working with generic code because
150  *  the trivial implementation does not work with `void` (before C++1z).
151  *
152  *  @param promise output (a valid future or a result)
153  *  @param future  input (a ready/waitable future or a valid result)
154  */
155 template <class P, class F> inline void set_promise(P& promise, F&& future)
156 {
157   fulfill_promise(promise, [&future] { return std::forward<F>(future).get(); });
158 }
159
160 }
161 }
162
163 #endif