1 #ifndef ANDROID_PDX_STATUS_H_ 2 #define ANDROID_PDX_STATUS_H_ 3 4 #include <algorithm> 5 #include <memory> 6 #include <string> 7 8 namespace android { 9 namespace pdx { 10 11 // This is a helper class for constructing Status<T> with an error code. 12 struct ErrorStatus { 13 public: 14 // NOLINTNEXTLINE(google-explicit-constructor) 15 ErrorStatus(int error) : error_{error} {} 16 int error() const { return error_; } 17 18 static std::string ErrorToString(int error_code); 19 20 private: 21 int error_; 22 }; 23 24 // Status<T> is a container class that can be used to return a value of type T 25 // or error code to the caller. 26 template <typename T> 27 class Status { 28 public: 29 // Default constructor so an empty Status object can be created. 30 Status() : error_{-1} {} 31 32 // Value copy/move constructors. These are intentionally not marked as 33 // explicit to allow direct value returns from functions without having 34 // to explicitly wrap them into Status<T>(). 35 // NOLINTNEXTLINE(google-explicit-constructor) 36 Status(const T& value) : value_{value} {} 37 // NOLINTNEXTLINE(google-explicit-constructor) 38 Status(T&& value) : value_{std::move(value)} {} 39 40 // Constructor for storing an error code inside the Status object. 41 // NOLINTNEXTLINE(google-explicit-constructor) 42 Status(const ErrorStatus& error_status) : error_{error_status.error()} {} 43 44 // Copy/move constructors. Move constructor leaves |other| object in empty 45 // state. 46 Status(const Status& other) = default; 47 Status(Status&& other) noexcept 48 : value_{std::move(other.value_)}, error_{other.error_} { 49 other.error_ = -1; 50 } 51 52 // Assignment operators. 53 Status& operator=(const Status& other) = default; 54 Status& operator=(Status&& other) noexcept { 55 error_ = other.error_; 56 value_ = std::move(other.value_); 57 other.error_ = -1; 58 T empty; 59 std::swap(other.value_, empty); 60 return *this; 61 } 62 63 // Change the value/error code of the status object directly. 64 void SetValue(T value) { 65 error_ = 0; 66 value_ = std::move(value); 67 } 68 void SetError(int error) { 69 error_ = error; 70 T empty; 71 std::swap(value_, empty); 72 } 73 74 // If |other| is in error state, copy the error code to this object. 75 // Returns true if error was propagated 76 template<typename U> 77 bool PropagateError(const Status<U>& other) { 78 if (!other.ok() && !other.empty()) { 79 SetError(other.error()); 80 return true; 81 } 82 return false; 83 } 84 85 // Returns true if the status object contains valid value for type T. 86 // This means, the object is not empty and does not contain an error code. 87 bool ok() const { return error_ == 0; } 88 89 // Checks if the object is empty (doesn't contain a valid value nor an error). 90 bool empty() const { return error_ < 0; } 91 92 // Explicit bool conversion, equivalent to invoking ok(). 93 explicit operator bool() const { return ok(); } 94 95 // Accessors for the value stored in Status. Calling when ok() is false leads 96 // to undefined behavior. 97 const T& get() const { return value_; } 98 T&& take() { 99 error_ = -1; 100 return std::move(value_); 101 } 102 103 // Returns the error code stored in the object. These codes are positive 104 // non-zero values. 105 // Can be called only when an error is actually stored (that is, the object 106 // is not empty nor containing a valid value). 107 int error() const { return std::max(error_, 0); } 108 109 // Returns the error code as ErrorStatus object. This is a helper method 110 // to aid in propagation of error codes between Status<T> of different types 111 // as in the following example: 112 // Status<int> foo() { 113 // Status<void> status = bar(); 114 // if(!status) 115 // return status.error_status(); 116 // return 12; 117 // } 118 inline ErrorStatus error_status() const { return ErrorStatus{error()}; } 119 120 // Returns the error message associated with error code stored in the object. 121 // The message is the same as the string returned by strerror(status.error()). 122 // Can be called only when an error is actually stored (that is, the object 123 // is not empty nor containing a valid value). 124 std::string GetErrorMessage() const { 125 std::string message; 126 if (error_ > 0) 127 message = ErrorStatus::ErrorToString(error_); 128 return message; 129 } 130 131 private: 132 T value_{}; 133 int error_{0}; 134 }; 135 136 // Specialization for status containing no other value but the error code. 137 template <> 138 class Status<void> { 139 public: 140 Status() = default; 141 // NOLINTNEXTLINE(google-explicit-constructor) 142 Status(const ErrorStatus& error_status) : error_{error_status.error()} {} 143 void SetValue() { error_ = 0; } 144 void SetError(int error) { error_ = error; } 145 146 template<typename U> 147 bool PropagateError(const Status<U>& other) { 148 if (!other.ok() && !other.empty()) { 149 SetError(other.error()); 150 return true; 151 } 152 return false; 153 } 154 155 bool ok() const { return error_ == 0; } 156 bool empty() const { return false; } 157 explicit operator bool() const { return ok(); } 158 int error() const { return std::max(error_, 0); } 159 inline ErrorStatus error_status() const { return ErrorStatus{error()}; } 160 std::string GetErrorMessage() const { 161 std::string message; 162 if (error_ > 0) 163 message = ErrorStatus::ErrorToString(error_); 164 return message; 165 } 166 167 private: 168 int error_{0}; 169 }; 170 171 // TODO(avakulenko): Remove these function once all callers of it are gone. 172 inline int ReturnStatusOrError(const Status<void>& status) { 173 return status ? 0 : -status.error(); 174 } 175 176 inline int ReturnStatusOrError(const Status<int>& status) { 177 return status ? status.get() : -status.error(); 178 } 179 180 } // namespace pdx 181 } // namespace android 182 183 #endif // ANDROID_PDX_STATUS_H_ 184