xref: /aosp_15_r20/external/icing/icing/text_classifier/lib3/utils/base/statusor.h (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
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