xref: /aosp_15_r20/external/executorch/runtime/core/result.h (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
9 /**
10  * @file
11  * Result type to be used in conjunction with ExecuTorch Error type.
12  */
13 
14 #pragma once
15 
16 #include <new>
17 #include <utility>
18 
19 #include "executorch/runtime/core/error.h"
20 #include "executorch/runtime/platform/assert.h"
21 
22 namespace executorch {
23 namespace runtime {
24 
25 /**
26  * Result type wrapping either a value of type T or an error.
27  *
28  * Example use case:
29  * @code
30  *   Result<OpFn> getOp(int opcode) {
31  *     if (isValidOpCode(opcode)) {
32  *       return opFns[opcode];
33  *     }
34  *     return Error::NotFound;
35  *   }
36  *
37  *   Error useOp(int opcode) {
38  *     Result<OpFn> op = getOp(opcode);
39  *     if (!op.ok()) {
40  *       return op.error();
41  *     }
42  *     print(op->toString());
43  *     execute(*op);
44  *     return Error::Ok;
45  *   }
46  * @endcode
47  */
48 template <typename T>
49 class Result final {
50  public:
51   /// `value_type` member for generic programming.
52   typedef T value_type;
53 
54   /**
55    * Creates a Result object from an Error.
56    *
57    * To preserve the invariant that `(result.error() == Error::Ok) ==
58    * result.ok()`, an `error` parameter value of `Error:Ok` will be converted to
59    * a non-Ok value.
60    */
Result(Error error)61   /* implicit */ Result(Error error)
62       : error_(error == Error::Ok ? Error::Internal : error),
63         hasValue_(false) {}
64 
65   /// Value copy constructor.
Result(const T & val)66   /* implicit */ Result(const T& val) : value_(val), hasValue_(true) {}
67 
68   /// Value move constructor.
Result(T && val)69   /* implicit */ Result(T&& val) : value_(std::move(val)), hasValue_(true) {}
70 
71   /// Result move constructor.
Result(Result && rhs)72   /* implicit */ Result(Result&& rhs) noexcept : hasValue_(rhs.hasValue_) {
73     if (hasValue_) {
74       // Use the value type's move constructor.
75       new (&value_) T(std::move(rhs.value_));
76     } else {
77       error_ = rhs.error_;
78     }
79   }
80 
~Result()81   ~Result() {
82     if (hasValue_) {
83       // Manual value destruction.
84       // Result "owns" the memory, so `delete` would segfault.
85       value_.~T();
86     }
87   }
88 
89   /**
90    * Returns true if this Result has a value.
91    *
92    * If true, it is guaranteed that `error()` will return `Error::Ok`.
93    * If false, it is guaranteed that `error()` will not return `Error::Ok`.
94    */
ok()95   ET_NODISCARD bool ok() const {
96     return hasValue_;
97   }
98 
99   /**
100    * Returns the error code of this Result.
101    *
102    * If this returns `Error::Ok`, it is guaranteed that `ok()` will return true.
103    * If this does not return `Error:Ok`, it is guaranteed that `ok()` will
104    * return false.
105    */
error()106   ET_NODISCARD Error error() const {
107     if (hasValue_) {
108       return Error::Ok;
109     } else {
110       return error_;
111     }
112   }
113 
114   /**
115    * Returns a reference to the Result's value; longhand for operator*().
116    *
117    * Only legal to call if `ok()` returns true.
118    */
get()119   T& get() {
120     CheckOk();
121     return value_;
122   }
123 
124   /**
125    * Returns a reference to the Result's value; longhand for operator*().
126    *
127    * Only legal to call if `ok()` returns true.
128    */
get()129   const T& get() const {
130     CheckOk();
131     return value_;
132   }
133 
134   /*
135    * Returns a reference to the Result's value; shorthand for get().
136    *
137    * Only legal to call if `ok()` returns true.
138    */
139   const T& operator*() const&;
140   T& operator*() &;
141 
142   /*
143    * Returns a pointer to the Result's value.
144    *
145    * Only legal to call if `ok()` returns true.
146    */
147   const T* operator->() const;
148   T* operator->();
149 
150  private:
151   /**
152    * Delete default constructor since all Results should contain a value or
153    * error.
154    */
155   Result() = delete;
156   /// Delete copy constructor since T may not be copyable.
157   Result(const Result&) = delete;
158   /// Delete copy assignment since T may not be copyable.
159   Result& operator=(const Result&) = delete;
160   /// Delete move assignment since it's not a supported pattern to reuse Result.
161   Result& operator=(Result&& rhs) = delete;
162 
163   // Panics if ok() would return false;
CheckOk()164   void CheckOk() const {
165     ET_CHECK(hasValue_);
166   }
167 
168   union {
169     T value_; // Used if hasValue_ is true.
170     Error error_; // Used if hasValue_ is false.
171   };
172 
173   /// True if the Result contains a value.
174   const bool hasValue_;
175 };
176 
177 template <typename T>
178 const T& Result<T>::operator*() const& {
179   CheckOk();
180   return value_;
181 }
182 
183 template <typename T>
184 T& Result<T>::operator*() & {
185   CheckOk();
186   return value_;
187 }
188 
189 template <typename T>
190 const T* Result<T>::operator->() const {
191   CheckOk();
192   return &value_;
193 }
194 
195 template <typename T>
196 T* Result<T>::operator->() {
197   CheckOk();
198   return &value_;
199 }
200 
201 } // namespace runtime
202 } // namespace executorch
203 
204 namespace torch {
205 namespace executor {
206 // TODO(T197294990): Remove these deprecated aliases once all users have moved
207 // to the new `::executorch` namespaces.
208 using ::executorch::runtime::Result;
209 } // namespace executor
210 } // namespace torch
211 
212 /**
213  * Unwrap a Result to obtain its value. If the Result contains an error,
214  * propogate the error via trivial function return.
215  *
216  * Note: A function using ET_UNWRAP should itself return a Result or Error.
217  *
218  * @param[in] result__ Expression yielding the result to unwrap.
219  * @param[in] ... Optional format string for the log error message and its
220  * arguments.
221  */
222 #define ET_UNWRAP(result__, ...) ET_INTERNAL_UNWRAP(result__, ##__VA_ARGS__)
223 
224 // Internal only: Use ET_UNWRAP() instead.
225 #define ET_INTERNAL_UNWRAP(...)                                         \
226   ET_INTERNAL_UNWRAP_SELECT(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) \
227   (__VA_ARGS__)
228 
229 // Internal only: Use ET_UNWRAP() instead.
230 #define ET_INTERNAL_UNWRAP_SELECT(                   \
231     _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) \
232   ET_INTERNAL_UNWRAP_##N
233 
234 // Internal only: Use ET_UNWRAP() instead.
235 #define ET_INTERNAL_UNWRAP_1(result__) \
236   ({                                   \
237     auto et_result__ = (result__);     \
238     if (!et_result__.ok()) {           \
239       return et_result__.error();      \
240     }                                  \
241     std::move(*et_result__);           \
242   })
243 
244 // Internal only: Use ET_UNWRAP() instead.
245 #define ET_INTERNAL_UNWRAP_2(result__, message__, ...) \
246   ({                                                   \
247     auto et_result__ = (result__);                     \
248     if (!et_result__.ok()) {                           \
249       ET_LOG(Error, message__, ##__VA_ARGS__);         \
250       return et_result__.error();                      \
251     }                                                  \
252     std::move(*et_result__);                           \
253   })
254 
255 // Internal only: Use ET_UNWRAP() instead.
256 #define ET_INTERNAL_UNWRAP_3 ET_INTERNAL_UNWRAP_2
257 #define ET_INTERNAL_UNWRAP_4 ET_INTERNAL_UNWRAP_2
258 #define ET_INTERNAL_UNWRAP_5 ET_INTERNAL_UNWRAP_2
259 #define ET_INTERNAL_UNWRAP_6 ET_INTERNAL_UNWRAP_2
260 #define ET_INTERNAL_UNWRAP_7 ET_INTERNAL_UNWRAP_2
261 #define ET_INTERNAL_UNWRAP_8 ET_INTERNAL_UNWRAP_2
262 #define ET_INTERNAL_UNWRAP_9 ET_INTERNAL_UNWRAP_2
263 #define ET_INTERNAL_UNWRAP_10 ET_INTERNAL_UNWRAP_2
264