Home | History | Annotate | Download | only in pdx
      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