1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef LIB_FIT_RESULT_H_
6 #define LIB_FIT_RESULT_H_
7
8 #include <assert.h>
9
10 #include <new>
11 #include <type_traits>
12 #include <utility>
13
14 #include "traits.h"
15 #include "variant.h"
16
17 namespace fit {
18
19 // Represents the intermediate state of a result that has not yet completed.
20 struct pending_result final {};
21
22 // Returns an value that represents a pending result.
pending()23 constexpr inline pending_result pending() {
24 return pending_result{};
25 }
26
27 // Represents the result of a successful task.
28 template <typename V = void>
29 struct ok_result final {
30 using value_type = V;
31
ok_resultfinal32 explicit constexpr ok_result(V value)
33 : value(std::move(value)) {}
34
35 V value;
36 };
37 template <>
38 struct ok_result<void> {
39 using value_type = void;
40 };
41
42 // Wraps the result of a successful task as an |ok_result<T>|.
43 template <typename V>
44 constexpr inline ok_result<V> ok(V value) {
45 return ok_result<V>(std::move(value));
46 }
47 constexpr inline ok_result<> ok() {
48 return ok_result<>{};
49 }
50
51 // Represents the result of a failed task.
52 template <typename E = void>
53 struct error_result final {
54 using error_type = E;
55
56 explicit constexpr error_result(E error)
57 : error(std::move(error)) {}
58
59 E error;
60 };
61 template <>
62 struct error_result<void> {
63 using error_type = void;
64 };
65
66 // Wraps the result of a failed task as an |error_result<T>|.
67 template <typename E>
68 constexpr inline error_result<E> error(E error) {
69 return error_result<E>(std::move(error));
70 }
71 constexpr inline error_result<> error() {
72 return error_result<>{};
73 }
74
75 // Describes the status of a task's result.
76 enum class result_state {
77 // The task is still in progress.
78 pending,
79 // The task completed successfully.
80 ok,
81 // The task failed.
82 error
83 };
84
85 // Represents the result of a task which may have succeeded, failed,
86 // or still be in progress.
87 //
88 // Use |fit::pending()|, |fit::ok<T>()|, or |fit::error<T>| to initialize
89 // the result.
90 //
91 // |V| is the type of value produced when the completes successfully.
92 // Defaults to |void|.
93 //
94 // |E| is the type of error produced when the completes with an error.
95 // Defaults to |void|.
96 //
97 // EXAMPLE:
98 //
99 // fit::result<int, std::string> divide(int dividend, int divisor) {
100 // if (divisor == 0)
101 // return fit::error<std::string>("divide by zero");
102 // return fit::ok(dividend / divisor);
103 // }
104 //
105 // int try_divide(int dividend, int divisor) {
106 // auto result = divide(dividend, divisor);
107 // if (result.is_ok()) {
108 // printf("%d / %d = %d\n", dividend, divisor, result.value());
109 // return result.value();
110 // }
111 // printf("%d / %d: ERROR %s\n", dividend, divisor, result.error().c_str());
112 // return -999;
113 // }
114 //
115 // EXAMPLE WITH VOID RESULT VALUE AND ERROR:
116 //
117 // fit::result<> open(std::string secret) {
118 // printf("guessing \"%s\"\n", secret.c_str());
119 // if (secret == "sesame") {
120 // return fit::ok();
121 // puts("yes!");
122 // }
123 // puts("no.");
124 // return fit::error();
125 // }
126 //
127 // bool guess_combination() {
128 // return open("friend") || open("sesame") || open("I give up");
129 // }
130 template <typename V = void, typename E = void>
131 class result final {
132 public:
133 using value_type = V;
134 using error_type = E;
135
136 // Creates a pending result.
137 constexpr result() = default;
138 constexpr result(pending_result) {}
139
140 // Creates an ok result.
141 constexpr result(ok_result<V> result)
142 : state_(::fit::internal::in_place_index<1>, std::move(result)) {}
143 template <typename OtherV,
144 typename = std::enable_if_t<std::is_constructible<V, OtherV>::value>>
145 constexpr result(ok_result<OtherV> other)
146 : state_(::fit::internal::in_place_index<1>,
147 fit::ok<V>(std::move(other.value))) {}
148
149 // Creates an error result.
150 constexpr result(error_result<E> result)
151 : state_(::fit::internal::in_place_index<2>, std::move(result)) {}
152 template <typename OtherE,
153 typename = std::enable_if_t<std::is_constructible<E, OtherE>::value>>
154 constexpr result(error_result<OtherE> other)
155 : state_(::fit::internal::in_place_index<2>,
156 fit::error<E>(std::move(other.error))) {}
157
158 // Copies another result (if copyable).
159 result(const result& other) = default;
160
161 // Moves from another result, leaving the other one in a pending state.
162 result(result&& other)
163 : state_(std::move(other.state_)) {
164 other.reset();
165 }
166
167 ~result() = default;
168
169 // Returns the state of the task's result: pending, ok, or error.
170 constexpr result_state state() const {
171 return static_cast<result_state>(state_.index());
172 }
173
174 // Returns true if the result is not pending.
175 constexpr explicit operator bool() const {
176 return !is_pending();
177 }
178
179 // Returns true if the task is still in progress.
180 constexpr bool is_pending() const {
181 return state() == result_state::pending;
182 }
183
184 // Returns true if the task succeeded.
185 constexpr bool is_ok() const {
186 return state() == result_state::ok;
187 }
188
189 // Returns true if the task failed.
190 constexpr bool is_error() const {
191 return state() == result_state::error;
192 }
193
194 // Gets the result's value.
195 // Asserts that the result's state is |fit::result_state::ok|.
196 template <typename R = V,
197 typename = std::enable_if_t<!std::is_void<R>::value>>
198 constexpr R& value() {
199 return state_.template get<1>().value;
200 }
201 template <typename R = V,
202 typename = std::enable_if_t<!std::is_void<R>::value>>
203 constexpr const R& value() const {
204 return state_.template get<1>().value;
205 }
206
207 // Takes the result's value, leaving it in a pending state.
208 // Asserts that the result's state is |fit::result_state::ok|.
209 template <typename R = V,
210 typename = std::enable_if_t<!std::is_void<R>::value>>
211 R take_value() {
212 auto value = std::move(state_.template get<1>().value);
213 reset();
214 return value;
215 }
216 ok_result<V> take_ok_result() {
217 auto result = std::move(state_.template get<1>());
218 reset();
219 return result;
220 }
221
222 // Gets a reference to the result's error.
223 // Asserts that the result's state is |fit::result_state::error|.
224 template <typename R = E,
225 typename = std::enable_if_t<!std::is_void<R>::value>>
226 constexpr R& error() {
227 return state_.template get<2>().error;
228 }
229 template <typename R = E,
230 typename = std::enable_if_t<!std::is_void<R>::value>>
231 constexpr const R& error() const {
232 return state_.template get<2>().error;
233 }
234
235 // Takes the result's error, leaving it in a pending state.
236 // Asserts that the result's state is |fit::result_state::error|.
237 template <typename R = E,
238 typename = std::enable_if_t<!std::is_void<R>::value>>
239 R take_error() {
240 auto error = std::move(state_.template get<2>().error);
241 reset();
242 return error;
243 }
244 error_result<E> take_error_result() {
245 auto result = std::move(state_.template get<2>());
246 reset();
247 return result;
248 }
249
250 // Assigns from another result (if copyable).
251 result& operator=(const result& other) = default;
252
253 // Moves from another result, leaving the other one in a pending state.
254 result& operator=(result&& other) {
255 state_ = std::move(other.state_);
256 other.reset();
257 return *this;
258 }
259
260 // Swaps results.
261 void swap(result& other) {
262 state_.swap(other.state_);
263 }
264
265 private:
266 void reset() { state_.template emplace<0>(); }
267
268 ::fit::internal::variant<
269 ::fit::internal::monostate, ok_result<V>, error_result<E>>
270 state_;
271 };
272
273 template <typename V, typename E>
274 void swap(result<V, E>& a, result<V, E>& b) {
275 a.swap(b);
276 }
277
278 } // namespace fit
279
280 #endif // LIB_FIT_RESULT_H_
281