1 // Copyright (C) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ICING_TEXT_CLASSIFIER_LIB3_UTILS_BASE_STATUSOR_H_
16 #define ICING_TEXT_CLASSIFIER_LIB3_UTILS_BASE_STATUSOR_H_
17
18 #include <type_traits>
19 #include <utility>
20
21 #include "icing/text_classifier/lib3/utils/base/logging.h"
22 #include "icing/text_classifier/lib3/utils/base/macros.h"
23 #include "icing/text_classifier/lib3/utils/base/status.h"
24
25 namespace libtextclassifier3 {
26
27 // A StatusOr holds a Status (in the case of an error), or a value T.
28 template <typename T>
29 class StatusOr {
30 public:
31 // Has status UNKNOWN.
32 inline StatusOr();
33
34 // Builds from a non-OK status. Crashes if an OK status is specified.
35 inline StatusOr(const Status& status); // NOLINT
36
37 // Builds from the specified value.
38 inline StatusOr(const T& value); // NOLINT
39 inline StatusOr(T&& value); // NOLINT
40
41 // Copy constructor.
42 inline StatusOr(const StatusOr& other);
43 // Move constructor.
44 inline StatusOr(StatusOr&& other);
45
46 // Conversion copy constructor, T must be copy constructible from U.
47 template <typename U,
48 std::enable_if_t<
49 std::conjunction<std::negation<std::is_same<T, U>>,
50 std::is_constructible<T, const U&>,
51 std::is_convertible<const U&, T>>::value,
52 int> = 0>
53 inline StatusOr(const StatusOr<U>& other); // NOLINT
54
55 // Conversion move constructor, T must by move constructible from U.
56 template <
57 typename U,
58 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
59 std::is_constructible<T, U&&>,
60 std::is_convertible<U&&, T>>::value,
61 int> = 0>
62 inline StatusOr(StatusOr<U>&& other); // NOLINT
63
64 // Value conversion copy constructor, T must by copy constructible from U.
65 template <typename U,
66 std::enable_if_t<
67 std::conjunction<std::negation<std::is_same<T, U>>,
68 std::is_constructible<T, const U&>,
69 std::is_convertible<const U&, T>>::value,
70 int> = 0>
71 inline StatusOr(const U& value); // NOLINT
72
73 // Value conversion move constructor, T must by move constructible from U.
74 template <
75 typename U,
76 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
77 std::is_constructible<T, U&&>,
78 std::is_convertible<U&&, T>>::value,
79 int> = 0>
80 inline StatusOr(U&& value); // NOLINT
81
82 // Assignment operator.
83 inline StatusOr& operator=(const StatusOr& other);
84 inline StatusOr& operator=(StatusOr&& other);
85
86 // Conversion assignment operator, T must be assignable from U
87 template <typename U>
88 inline StatusOr& operator=(const StatusOr<U>& other);
89 template <typename U>
90 inline StatusOr& operator=(StatusOr<U>&& other);
91
92 inline ~StatusOr();
93
94 // Accessors.
status()95 inline const Status& status() const& { return status_; }
status()96 inline Status status() && { return std::move(status_); }
97
98 // Shorthand for status().ok().
ok()99 inline bool ok() const { return status_.ok(); }
100
101 // Returns value or crashes if ok() is false.
ValueOrDie()102 inline const T& ValueOrDie() const& {
103 if (!ok()) {
104 TC3_LOG(FATAL) << "Attempting to fetch value of non-OK StatusOr: "
105 << status();
106 exit(1);
107 }
108 return value_;
109 }
ValueOrDie()110 inline T& ValueOrDie() & {
111 if (!ok()) {
112 TC3_LOG(FATAL) << "Attempting to fetch value of non-OK StatusOr: "
113 << status();
114 exit(1);
115 }
116 return value_;
117 }
ValueOrDie()118 inline const T&& ValueOrDie() const&& {
119 if (!ok()) {
120 TC3_LOG(FATAL) << "Attempting to fetch value of non-OK StatusOr: "
121 << status();
122 exit(1);
123 }
124 return std::move(value_);
125 }
ValueOrDie()126 inline T&& ValueOrDie() && {
127 if (!ok()) {
128 TC3_LOG(FATAL) << "Attempting to fetch value of non-OK StatusOr: "
129 << status();
130 exit(1);
131 }
132 return std::move(value_);
133 }
134
135 template <typename U>
136 friend class StatusOr;
137
138 private:
Clear()139 void Clear() {
140 if (ok()) {
141 value_.~T();
142 }
143 }
144
145 // Construct the value through placement new with the passed argument.
146 template <typename... Arg>
MakeValue(Arg &&...arg)147 void MakeValue(Arg&&... arg) {
148 new (&value_) T(std::forward<Arg>(arg)...);
149 }
150
151 // Creates a valid instance of type T constructed with U and assigns it to
152 // value_. Handles how to properly assign to value_ if value_ was never
153 // actually initialized (if this is currently non-OK).
154 template <typename U>
AssignValue(U && value)155 void AssignValue(U&& value) {
156 if (ok()) {
157 value_ = std::forward<U>(value);
158 } else {
159 MakeValue(std::forward<U>(value));
160 status_ = Status::OK;
161 }
162 }
163
164 // Creates a status constructed with U and assigns it to status_. It also
165 // properly destroys value_ if this is OK and value_ represents a valid
166 // instance of T.
167 template <typename U>
AssignStatus(U && v)168 void AssignStatus(U&& v) {
169 Clear();
170 status_ = static_cast<Status>(std::forward<U>(v));
171 }
172
173 Status status_;
174 // The members of unions do not require initialization and are not destructed
175 // unless specifically called. This allows us to construct instances of
176 // StatusOr with only error statuses where T is not default constructible.
177 union {
178 // value_ is active iff status_.ok()==true
179 // WARNING: The destructor of value_ is called ONLY if status_ is OK.
180 T value_;
181 };
182 };
183
184 // Implementation.
185
186 template <typename T>
StatusOr()187 inline StatusOr<T>::StatusOr() : status_(StatusCode::UNKNOWN, "") {}
188
189 template <typename T>
StatusOr(const Status & status)190 inline StatusOr<T>::StatusOr(const Status& status) : status_(status) {
191 if (status.ok()) {
192 TC3_LOG(FATAL) << "OkStatus() is not a valid argument to StatusOr";
193 exit(1);
194 }
195 }
196
197 template <typename T>
StatusOr(const T & value)198 inline StatusOr<T>::StatusOr(const T& value) : value_(value) {}
199
200 template <typename T>
StatusOr(T && value)201 inline StatusOr<T>::StatusOr(T&& value) : value_(std::move(value)) {}
202
203 template <typename T>
StatusOr(const StatusOr & other)204 inline StatusOr<T>::StatusOr(const StatusOr& other) : status_(other.status_) {
205 if (other.ok()) {
206 MakeValue(other.value_);
207 }
208 }
209
210 template <typename T>
StatusOr(StatusOr && other)211 inline StatusOr<T>::StatusOr(StatusOr&& other)
212 : status_(std::move(other.status_)) {
213 if (other.ok()) {
214 MakeValue(std::move(other.value_));
215 }
216 }
217
218 template <typename T>
219 template <
220 typename U,
221 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
222 std::is_constructible<T, const U&>,
223 std::is_convertible<const U&, T>>::value,
224 int>>
StatusOr(const StatusOr<U> & other)225 inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
226 : status_(other.status_) {
227 if (other.ok()) {
228 MakeValue(other.value_);
229 }
230 }
231
232 template <typename T>
233 template <typename U,
234 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
235 std::is_constructible<T, U&&>,
236 std::is_convertible<U&&, T>>::value,
237 int>>
StatusOr(StatusOr<U> && other)238 inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
239 : status_(std::move(other.status_)) {
240 if (other.ok()) {
241 MakeValue(std::move(other.value_));
242 }
243 }
244
245 template <typename T>
246 template <
247 typename U,
248 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
249 std::is_constructible<T, const U&>,
250 std::is_convertible<const U&, T>>::value,
251 int>>
StatusOr(const U & value)252 inline StatusOr<T>::StatusOr(const U& value) : StatusOr(T(value)) {}
253
254 template <typename T>
255 template <typename U,
256 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
257 std::is_constructible<T, U&&>,
258 std::is_convertible<U&&, T>>::value,
259 int>>
StatusOr(U && value)260 inline StatusOr<T>::StatusOr(U&& value) : StatusOr(T(std::forward<U>(value))) {}
261
262 template <typename T>
263 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr& other) {
264 if (other.ok()) {
265 AssignValue(other.value_);
266 } else {
267 AssignStatus(other.status_);
268 }
269 return *this;
270 }
271
272 template <typename T>
273 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr&& other) {
274 if (other.ok()) {
275 AssignValue(std::move(other.value_));
276 } else {
277 AssignStatus(std::move(other.status_));
278 }
279 return *this;
280 }
281
282 template <typename T>
~StatusOr()283 inline StatusOr<T>::~StatusOr() {
284 Clear();
285 }
286
287 template <typename T>
288 template <typename U>
289 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
290 if (other.ok()) {
291 AssignValue(other.value_);
292 } else {
293 AssignStatus(other.status_);
294 }
295 return *this;
296 }
297
298 template <typename T>
299 template <typename U>
300 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) {
301 if (other.ok()) {
302 AssignValue(std::move(other.value_));
303 } else {
304 AssignStatus(std::move(other.status_));
305 }
306 return *this;
307 }
308
309 } // namespace libtextclassifier3
310
311 #define TC3_ASSIGN_OR_RETURN(...) \
312 TC_STATUS_MACROS_IMPL_GET_VARIADIC_( \
313 (__VA_ARGS__, TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_, \
314 TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_2_)) \
315 (__VA_ARGS__)
316
317 #define TC3_ASSIGN_OR_RETURN_NULL(lhs, rexpr) \
318 TC3_ASSIGN_OR_RETURN(lhs, rexpr, nullptr)
319
320 #define TC3_ASSIGN_OR_RETURN_FALSE(lhs, rexpr) \
321 TC3_ASSIGN_OR_RETURN(lhs, rexpr, false)
322
323 #define TC3_ASSIGN_OR_RETURN_0(...) \
324 TC_STATUS_MACROS_IMPL_GET_VARIADIC_( \
325 (__VA_ARGS__, TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_0_3_, \
326 TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_0_2_)) \
327 (__VA_ARGS__)
328
329 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_0_2_(lhs, rexpr) \
330 TC3_ASSIGN_OR_RETURN(lhs, rexpr, 0)
331 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_0_3_(lhs, rexpr, \
332 log_expression) \
333 TC3_ASSIGN_OR_RETURN(lhs, rexpr, (log_expression, 0))
334
335 // =================================================================
336 // == Implementation details, do not rely on anything below here. ==
337 // =================================================================
338
339 // Some builds do not support C++14 fully yet, using C++11 constexpr technique.
HasPossiblyConditionalOperator(const char * lhs,int index)340 constexpr bool HasPossiblyConditionalOperator(const char* lhs, int index) {
341 return (index == -1 ? false
342 : (lhs[index] == '?'
343 ? true
344 : HasPossiblyConditionalOperator(lhs, index - 1)));
345 }
346
347 // MSVC incorrectly expands variadic macros, splice together a macro call to
348 // work around the bug.
349 #define TC_STATUS_MACROS_IMPL_GET_VARIADIC_HELPER_(_1, _2, _3, NAME, ...) NAME
350 #define TC_STATUS_MACROS_IMPL_GET_VARIADIC_(args) \
351 TC_STATUS_MACROS_IMPL_GET_VARIADIC_HELPER_ args
352
353 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_2_(lhs, rexpr) \
354 TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_(lhs, rexpr, _)
355 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_(lhs, rexpr, \
356 error_expression) \
357 TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_( \
358 TC_STATUS_MACROS_IMPL_CONCAT_(_status_or_value, __COUNTER__), lhs, \
359 rexpr, error_expression)
360 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_(statusor, lhs, rexpr, \
361 error_expression) \
362 auto statusor = (rexpr); \
363 if (!statusor.ok()) { \
364 ::libtextclassifier3::Status _(std::move(statusor).status()); \
365 (void)_; /* error_expression is allowed to not use this variable */ \
366 return (error_expression); \
367 } \
368 { \
369 static_assert(#lhs[0] != '(' || #lhs[sizeof(#lhs) - 2] != ')' || \
370 !HasPossiblyConditionalOperator(#lhs, sizeof(#lhs) - 2), \
371 "Identified potential conditional operator, consider not " \
372 "using ASSIGN_OR_RETURN"); \
373 } \
374 TC_STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED(lhs) = \
375 std::move(statusor).ValueOrDie()
376
377 // Internal helpers for macro expansion.
378 #define TC_STATUS_MACROS_IMPL_EAT(...)
379 #define TC_STATUS_MACROS_IMPL_REM(...) __VA_ARGS__
380 #define TC_STATUS_MACROS_IMPL_EMPTY()
381
382 // Internal helpers for emptyness arguments check.
383 #define TC_STATUS_MACROS_IMPL_IS_EMPTY_INNER(...) \
384 TC_STATUS_MACROS_IMPL_IS_EMPTY_INNER_I(__VA_ARGS__, 0, 1)
385 #define TC_STATUS_MACROS_IMPL_IS_EMPTY_INNER_I(e0, e1, is_empty, ...) is_empty
386
387 #define TC_STATUS_MACROS_IMPL_IS_EMPTY(...) \
388 TC_STATUS_MACROS_IMPL_IS_EMPTY_I(__VA_ARGS__)
389 #define TC_STATUS_MACROS_IMPL_IS_EMPTY_I(...) \
390 TC_STATUS_MACROS_IMPL_IS_EMPTY_INNER(_, ##__VA_ARGS__)
391
392 // Internal helpers for if statement.
393 #define TC_STATUS_MACROS_IMPL_IF_1(_Then, _Else) _Then
394 #define TC_STATUS_MACROS_IMPL_IF_0(_Then, _Else) _Else
395 #define TC_STATUS_MACROS_IMPL_IF(_Cond, _Then, _Else) \
396 TC_STATUS_MACROS_IMPL_CONCAT_(TC_STATUS_MACROS_IMPL_IF_, _Cond)(_Then, _Else)
397
398 // Expands to 1 if the input is parenthesized. Otherwise expands to 0.
399 #define TC_STATUS_MACROS_IMPL_IS_PARENTHESIZED(...) \
400 TC_STATUS_MACROS_IMPL_IS_EMPTY(TC_STATUS_MACROS_IMPL_EAT __VA_ARGS__)
401
402 // If the input is parenthesized, removes the parentheses. Otherwise expands to
403 // the input unchanged.
404 #define TC_STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED(...) \
405 TC_STATUS_MACROS_IMPL_IF( \
406 TC_STATUS_MACROS_IMPL_IS_PARENTHESIZED(__VA_ARGS__), \
407 TC_STATUS_MACROS_IMPL_REM, TC_STATUS_MACROS_IMPL_EMPTY()) \
408 __VA_ARGS__
409
410 // Internal helper for concatenating macro values.
411 #define TC_STATUS_MACROS_IMPL_CONCAT_INNER_(x, y) x##y
412 #define TC_STATUS_MACROS_IMPL_CONCAT_(x, y) \
413 TC_STATUS_MACROS_IMPL_CONCAT_INNER_(x, y)
414
415 #endif // ICING_TEXT_CLASSIFIER_LIB3_UTILS_BASE_STATUSOR_H_
416