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