| // Copyright (c) 2022 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef QUICHE_COMMON_QUICHE_STATUS_UTILS_H_ | |
| #define QUICHE_COMMON_QUICHE_STATUS_UTILS_H_ | |
| #include <utility> | |
| #include "absl/base/optimization.h" | |
| #include "absl/status/status.h" | |
| #include "absl/status/statusor.h" // IWYU pragma: keep | |
| #include "absl/strings/cord.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| namespace quiche { | |
| // A simplified version of the standard google3 "return if error" macro. Unlike | |
| // the standard version, this does not come with a StatusBuilder support; the | |
| // AppendToStatus() function below is meant to partially fill that gap. | |
| #define QUICHE_RETURN_IF_ERROR(expr) \ | |
| do { \ | |
| absl::Status quiche_status_macro_value = (expr); \ | |
| if (ABSL_PREDICT_FALSE(!quiche_status_macro_value.ok())) { \ | |
| return quiche_status_macro_value; \ | |
| } \ | |
| } while (0) | |
| // A simplified version of the standard google3 "assign or return" macro. | |
| // Unlike the standard version, this does not come with a StatusBuilder support; | |
| // the optional error-mapper lambda parameter is meant to partially fill that | |
| // gap. | |
| // | |
| // Example usage, where `Calculate()` returns `absl::StatusOr<Foo>`: | |
| // | |
| // QUICHE_ASSIGN_OR_RETURN(Foo x, Calculate()); | |
| // | |
| // Optionally, the macro also accepts an error-mapper lambda that takes a `const | |
| // absl::Status&` and returns `absl::Status`. The lambda is only invoked when | |
| // the status is an error. When it is called, its return value becomes the | |
| // caller's return value. | |
| // | |
| // Example of using the error-mapper lambda to transform the error: | |
| // | |
| // QUICHE_ASSIGN_OR_RETURN(Foo x, Calculate(), | |
| // [](const absl::Status& status) { | |
| // return absl::InvalidArgumentError(absl::StrCat( | |
| // "custom message: ", status.message())); | |
| // }); | |
| // | |
| // Example of using the error-mapper lambda purely for its side effects: | |
| // | |
| // QUICHE_ASSIGN_OR_RETURN(Foo x, Calculate(), | |
| // [](const absl::Status& status) { | |
| // QUICHE_LOG(INFO) << status; | |
| // return status; | |
| // }); | |
| #define QUICHE_ASSIGN_OR_RETURN(lhs, expr, ...) \ | |
| QUICHE_ASSIGN_OR_RETURN_IMPL( \ | |
| lhs, (expr), \ | |
| QUICHE_STATUS_UTILS_INTERNAL_CONCAT2( \ | |
| quiche_status_utils_internal_statusor, __COUNTER__), \ | |
| QUICHE_STATUS_UTILS_INTERNAL_CONCAT2( \ | |
| quiche_status_utils_internal_status, __COUNTER__), \ | |
| QUICHE_STATUS_UTILS_INTERNAL_CONCAT2( \ | |
| quiche_status_utils_internal_lambda, __COUNTER__) __VA_OPT__(, ) \ | |
| __VA_ARGS__) | |
| // Copies absl::Status payloads from `original` to `target`; required to copy a | |
| // status correctly. | |
| inline void CopyStatusPayloads(const absl::Status& original, | |
| absl::Status& target) { | |
| original.ForEachPayload([&](absl::string_view key, const absl::Cord& value) { | |
| target.SetPayload(key, value); | |
| }); | |
| } | |
| // Appends additional into to a status message if the status message is | |
| // an error. | |
| template <typename... T> | |
| absl::Status AppendToStatus(absl::Status input, T&&... args) { | |
| if (ABSL_PREDICT_TRUE(input.ok())) { | |
| return input; | |
| } | |
| absl::Status result = absl::Status( | |
| input.code(), absl::StrCat(input.message(), std::forward<T>(args)...)); | |
| CopyStatusPayloads(input, result); | |
| return result; | |
| } | |
| // ======================================================================== | |
| // == Implementation details. Do not depend on anything below this line. == | |
| // ======================================================================== | |
| #ifndef __COUNTER__ | |
| static_assert(false, "QUICHE_ASSIGN_OR_RETURN requires the __COUNTER__ macro."); | |
| #endif | |
| // clang-format off | |
| #define QUICHE_ASSIGN_OR_RETURN_IMPL(lhs, expr, statusor_ident, status_ident, \ | |
| lambda_ident, ...) \ | |
| auto statusor_ident = (expr); \ | |
| if (ABSL_PREDICT_FALSE(!statusor_ident.ok())) { \ | |
| absl::Status status_ident = statusor_ident.status(); \ | |
| /* Invoke the error-mapper lambda if the variadic args are non-empty. */ \ | |
| __VA_OPT__( \ | |
| auto lambda_ident = (__VA_ARGS__); \ | |
| static_assert(std::is_convertible_v< \ | |
| decltype(lambda_ident), \ | |
| std::function<absl::Status(const absl::Status&)>>, \ | |
| "QUICHE_ASSIGN_OR_RETURN: the on-error lambda has an " \ | |
| "unexpected type"); \ | |
| status_ident = lambda_ident(status_ident); \ | |
| ) \ | |
| return status_ident; \ | |
| } \ | |
| lhs = std::move(statusor_ident).value(); | |
| // clang-format on | |
| #define QUICHE_STATUS_UTILS_INTERNAL_CONCAT2(a, b) \ | |
| QUICHE_STATUS_UTILS_INTERNAL_CONCAT2_IMPL(a, b) | |
| // Without this multi-layer concat macro, `__COUNTER__` would not be expanded. | |
| #define QUICHE_STATUS_UTILS_INTERNAL_CONCAT2_IMPL(a, b) a##b | |
| } // namespace quiche | |
| #endif // QUICHE_COMMON_QUICHE_STATUS_UTILS_H_ |