blob: 47476c9ceefd1c2a5eaaa27e7f91e7bb6023ac1e [file] [log] [blame]
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
// This file is forked from absl.
#ifndef TENSORFLOW_LITE_SUPPORT_CC_PORT_DEFAULT_STATUS_MACROS_H_
#define TENSORFLOW_LITE_SUPPORT_CC_PORT_DEFAULT_STATUS_MACROS_H_
#include "absl/base/optimization.h"
#include "absl/status/status.h"
// Evaluates an expression that produces a `absl::Status`. If the status is not
// ok, returns it from the current function.
//
// For example:
// absl::Status MultiStepFunction() {
// RETURN_IF_ERROR(Function(args...));
// RETURN_IF_ERROR(foo.Method(args...));
// return absl::OkStatus();
// }
#define RETURN_IF_ERROR(expr) \
STATUS_MACROS_IMPL_ELSE_BLOCKER_ \
if (::tflite::support::status_macro_internal::StatusAdaptorForMacros \
status_macro_internal_adaptor = {(expr)}) { \
} else /* NOLINT */ \
return status_macro_internal_adaptor.Consume()
// Executes an expression `rexpr` that returns a `tflite::support::StatusOr<T>`.
// On OK, moves its value into the variable defined by `lhs`, otherwise returns
// from the current function. By default the error status is returned
// unchanged, but it may be modified by an `error_expression`. If there is an
// error, `lhs` is not evaluated; thus any side effects that `lhs` may have
// only occur in the success case.
//
// Interface:
//
// ASSIGN_OR_RETURN(lhs, rexpr)
// ASSIGN_OR_RETURN(lhs, rexpr, error_expression);
//
// WARNING: if lhs is parenthesized, the parentheses are removed. See examples
// for more details.
//
// WARNING: expands into multiple statements; it cannot be used in a single
// statement (e.g. as the body of an if statement without {})!
//
// Example: Declaring and initializing a new variable (ValueType can be anything
// that can be initialized with assignment, including references):
// ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(arg));
//
// Example: Assigning to an existing variable:
// ValueType value;
// ASSIGN_OR_RETURN(value, MaybeGetValue(arg));
//
// Example: Assigning to an expression with side effects:
// MyProto data;
// ASSIGN_OR_RETURN(*data.mutable_str(), MaybeGetValue(arg));
// // No field "str" is added on error.
//
// Example: Assigning to a std::unique_ptr.
// ASSIGN_OR_RETURN(std::unique_ptr<T> ptr, MaybeGetPtr(arg));
//
// Example: Assigning to a map. Because of C preprocessor
// limitation, the type used in ASSIGN_OR_RETURN cannot contain comma, so
// wrap lhs in parentheses:
// ASSIGN_OR_RETURN((absl::flat_hash_map<Foo, Bar> my_map), GetMap());
// Or use auto if the type is obvious enough:
// ASSIGN_OR_RETURN(const auto& my_map, GetMapRef());
//
// Example: Assigning to structured bindings. The same situation with comma as
// in map, so wrap the statement in parentheses.
// ASSIGN_OR_RETURN((const auto& [first, second]), GetPair());
#define ASSIGN_OR_RETURN(...) \
STATUS_MACROS_IMPL_GET_VARIADIC_((__VA_ARGS__, \
STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_, \
STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_2_)) \
(__VA_ARGS__)
// =================================================================
// == Implementation details, do not rely on anything below here. ==
// =================================================================
// Some builds do not support C++14 fully yet, using C++11 constexpr technique.
constexpr bool TFLSHasPotentialConditionalOperator(const char* lhs, int index) {
return (index == -1
? false
: (lhs[index] == '?'
? true
: TFLSHasPotentialConditionalOperator(lhs, index - 1)));
}
// MSVC incorrectly expands variadic macros, splice together a macro call to
// work around the bug.
#define STATUS_MACROS_IMPL_GET_VARIADIC_HELPER_(_1, _2, _3, NAME, ...) NAME
#define STATUS_MACROS_IMPL_GET_VARIADIC_(args) \
STATUS_MACROS_IMPL_GET_VARIADIC_HELPER_ args
#define STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_2_(lhs, rexpr) \
STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_(lhs, rexpr, _)
#define STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_(lhs, rexpr, error_expression) \
STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_( \
STATUS_MACROS_IMPL_CONCAT_(_status_or_value, __LINE__), lhs, rexpr, \
error_expression)
#define STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_(statusor, lhs, rexpr, \
error_expression) \
auto statusor = (rexpr); \
if (ABSL_PREDICT_FALSE(!statusor.ok())) { \
::absl::Status _(std::move(statusor).status()); \
(void)_; /* error_expression is allowed to not use this variable */ \
return (error_expression); \
} \
{ \
static_assert( \
#lhs[0] != '(' || #lhs[sizeof(#lhs) - 2] != ')' || \
!TFLSHasPotentialConditionalOperator(#lhs, sizeof(#lhs) - 2), \
"Identified potential conditional operator, consider not " \
"using ASSIGN_OR_RETURN"); \
} \
STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED(lhs) = \
std::move(statusor).value()
// Internal helpers for macro expansion.
#define STATUS_MACROS_IMPL_EAT(...)
#define STATUS_MACROS_IMPL_REM(...) __VA_ARGS__
#define STATUS_MACROS_IMPL_EMPTY()
// Internal helpers for emptyness arguments check.
#define STATUS_MACROS_IMPL_IS_EMPTY_INNER(...) \
STATUS_MACROS_IMPL_IS_EMPTY_INNER_I(__VA_ARGS__, 0, 1)
#define STATUS_MACROS_IMPL_IS_EMPTY_INNER_I(e0, e1, is_empty, ...) is_empty
#define STATUS_MACROS_IMPL_IS_EMPTY(...) \
STATUS_MACROS_IMPL_IS_EMPTY_I(__VA_ARGS__)
#define STATUS_MACROS_IMPL_IS_EMPTY_I(...) \
STATUS_MACROS_IMPL_IS_EMPTY_INNER(_, ##__VA_ARGS__)
// Internal helpers for if statement.
#define STATUS_MACROS_IMPL_IF_1(_Then, _Else) _Then
#define STATUS_MACROS_IMPL_IF_0(_Then, _Else) _Else
#define STATUS_MACROS_IMPL_IF(_Cond, _Then, _Else) \
STATUS_MACROS_IMPL_CONCAT_(STATUS_MACROS_IMPL_IF_, _Cond) \
(_Then, _Else)
// Expands to 1 if the input is parenthesized. Otherwise expands to 0.
#define STATUS_MACROS_IMPL_IS_PARENTHESIZED(...) \
STATUS_MACROS_IMPL_IS_EMPTY(STATUS_MACROS_IMPL_EAT __VA_ARGS__)
// If the input is parenthesized, removes the parentheses. Otherwise expands to
// the input unchanged.
#define STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED(...) \
STATUS_MACROS_IMPL_IF(STATUS_MACROS_IMPL_IS_PARENTHESIZED(__VA_ARGS__), \
STATUS_MACROS_IMPL_REM, STATUS_MACROS_IMPL_EMPTY()) \
__VA_ARGS__
// Internal helper for concatenating macro values.
#define STATUS_MACROS_IMPL_CONCAT_INNER_(x, y) x##y
#define STATUS_MACROS_IMPL_CONCAT_(x, y) STATUS_MACROS_IMPL_CONCAT_INNER_(x, y)
// The GNU compiler emits a warning for code like:
//
// if (foo)
// if (bar) { } else baz;
//
// because it thinks you might want the else to bind to the first if. This
// leads to problems with code like:
//
// if (do_expr) RETURN_IF_ERROR(expr) << "Some message";
//
// The "switch (0) case 0:" idiom is used to suppress this.
#define STATUS_MACROS_IMPL_ELSE_BLOCKER_ \
switch (0) \
case 0: \
default: // NOLINT
namespace tflite {
namespace support {
namespace status_macro_internal {
// Provides a conversion to bool so that it can be used inside an if statement
// that declares a variable.
class StatusAdaptorForMacros {
public:
StatusAdaptorForMacros(const ::absl::Status& status) // NOLINT
: status_(status) {}
StatusAdaptorForMacros(::absl::Status&& status) // NOLINT
: status_(std::move(status)) {}
StatusAdaptorForMacros(const StatusAdaptorForMacros&) = delete;
StatusAdaptorForMacros& operator=(const StatusAdaptorForMacros&) = delete;
explicit operator bool() const { return ABSL_PREDICT_TRUE(status_.ok()); }
::absl::Status&& Consume() { return std::move(status_); }
private:
::absl::Status status_;
};
} // namespace status_macro_internal
} // namespace support
} // namespace tflite
#endif // TENSORFLOW_LITE_SUPPORT_CC_PORT_DEFAULT_STATUS_MACROS_H_