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