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