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)
205 : status_(other.status_), value_(other.value_) {}
206
207 template <typename T>
StatusOr(StatusOr && other)208 inline StatusOr<T>::StatusOr(StatusOr&& other)
209 : status_(other.status_), value_(std::move(other.value_)) {}
210
211 template <typename T>
212 template <
213 typename U,
214 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
215 std::is_constructible<T, const U&>,
216 std::is_convertible<const U&, T>>::value,
217 int>>
StatusOr(const StatusOr<U> & other)218 inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
219 : status_(other.status_), value_(other.value_) {}
220
221 template <typename T>
222 template <typename U,
223 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
224 std::is_constructible<T, U&&>,
225 std::is_convertible<U&&, T>>::value,
226 int>>
StatusOr(StatusOr<U> && other)227 inline StatusOr<T>::StatusOr(StatusOr<U>&& other)
228 : status_(other.status_), value_(std::move(other.value_)) {}
229
230 template <typename T>
231 template <
232 typename U,
233 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
234 std::is_constructible<T, const U&>,
235 std::is_convertible<const U&, T>>::value,
236 int>>
StatusOr(const U & value)237 inline StatusOr<T>::StatusOr(const U& value) : StatusOr(T(value)) {}
238
239 template <typename T>
240 template <typename U,
241 std::enable_if_t<std::conjunction<std::negation<std::is_same<T, U>>,
242 std::is_constructible<T, U&&>,
243 std::is_convertible<U&&, T>>::value,
244 int>>
StatusOr(U && value)245 inline StatusOr<T>::StatusOr(U&& value) : StatusOr(T(std::forward<U>(value))) {}
246
247 template <typename T>
248 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr& other) {
249 if (other.ok()) {
250 AssignValue(other.value_);
251 } else {
252 AssignStatus(other.status_);
253 }
254 return *this;
255 }
256
257 template <typename T>
258 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr&& other) {
259 if (other.ok()) {
260 AssignValue(std::move(other.value_));
261 } else {
262 AssignStatus(std::move(other.status_));
263 }
264 return *this;
265 }
266
267 template <typename T>
~StatusOr()268 inline StatusOr<T>::~StatusOr() {
269 Clear();
270 }
271
272 template <typename T>
273 template <typename U>
274 inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
275 if (other.ok()) {
276 AssignValue(other.value_);
277 } else {
278 AssignStatus(other.status_);
279 }
280 return *this;
281 }
282
283 template <typename T>
284 template <typename U>
285 inline StatusOr<T>& StatusOr<T>::operator=(StatusOr<U>&& other) {
286 if (other.ok()) {
287 AssignValue(std::move(other.value_));
288 } else {
289 AssignStatus(std::move(other.status_));
290 }
291 return *this;
292 }
293
294 } // namespace libtextclassifier3
295
296 #define TC3_ASSIGN_OR_RETURN(...) \
297 TC_STATUS_MACROS_IMPL_GET_VARIADIC_( \
298 (__VA_ARGS__, TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_, \
299 TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_2_)) \
300 (__VA_ARGS__)
301
302 #define TC3_ASSIGN_OR_RETURN_NULL(lhs, rexpr) \
303 TC3_ASSIGN_OR_RETURN(lhs, rexpr, nullptr)
304
305 #define TC3_ASSIGN_OR_RETURN_FALSE(lhs, rexpr) \
306 TC3_ASSIGN_OR_RETURN(lhs, rexpr, false)
307
308 #define TC3_ASSIGN_OR_RETURN_0(...) \
309 TC_STATUS_MACROS_IMPL_GET_VARIADIC_( \
310 (__VA_ARGS__, TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_0_3_, \
311 TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_0_2_)) \
312 (__VA_ARGS__)
313
314 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_0_2_(lhs, rexpr) \
315 TC3_ASSIGN_OR_RETURN(lhs, rexpr, 0)
316 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_0_3_(lhs, rexpr, \
317 log_expression) \
318 TC3_ASSIGN_OR_RETURN(lhs, rexpr, (log_expression, 0))
319
320 // =================================================================
321 // == Implementation details, do not rely on anything below here. ==
322 // =================================================================
323
324 // Some builds do not support C++14 fully yet, using C++11 constexpr technique.
HasPossiblyConditionalOperator(const char * lhs,int index)325 constexpr bool HasPossiblyConditionalOperator(const char* lhs, int index) {
326 return (index == -1 ? false
327 : (lhs[index] == '?'
328 ? true
329 : HasPossiblyConditionalOperator(lhs, index - 1)));
330 }
331
332 // MSVC incorrectly expands variadic macros, splice together a macro call to
333 // work around the bug.
334 #define TC_STATUS_MACROS_IMPL_GET_VARIADIC_HELPER_(_1, _2, _3, NAME, ...) NAME
335 #define TC_STATUS_MACROS_IMPL_GET_VARIADIC_(args) \
336 TC_STATUS_MACROS_IMPL_GET_VARIADIC_HELPER_ args
337
338 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_2_(lhs, rexpr) \
339 TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_(lhs, rexpr, _)
340 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_(lhs, rexpr, \
341 error_expression) \
342 TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_( \
343 TC_STATUS_MACROS_IMPL_CONCAT_(_status_or_value, __COUNTER__), lhs, \
344 rexpr, error_expression)
345 #define TC_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_(statusor, lhs, rexpr, \
346 error_expression) \
347 auto statusor = (rexpr); \
348 if (!statusor.ok()) { \
349 ::libtextclassifier3::Status _(std::move(statusor).status()); \
350 (void)_; /* error_expression is allowed to not use this variable */ \
351 return (error_expression); \
352 } \
353 { \
354 static_assert(#lhs[0] != '(' || #lhs[sizeof(#lhs) - 2] != ')' || \
355 !HasPossiblyConditionalOperator(#lhs, sizeof(#lhs) - 2), \
356 "Identified potential conditional operator, consider not " \
357 "using ASSIGN_OR_RETURN"); \
358 } \
359 TC_STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED(lhs) = \
360 std::move(statusor).ValueOrDie()
361
362 // Internal helpers for macro expansion.
363 #define TC_STATUS_MACROS_IMPL_EAT(...)
364 #define TC_STATUS_MACROS_IMPL_REM(...) __VA_ARGS__
365 #define TC_STATUS_MACROS_IMPL_EMPTY()
366
367 // Internal helpers for emptyness arguments check.
368 #define TC_STATUS_MACROS_IMPL_IS_EMPTY_INNER(...) \
369 TC_STATUS_MACROS_IMPL_IS_EMPTY_INNER_I(__VA_ARGS__, 0, 1)
370 #define TC_STATUS_MACROS_IMPL_IS_EMPTY_INNER_I(e0, e1, is_empty, ...) is_empty
371
372 #define TC_STATUS_MACROS_IMPL_IS_EMPTY(...) \
373 TC_STATUS_MACROS_IMPL_IS_EMPTY_I(__VA_ARGS__)
374 #define TC_STATUS_MACROS_IMPL_IS_EMPTY_I(...) \
375 TC_STATUS_MACROS_IMPL_IS_EMPTY_INNER(_, ##__VA_ARGS__)
376
377 // Internal helpers for if statement.
378 #define TC_STATUS_MACROS_IMPL_IF_1(_Then, _Else) _Then
379 #define TC_STATUS_MACROS_IMPL_IF_0(_Then, _Else) _Else
380 #define TC_STATUS_MACROS_IMPL_IF(_Cond, _Then, _Else) \
381 TC_STATUS_MACROS_IMPL_CONCAT_(TC_STATUS_MACROS_IMPL_IF_, _Cond)(_Then, _Else)
382
383 // Expands to 1 if the input is parenthesized. Otherwise expands to 0.
384 #define TC_STATUS_MACROS_IMPL_IS_PARENTHESIZED(...) \
385 TC_STATUS_MACROS_IMPL_IS_EMPTY(TC_STATUS_MACROS_IMPL_EAT __VA_ARGS__)
386
387 // If the input is parenthesized, removes the parentheses. Otherwise expands to
388 // the input unchanged.
389 #define TC_STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED(...) \
390 TC_STATUS_MACROS_IMPL_IF( \
391 TC_STATUS_MACROS_IMPL_IS_PARENTHESIZED(__VA_ARGS__), \
392 TC_STATUS_MACROS_IMPL_REM, TC_STATUS_MACROS_IMPL_EMPTY()) \
393 __VA_ARGS__
394
395 // Internal helper for concatenating macro values.
396 #define TC_STATUS_MACROS_IMPL_CONCAT_INNER_(x, y) x##y
397 #define TC_STATUS_MACROS_IMPL_CONCAT_(x, y) \
398 TC_STATUS_MACROS_IMPL_CONCAT_INNER_(x, y)
399
400 #endif // ICING_TEXT_CLASSIFIER_LIB3_UTILS_BASE_STATUSOR_H_
401