| #ifndef ANDROID_PDX_STATUS_H_ |
| #define ANDROID_PDX_STATUS_H_ |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| |
| namespace android { |
| namespace pdx { |
| |
| // This is a helper class for constructing Status<T> with an error code. |
| struct ErrorStatus { |
| public: |
| ErrorStatus(int error) : error_{error} {} |
| int error() const { return error_; } |
| |
| static std::string ErrorToString(int error_code); |
| |
| private: |
| int error_; |
| }; |
| |
| // Status<T> is a container class that can be used to return a value of type T |
| // or error code to the caller. |
| template <typename T> |
| 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<T>(). |
| Status(const T& value) : value_{value} {} // NOLINT(runtime/explicit) |
| Status(T&& value) : value_{std::move(value)} {} // NOLINT(runtime/explicit) |
| |
| // Constructor for storing an error code inside the Status object. |
| Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit) |
| : error_{error_status.error()} {} |
| |
| // Copy/move constructors. Move constructor leaves |other| object in empty |
| // state. |
| Status(const Status& other) = default; |
| Status(Status&& other) |
| : value_{std::move(other.value_)}, error_{other.error_} { |
| other.error_ = -1; |
| } |
| |
| // Assignment operators. |
| Status& operator=(const Status& other) = default; |
| Status& operator=(Status&& other) { |
| 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<typename U> |
| bool PropagateError(const Status<U>& 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<T> of different types |
| // as in the following example: |
| // Status<int> foo() { |
| // Status<void> 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<void> { |
| public: |
| Status() = default; |
| Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit) |
| : error_{error_status.error()} {} |
| void SetValue() { error_ = 0; } |
| void SetError(int error) { error_ = error; } |
| |
| template<typename U> |
| bool PropagateError(const Status<U>& 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<void>& status) { |
| return status ? 0 : -status.error(); |
| } |
| |
| inline int ReturnStatusOrError(const Status<int>& status) { |
| return status ? status.get() : -status.error(); |
| } |
| |
| } // namespace pdx |
| } // namespace android |
| |
| #endif // ANDROID_PDX_STATUS_H_ |