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