| // Copyright 2017 The Fuchsia 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 COBALT_SRC_PUBLIC_LIB_STATUSOR_STATUS_MACROS_H_ |
| #define COBALT_SRC_PUBLIC_LIB_STATUSOR_STATUS_MACROS_H_ |
| |
| #include <memory> |
| #include <ostream> // NOLINT |
| #include <sstream> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "src/public/lib/statusor/statusor.h" |
| |
| namespace cobalt::lib::statusor::status_macros { |
| |
| using cobalt::Status; |
| |
| // Stream object used to collect error messages in MAKE_ERROR macros |
| // or append error messages with APPEND_ERROR. It accepts any |
| // arguments with operator<< to build an error string, and then has an |
| // implicit cast operator to Status, which converts the |
| // logged string to a Status object and returns it, after logging the |
| // error. At least one call to operator<< is required; a compile time |
| // error will be generated if none are given. Errors will only be |
| // logged by default for certain status codes, as defined in |
| // IsLoggedByDefault. This class will give ERROR errors if you don't |
| // retrieve a Status exactly once before destruction. |
| // |
| // The class converts into an intermediate wrapper object |
| // MakeErrorStreamWithOutput to check that the error stream gets at least one |
| // item of input. |
| class MakeErrorStream { |
| public: |
| // Wrapper around MakeErrorStream that only allows for output. This |
| // is created as output of the first operator<< call on |
| // MakeErrorStream. The bare MakeErrorStream does not have a |
| // Status operator. The net effect of that is that you |
| // have to call operator<< at least once or else you'll get a |
| // compile time error. |
| class MakeErrorStreamWithOutput { |
| public: |
| explicit MakeErrorStreamWithOutput(MakeErrorStream* error_stream) |
| : wrapped_error_stream_(error_stream) {} |
| |
| template <typename T> |
| MakeErrorStreamWithOutput& operator<<(const T& value) { |
| *wrapped_error_stream_ << value; |
| return *this; |
| } |
| |
| // Implicit cast operators to Status and StatusOr. |
| // Exactly one of these must be called exactly once before destruction. |
| operator Status() { // NOLINT(google-explicit-constructor) |
| return wrapped_error_stream_->GetStatus(); |
| } |
| template <typename T> |
| operator StatusOr<T>() { // NOLINT(google-explicit-constructor) |
| return wrapped_error_stream_->GetStatus(); |
| } |
| |
| private: |
| MakeErrorStream* wrapped_error_stream_; |
| }; |
| |
| // When starting from an existing error status, this determines whether we'll |
| // append or prepend to that status's error message. |
| enum PriorMessageHandling { kAppendToPriorMessage, kPrependToPriorMessage }; |
| |
| // Make an error with the given code. |
| template <typename ERROR_CODE_TYPE> |
| MakeErrorStream(const char* file, int line, ERROR_CODE_TYPE code) |
| : impl_(new Impl(file, line, code, this, true)) {} |
| |
| template <typename T> |
| MakeErrorStreamWithOutput& operator<<(const T& value) { |
| CheckNotDone(); |
| impl_->stream_ << value; |
| return impl_->make_error_stream_with_output_wrapper_; |
| } |
| |
| // When this message is logged (see with_logging()), include the stack trace. |
| MakeErrorStream& with_log_stack_trace() { |
| impl_->should_log_stack_trace_ = true; |
| return *this; |
| } |
| |
| // Adds RET_CHECK failure text to error message. |
| MakeErrorStreamWithOutput& add_ret_check_failure(const char* condition) { |
| return *this << "RET_CHECK failure (" << impl_->file_ << ":" << impl_->line_ << ") " |
| << condition << " "; |
| } |
| |
| private: |
| class Impl { |
| public: |
| Impl(const char* file, int line, cobalt::StatusCode code, MakeErrorStream* error_stream, |
| bool is_logged_by_default = true); |
| Impl(const Status& status, PriorMessageHandling prior_message_handling, const char* file, |
| int line, MakeErrorStream* error_stream); |
| |
| ~Impl(); |
| |
| // This must be called exactly once before destruction. |
| Status GetStatus(); |
| |
| void CheckNotDone() const; |
| |
| private: |
| const char* file_; |
| int line_; |
| cobalt::StatusCode code_; |
| |
| PriorMessageHandling prior_message_handling_ = kAppendToPriorMessage; |
| std::string prior_message_; |
| bool is_done_; // true after Status object has been returned |
| std::ostringstream stream_; |
| bool should_log_; |
| int log_severity_; |
| bool should_log_stack_trace_; |
| |
| // Wrapper around the MakeErrorStream object that has a |
| // Status conversion. The first << operator called on |
| // MakeErrorStream will return this object, and only this object |
| // can implicitly convert to Status. The net effect of |
| // this is that you'll get a compile time error if you call |
| // MAKE_ERROR etc. without adding any output. |
| MakeErrorStreamWithOutput make_error_stream_with_output_wrapper_; |
| |
| friend class MakeErrorStream; |
| }; |
| |
| void CheckNotDone() const; |
| |
| // Returns the status. Used by MakeErrorStreamWithOutput. |
| [[nodiscard]] Status GetStatus() const { return impl_->GetStatus(); } |
| |
| // Store the actual data on the heap to reduce stack frame sizes. |
| std::unique_ptr<Impl> impl_; |
| }; |
| |
| // Provides a conversion to bool so that it can be used inside an if statement |
| // that declares a variable. |
| class StatusAdaptorForMacros { |
| public: |
| explicit StatusAdaptorForMacros(Status status) : status_(std::move(status)) {} |
| |
| StatusAdaptorForMacros(const StatusAdaptorForMacros&) = delete; |
| StatusAdaptorForMacros& operator=(const StatusAdaptorForMacros&) = delete; |
| |
| explicit operator bool() const { return status_.ok(); } |
| |
| Status&& Consume() { return std::move(status_); } |
| |
| private: |
| Status status_; |
| }; |
| |
| } // namespace cobalt::lib::statusor::status_macros |
| |
| // Early-returns the status if it is an error, otherwise it proceeds. |
| // |
| // The argument expression is evaluated only once. |
| #define CB_RETURN_IF_ERROR(__status) \ |
| do { \ |
| auto status = (__status); \ |
| if (!status.ok()) { \ |
| return status; \ |
| } \ |
| } while (false) |
| |
| #define CB_RET_CHECK(condition) \ |
| while (!(condition)) \ |
| return util::status_macros::MakeErrorStream(__FILE__, __LINE__, tensorflow::error::INTERNAL) \ |
| .with_log_stack_trace() \ |
| .add_ret_check_failure(#condition) |
| |
| #define CB_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ |
| CB_ASSERT_OK_AND_ASSIGN_IMPL(CB_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \ |
| rexpr); |
| |
| #define CB_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \ |
| auto statusor = (rexpr); /* NOLINT */ \ |
| ASSERT_TRUE(statusor.status().ok()) /*NOLINT*/ \ |
| << "status:" << statusor.status().error_code() << ", " /*NOLINT*/ \ |
| << statusor.status().error_message(); /* NOLINT */ \ |
| lhs = std::move(statusor.ValueOrDie()) /* NOLINT */ |
| |
| #define CB_STATUS_MACROS_CONCAT_NAME(x, y) CB_STATUS_MACROS_CONCAT_IMPL(x, y) |
| #define CB_STATUS_MACROS_CONCAT_IMPL(x, y) x##y |
| |
| #define CB_ASSIGN_OR_RETURN(lhs, rexpr) \ |
| CB_ASSIGN_OR_RETURN_IMPL(CB_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr) |
| |
| #define CB_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \ |
| auto statusor = (rexpr); /* NOLINT */ \ |
| if (!statusor.ok()) { /* NOLINT */ \ |
| return statusor.status(); /* NOLINT */ \ |
| } \ |
| lhs = std::move(statusor.ValueOrDie()) /* NOLINT */ |
| |
| #endif // COBALT_SRC_PUBLIC_LIB_STATUSOR_STATUS_MACROS_H_ |