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