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