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)
ErrorStatusErrorStatus15 ErrorStatus(int error) : error_{error} {}
errorErrorStatus16 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.
Status()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)
Status(const T & value)36 Status(const T& value) : value_{value} {}
37 // NOLINTNEXTLINE(google-explicit-constructor)
Status(T && value)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)
Status(const ErrorStatus & error_status)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;
Status(Status && other)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.
SetValue(T value)64 void SetValue(T value) {
65 error_ = 0;
66 value_ = std::move(value);
67 }
SetError(int error)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>
PropagateError(const Status<U> & other)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.
ok()87 bool ok() const { return error_ == 0; }
88
89 // Checks if the object is empty (doesn't contain a valid value nor an error).
empty()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.
get()97 const T& get() const { return value_; }
take()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).
error()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 // }
error_status()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).
GetErrorMessage()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)
Status(const ErrorStatus & error_status)142 Status(const ErrorStatus& error_status) : error_{error_status.error()} {}
SetValue()143 void SetValue() { error_ = 0; }
SetError(int error)144 void SetError(int error) { error_ = error; }
145
146 template<typename U>
PropagateError(const Status<U> & other)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
ok()155 bool ok() const { return error_ == 0; }
empty()156 bool empty() const { return false; }
157 explicit operator bool() const { return ok(); }
error()158 int error() const { return std::max(error_, 0); }
error_status()159 inline ErrorStatus error_status() const { return ErrorStatus{error()}; }
GetErrorMessage()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.
ReturnStatusOrError(const Status<void> & status)172 inline int ReturnStatusOrError(const Status<void>& status) {
173 return status ? 0 : -status.error();
174 }
175
ReturnStatusOrError(const Status<int> & status)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