1#ifndef ANDROID_PDX_STATUS_H_
2#define ANDROID_PDX_STATUS_H_
3
4#include <algorithm>
5#include <memory>
6#include <string>
7
8namespace android {
9namespace pdx {
10
11// This is a helper class for constructing Status<T> with an error code.
12struct ErrorStatus {
13 public:
14  ErrorStatus(int error) : error_{error} {}
15  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.
25template <typename T>
26class Status {
27 public:
28  // Default constructor so an empty Status object can be created.
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>().
34  Status(const T& value) : value_{value} {}        // NOLINT(runtime/explicit)
35  Status(T&& value) : value_{std::move(value)} {}  // NOLINT(runtime/explicit)
36
37  // Constructor for storing an error code inside the Status object.
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;
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.
61  void SetValue(T value) {
62    error_ = 0;
63    value_ = std::move(value);
64  }
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>
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.
84  bool ok() const { return error_ == 0; }
85
86  // Checks if the object is empty (doesn't contain a valid value nor an error).
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.
94  const T& get() const { return value_; }
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).
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  //    }
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).
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.
134template <>
135class Status<void> {
136 public:
137  Status() = default;
138  Status(const ErrorStatus& error_status)  // NOLINT(runtime/explicit)
139      : error_{error_status.error()} {}
140  void SetValue() { error_ = 0; }
141  void SetError(int error) { error_ = error; }
142
143  template<typename U>
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
152  bool ok() const { return error_ == 0; }
153  bool empty() const { return false; }
154  explicit operator bool() const { return ok(); }
155  int error() const { return std::max(error_, 0); }
156  inline ErrorStatus error_status() const { return ErrorStatus{error()}; }
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.
169inline int ReturnStatusOrError(const Status<void>& status) {
170  return status ? 0 : -status.error();
171}
172
173inline 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