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