Logo AND Algorithmique Numérique Distribuée

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