#ifndef ANDROID_PDX_STATUS_H_ #define ANDROID_PDX_STATUS_H_ #include #include #include namespace android { namespace pdx { // This is a helper class for constructing Status with an error code. struct ErrorStatus { public: // NOLINTNEXTLINE(google-explicit-constructor) ErrorStatus(int error) : error_{error} {} int error() const { return error_; } static std::string ErrorToString(int error_code); private: int error_; }; // Status is a container class that can be used to return a value of type T // or error code to the caller. template class Status { public: // Default constructor so an empty Status object can be created. Status() : error_{-1} {} // Value copy/move constructors. These are intentionally not marked as // explicit to allow direct value returns from functions without having // to explicitly wrap them into Status(). // NOLINTNEXTLINE(google-explicit-constructor) Status(const T& value) : value_{value} {} // NOLINTNEXTLINE(google-explicit-constructor) Status(T&& value) : value_{std::move(value)} {} // Constructor for storing an error code inside the Status object. // NOLINTNEXTLINE(google-explicit-constructor) Status(const ErrorStatus& error_status) : error_{error_status.error()} {} // Copy/move constructors. Move constructor leaves |other| object in empty // state. Status(const Status& other) = default; Status(Status&& other) noexcept : value_{std::move(other.value_)}, error_{other.error_} { other.error_ = -1; } // Assignment operators. Status& operator=(const Status& other) = default; Status& operator=(Status&& other) noexcept { error_ = other.error_; value_ = std::move(other.value_); other.error_ = -1; T empty; std::swap(other.value_, empty); return *this; } // Change the value/error code of the status object directly. void SetValue(T value) { error_ = 0; value_ = std::move(value); } void SetError(int error) { error_ = error; T empty; std::swap(value_, empty); } // If |other| is in error state, copy the error code to this object. // Returns true if error was propagated template bool PropagateError(const Status& other) { if (!other.ok() && !other.empty()) { SetError(other.error()); return true; } return false; } // Returns true if the status object contains valid value for type T. // This means, the object is not empty and does not contain an error code. bool ok() const { return error_ == 0; } // Checks if the object is empty (doesn't contain a valid value nor an error). bool empty() const { return error_ < 0; } // Explicit bool conversion, equivalent to invoking ok(). explicit operator bool() const { return ok(); } // Accessors for the value stored in Status. Calling when ok() is false leads // to undefined behavior. const T& get() const { return value_; } T&& take() { error_ = -1; return std::move(value_); } // Returns the error code stored in the object. These codes are positive // non-zero values. // Can be called only when an error is actually stored (that is, the object // is not empty nor containing a valid value). int error() const { return std::max(error_, 0); } // Returns the error code as ErrorStatus object. This is a helper method // to aid in propagation of error codes between Status of different types // as in the following example: // Status foo() { // Status status = bar(); // if(!status) // return status.error_status(); // return 12; // } inline ErrorStatus error_status() const { return ErrorStatus{error()}; } // Returns the error message associated with error code stored in the object. // The message is the same as the string returned by strerror(status.error()). // Can be called only when an error is actually stored (that is, the object // is not empty nor containing a valid value). std::string GetErrorMessage() const { std::string message; if (error_ > 0) message = ErrorStatus::ErrorToString(error_); return message; } private: T value_{}; int error_{0}; }; // Specialization for status containing no other value but the error code. template <> class Status { public: Status() = default; // NOLINTNEXTLINE(google-explicit-constructor) Status(const ErrorStatus& error_status) : error_{error_status.error()} {} void SetValue() { error_ = 0; } void SetError(int error) { error_ = error; } template bool PropagateError(const Status& other) { if (!other.ok() && !other.empty()) { SetError(other.error()); return true; } return false; } bool ok() const { return error_ == 0; } bool empty() const { return false; } explicit operator bool() const { return ok(); } int error() const { return std::max(error_, 0); } inline ErrorStatus error_status() const { return ErrorStatus{error()}; } std::string GetErrorMessage() const { std::string message; if (error_ > 0) message = ErrorStatus::ErrorToString(error_); return message; } private: int error_{0}; }; // TODO(avakulenko): Remove these function once all callers of it are gone. inline int ReturnStatusOrError(const Status& status) { return status ? 0 : -status.error(); } inline int ReturnStatusOrError(const Status& status) { return status ? status.get() : -status.error(); } } // namespace pdx } // namespace android #endif // ANDROID_PDX_STATUS_H_