xref: /aosp_15_r20/external/libtextclassifier/native/utils/base/statusor.h (revision 993b0882672172b81d12fad7a7ac0c3e5c824a12)
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