sql_utils/base/status_macros.h (62 lines of code) (raw):
/*
* Copyright 2023 Google LLC
*
* 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.
*/
#ifndef THIRD_PARTY_PY_BIGQUERY_ML_UTILS_SQL_UTILS_BASE_STATUS_MACROS_H_
#define THIRD_PARTY_PY_BIGQUERY_ML_UTILS_SQL_UTILS_BASE_STATUS_MACROS_H_
#include "absl/status/status.h"
#include "sql_utils/base/status_builder.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() {
// SQL_RETURN_IF_ERROR(Function(args...));
// SQL_RETURN_IF_ERROR(foo.Method(args...));
// return absl::OkStatus();
// }
//
// The macro ends with a `bigquery_ml_utils_base::StatusBuilder` which allows the returned
// status to be extended with more details. Any chained expressions after the
// macro will not be evaluated unless there is an error.
//
// For example:
// absl::Status MultiStepFunction() {
// SQL_RETURN_IF_ERROR(Function(args...)) << "in MultiStepFunction";
// SQL_RETURN_IF_ERROR(foo.Method(args...)).Log(base_logging::ERROR)
// << "while processing query: " << query.DebugString();
// return absl::OkStatus();
// }
//
//
// If using this macro inside a lambda, you need to annotate the return type
// to avoid confusion between a `bigquery_ml_utils_base::StatusBuilder` and a
// `absl::Status` type. E.g.
//
// []() -> ::absl::Status {
// SQL_RETURN_IF_ERROR(Function(args...));
// SQL_RETURN_IF_ERROR(foo.Method(args...));
// return absl::OkStatus();
// }
#define SQL_RETURN_IF_ERROR(expr) \
SQL_STATUS_MACROS_IMPL_ELSE_BLOCKER_ \
if (::bigquery_ml_utils_base::status_macro_internal::StatusAdaptorForMacros \
status_macro_internal_adaptor = {(expr), SQL_LOC}) { \
} else /* NOLINT */ \
return status_macro_internal_adaptor.Consume()
// Executes an expression `rexpr` that returns a `absl::StatusOr<T>`. On
// OK, extracts 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:
//
// SQL_ASSIGN_OR_RETURN(lhs, rexpr)
// SQL_ASSIGN_OR_RETURN(lhs, rexpr, error_expression);
//
// 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):
// SQL_ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(arg));
//
// Example: Assigning to an existing variable:
// ValueType value;
// SQL_ASSIGN_OR_RETURN(value, MaybeGetValue(arg));
//
// Example: Assigning to an expression with side effects:
// MyProto data;
// SQL_ASSIGN_OR_RETURN(*data.mutable_str(), MaybeGetValue(arg));
// // No field "str" is added on error.
//
// Example: Assigning to a std::unique_ptr.
// SQL_ASSIGN_OR_RETURN(std::unique_ptr<T> ptr, MaybeGetPtr(arg));
//
// If passed, the `error_expression` is evaluated to produce the return
// value. The expression may reference any variable visible in scope, as
// well as a `bigquery_ml_utils_base::StatusBuilder` object populated with the error and
// named by a single underscore `_`. The expression typically uses the
// builder to modify the status and is returned directly in manner similar
// to SQL_RETURN_IF_ERROR. The expression may, however, evaluate to any type
// returnable by the function, including (void). For example:
//
// Example: Adjusting the error message.
// SQL_ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(query),
// _ << "while processing query " << query.DebugString());
//
// Example: Logging the error on failure.
// SQL_ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(query),
// _.LogError());
//
#define SQL_ASSIGN_OR_RETURN(...) \
SQL_STATUS_MACROS_IMPL_GET_VARIADIC_( \
__VA_ARGS__, SQL_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_, \
SQL_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_2_) \
(__VA_ARGS__)
// =================================================================
// == Implementation details, do not rely on anything below here. ==
// =================================================================
#define SQL_STATUS_MACROS_IMPL_GET_VARIADIC_(_1, _2, _3, NAME, ...) NAME
#define SQL_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_2_(lhs, rexpr) \
SQL_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_(lhs, rexpr, std::move(_))
#define SQL_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_(lhs, rexpr, \
error_expression) \
SQL_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_( \
SQL_STATUS_MACROS_IMPL_CONCAT_(_status_or_value, __LINE__), lhs, \
rexpr, error_expression)
#define SQL_STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_(statusor, lhs, rexpr, \
error_expression) \
auto statusor = (rexpr); \
if (ABSL_PREDICT_FALSE(!statusor.ok())) { \
::bigquery_ml_utils_base::StatusBuilder _(std::move(statusor).status(), \
SQL_LOC); \
(void)_; /* error_expression is allowed to not use this variable */ \
return (error_expression); \
} \
lhs = std::move(statusor).value()
// Internal helper for concatenating macro values.
#define SQL_STATUS_MACROS_IMPL_CONCAT_INNER_(x, y) x##y
#define SQL_STATUS_MACROS_IMPL_CONCAT_(x, y) \
SQL_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) SQL_RETURN_IF_ERROR(expr) << "Some message";
//
// The "switch (0) case 0:" idiom is used to suppress this.
#define SQL_STATUS_MACROS_IMPL_ELSE_BLOCKER_ \
switch (0) \
case 0: \
default: // NOLINT
namespace bigquery_ml_utils_base {
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, SourceLocation loc)
: builder_(status, loc) {}
StatusAdaptorForMacros(absl::Status&& status, SourceLocation loc)
: builder_(std::move(status), loc) {}
StatusAdaptorForMacros(const StatusBuilder& builder, SourceLocation loc)
: builder_(builder) {}
StatusAdaptorForMacros(StatusBuilder&& builder, SourceLocation loc)
: builder_(std::move(builder)) {}
StatusAdaptorForMacros(const StatusAdaptorForMacros&) = delete;
StatusAdaptorForMacros& operator=(const StatusAdaptorForMacros&) = delete;
explicit operator bool() const { return ABSL_PREDICT_TRUE(builder_.ok()); }
bigquery_ml_utils_base::StatusBuilder&& Consume() { return std::move(builder_); }
private:
bigquery_ml_utils_base::StatusBuilder builder_;
};
} // namespace status_macro_internal
} // namespace bigquery_ml_utils_base
#endif // THIRD_PARTY_PY_BIGQUERY_ML_UTILS_SQL_UTILS_BASE_STATUS_MACROS_H_