blob: 36cf33a8dfe04f8563029d8e584ada0e5e3ac08a [file] [log] [blame]
/*
* Copyright 2020 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.
*/
// This library provides several useful macros for simplifying very, very common
// patterns that occur when writing code that make extensive use of absl::Status
// and absl::StatusOr.
//
// In particular, it provides macros:
//
// * ECCLESIA_RETURN_IF_ERROR(expr):
// This will evaluate an expression that is expected to produce an
// absl::Status. If the status is not OK it returns the status.
//
// An example of usage is:
//
// ECCLESIA_RETURN_IF_ERROR(file->Write(my_text));
// ECCLESIA_RETURN_IF_ERROR(file->Write(more_text));
//
// This will try and perform a pair of writes, returning immediately if
// either one of them fails. If both succeed then the code will continue
// on, not returning at all.
//
// * ECCLESIA_ASSIGN_OR_RETURN(var, expr):
// This will evaluate an expression that is expected to produce an
// absl::StatusOr<T> for some type. If the status is not OK it returns the
// status. If the status is OK it will move the value into var. The
// expression for var can be either an existing variable name, or the
// declaration of a new variable.
//
// Examples usage would look like:
//
// ECCLESIA_ASSIGN_OR_RETURN(buffer, file->Read());
// ECCLESIA_ASSIGN_OR_RETURN(DataStructure data,
// parser->Decode(buffer));
//
// The first example is a simple assignment: read from a file, returning
// the not-OK status if it fails and assigning the results to "buffer" if
// it succeeds. The second example then does something more complex, not
// just assigning a variable but declaring a new DataStructure variable.
// However, after that declaration, it works the same way: if the parser
// decode fails it returns the not-OK status, and otherwise it initializes
// the "data" variable with the result.
//
// The variable will be assigned using move assignment, and so the type
// stored in the StatusOr must be movable (or copyable).
//
// Unlike some other status macro libraries you may or may not have seen, this
// one does not attempt to provide any complex error handling or extensive
// customization options. If you are writing code that needs to do more than
// immediately return on an error then you'll have to write it out manually.
//
// In general it is best to avoid using this code directly in header files if
// possible, since the macros in here will then pollute the namespaces of any
// code that includes the headers.
//
// Don't use any of the ECCLESIA_STATUS_MACROS_* macros directly, they're only
// intended to help implement the actual documented macros.
#ifndef THIRD_PARTY_ECCLESIA_LIB_STATUS_MACROS_H_
#define THIRD_PARTY_ECCLESIA_LIB_STATUS_MACROS_H_
// Return-if-error implementation. We use a simple do-while idiom to wrap the
// code in a scope that avoids introducing any variables and plays nicely with
// surrounding expressions.
#define ECCLESIA_RETURN_IF_ERROR(expr) \
do { \
auto _expr_result = (expr); \
if (!_expr_result.ok()) { \
return _expr_result; \
} \
} while (0)
// Assign-or-return implementation. Unfortunately, it's not really possible for
// us to avoid leaking a temporary variable into the enclosing scope. We can't
// wrap the whole thing in a scope because then "var" can't be used to declare a
// new variable. So instead we make up a new name and bolt the line number onto
// it. This does then mean that you can't use multiple of these macros on a
// single line but we're not trying to support that kind of thing.
#define ECCLESIA_ASSIGN_OR_RETURN(var, expr) \
ECCLESIA_STATUS_MACROS_ASSIGN_OR_RETURN_IMPL( \
ECCLESIA_STATUS_MACROS_CONCAT(_status_or_expr, __LINE__), var, expr)
// Inner implementation of ECCLESIA_ASSIGN_OR_RETURN. Adds in a parameter which
// is used to supply the name for the temporary variable used to store the
// statusor.
#define ECCLESIA_STATUS_MACROS_ASSIGN_OR_RETURN_IMPL(statusor, var, expr) \
auto statusor = (expr); \
if (!statusor.ok()) { \
return statusor.status(); \
} \
var = std::move(statusor).value();
// Helpers for concatenating values, needed to construct "unique" names.
// These helpers are also used in //testing/macros.h.
#define ECCLESIA_STATUS_MACROS_CONCAT_INNER(x, y) x##y
#define ECCLESIA_STATUS_MACROS_CONCAT(x, y) \
ECCLESIA_STATUS_MACROS_CONCAT_INNER(x, y)
#endif // THIRD_PARTY_ECCLESIA_LIB_STATUS_MACROS_H_