• 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)
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