// Copyright 2019 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 DARWINN_PORT_DEFAULT_STATUS_MACROS_H_ #define DARWINN_PORT_DEFAULT_STATUS_MACROS_H_ #include #include // NOLINT #include #include #include #include "port/default/error_codes.h" #include "port/default/logging.h" #include "port/default/macros.h" #include "port/default/status.h" #include "port/default/statusor.h" namespace platforms { namespace darwinn { namespace status_macros { // 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 DFATAL 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 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() { return wrapped_error_stream_->GetStatus(); } template operator StatusOr() { return wrapped_error_stream_->GetStatus(); } private: MakeErrorStream* wrapped_error_stream_; DISALLOW_COPY_AND_ASSIGN(MakeErrorStreamWithOutput); }; // 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 MakeErrorStream(const char* file, int line, ERROR_CODE_TYPE code) : impl_(new Impl(file, line, code, this, true)) {} template 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, error::Code 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_; error::Code 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; DISALLOW_COPY_AND_ASSIGN(Impl); }; void CheckNotDone() const; // Returns the status. Used by MakeErrorStreamWithOutput. Status GetStatus() const { return impl_->GetStatus(); } // Store the actual data on the heap to reduce stack frame sizes. std::unique_ptr impl_; DISALLOW_COPY_AND_ASSIGN(MakeErrorStream); }; // 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 PREDICT_TRUE(status_.ok()); } Status&& Consume() { return std::move(status_); } private: Status status_; }; } // namespace status_macros } // namespace darwinn } // namespace platforms #define RET_CHECK(condition) \ while (PREDICT_FALSE(!(condition))) \ return ::platforms::darwinn::status_macros::MakeErrorStream( \ __FILE__, __LINE__, ::platforms::darwinn::error::INTERNAL) \ .with_log_stack_trace() \ .add_ret_check_failure(#condition) #define ASSIGN_OR_ASSERT_OK(lhs, rexpr) \ ASSIGN_OR_ASSERT_OK_IMPL( \ STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr); #define ASSIGN_OR_ASSERT_OK_IMPL(statusor, lhs, rexpr) \ auto statusor = (rexpr); \ ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \ lhs = statusor.ConsumeValueOrDie() #define STATUS_MACROS_CONCAT_NAME(x, y) STATUS_MACROS_CONCAT_IMPL(x, y) #define STATUS_MACROS_CONCAT_IMPL(x, y) x##y #if defined(_WIN32) #define ASSIGN_OR_RETURN(_1, _2, ...) ASSIGN_OR_RETURN_IMPL_2(_1, _2) #else #define ASSIGN_OR_RETURN(...) \ STATUS_MACRO_GET_VARIADIC_IMPL(__VA_ARGS__, ASSIGN_OR_RETURN_IMPL_3, \ ASSIGN_OR_RETURN_IMPL_2) \ (__VA_ARGS__) #define STATUS_MACRO_GET_VARIADIC_IMPL(_1, _2, _3, NAME, ...) NAME #endif #define ASSIGN_OR_RETURN_IMPL_2(lhs, rexpr) ASSIGN_OR_RETURN_IMPL_3(lhs, rexpr) #define ASSIGN_OR_RETURN_IMPL_3(lhs, rexpr) \ ASSIGN_OR_RETURN_IMPL( \ STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr) #define ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \ auto statusor = (rexpr); \ if (PREDICT_FALSE(!statusor.ok())) { \ return statusor.status(); \ } \ lhs = std::move(statusor.ValueOrDie()) // For propagating errors when calling a function. #define RETURN_IF_ERROR(expr) \ do { \ const ::platforms::darwinn::Status _status = (expr); \ if (PREDICT_FALSE(!_status.ok())) return _status; \ } while (0) #define RETURN_WITH_CONTEXT_IF_ERROR(expr, ...) \ do { \ ::platforms::darwinn::Status _status = (expr); \ if (PREDICT_FALSE(!_status.ok())) { \ ::platforms::darwinn::AppendToMessage(&_status, __VA_ARGS__); \ return _status; \ } \ } while (0) #endif // DARWINN_PORT_DEFAULT_STATUS_MACROS_H_