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