status.h
1 // Copyright 2017, Beeri 15. All rights reserved.
2 // Author: Roman Gershman (romange@gmail.com)
3 //
4 
5 #pragma once
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/macros.h"
11 #include "util/status.pb.h"
12 
13 namespace util {
14 
15 class Status {
16  public:
17  constexpr Status() noexcept {}
18 
19  // copy c'tor makes copy of error detail so Status can be returned by value
20  Status(const Status& status)
21  : error_detail_(status.error_detail_ ? new ErrorDetail(*status.error_detail_) : nullptr) {}
22 
23  // Move c'tor.
24  Status(Status&& st) noexcept : error_detail_(std::move(st.error_detail_)) {}
25 
26  // c'tor for error case - is this useful for anything other than CANCELLED?
27  Status(StatusCode::Code code) : error_detail_(new ErrorDetail(code)) {}
28 
29  // c'tor for error case
30  Status(StatusCode::Code code, std::string error_msg)
31  : error_detail_(new ErrorDetail(code, std::move(error_msg))) {}
32 
33  // c'tor for internal error
34  Status(std::string error_msg)
35  : error_detail_(new ErrorDetail(StatusCode::INTERNAL_ERROR, std::move(error_msg))) {}
36 
37  // same as copy c'tor
38  Status& operator=(const Status& status) {
39  if (LIKELY(!status.error_detail_)) {
40  error_detail_.reset();
41  } else {
42  error_detail_.reset(new ErrorDetail{*status.error_detail_});
43  }
44  return *this;
45  }
46 
47  Status& operator=(Status&& status) noexcept {
48  std::swap(error_detail_, status.error_detail_);
49  return *this;
50  }
51 
52  bool ok() const { return !error_detail_; }
53 
54  bool IsCancelled() const {
55  return error_detail_ && error_detail_->error_code == StatusCode::CANCELLED;
56  }
57 
58  // Does nothing if status.ok().
59  // Otherwise: if 'this' is an error status, adds the error msg from 'status;
60  // otherwise assigns 'status'.
61  void AddError(const Status& status);
62  void AddErrorMsg(StatusCode::Code code, const std::string& msg);
63  void AddErrorMsg(const std::string& msg);
64  void GetErrorMsgs(std::vector<std::string>* msgs) const;
65  void GetErrorMsg(std::string* msg) const;
66 
67  std::string ToString() const {
68  std::string msg;
69  GetErrorMsg(&msg);
70  return msg;
71  }
72 
73  StatusCode::Code code() const {
74  return error_detail_ == NULL ? StatusCode::OK : error_detail_->error_code;
75  }
76 
77  static const Status OK;
78  static const Status CANCELLED;
79 
80  friend std::ostream& operator<<(std::ostream& o, const Status& status);
81 
82  private:
83  static Status ByCode(StatusCode::Code code) { return Status(code); }
84 
85  struct ErrorDetail {
86  StatusCode::Code error_code; // anything other than OK
87  std::vector<std::string> error_msgs;
88 
89  ErrorDetail(StatusCode::Code code) : error_code(code) {}
90  ErrorDetail(StatusCode::Code code, std::string msg) : error_code(code) {
91  error_msgs.push_back(std::move(msg));
92  }
93  };
94 
95  std::unique_ptr<ErrorDetail> error_detail_;
96 };
97 
98 // Sometimes functions need to return both data object and status.
99 // It's inconvenient to set this data object by reference via argument parameter.
100 // StatusObject should help with this problem.
101 template <typename T> struct StatusObject {
102  Status status;
103  T obj;
104 
105  bool ok() const { return status.ok(); }
106 
107  StatusObject() noexcept = default;
108  StatusObject(StatusObject&&) noexcept = default;
109  StatusObject(const StatusObject& st) = default;
110 
111  StatusObject(const Status& s) : status(s), obj() {}
112  StatusObject(Status&& s) noexcept : status(std::move(s)), obj() {}
113 
114  StatusObject(const T& t) : obj(t) {}
115  StatusObject(T&& t) : obj(std::move(t)) {}
116 
117  StatusObject& operator=(StatusObject&&) noexcept;
118  StatusObject& operator=(const StatusObject&) = default;
119 
120  std::string ToString() const { return status.ToString(); }
121 };
122 
123 template <typename T> StatusObject<T>& StatusObject<T>::operator=(StatusObject<T>&& o) noexcept {
124  status = std::move(o.status);
125  obj = std::move(o.obj);
126  return *this;
127 }
128 
129 namespace detail {
130 
131 // Returns true if status failed.
132 bool StatusFailPrintImpl(const Status& st);
133 
134 void PrintFatal(const char* file, unsigned line_num, const Status& st);
135 
136 template<typename T> T CheckedGet(const char* file, unsigned line_num, StatusObject<T>&& st_obj) {
137  if (!st_obj.ok()) {
138  PrintFatal(file, line_num, st_obj.status);
139  }
140  return std::move(st_obj.obj);
141 }
142 
143 } // namespace detail
144 
145 // some generally useful macros
146 #define RETURN_IF_ERROR(stmt) \
147  do { \
148  auto __status__ = (stmt); \
149  if (UNLIKELY(!__status__.ok())) \
150  return __status__; \
151  } while (false)
152 
153 
154 #define GET_UNLESS_ERROR(var, stmt) \
155  decltype((stmt).obj) var; \
156  do { \
157  auto __res__ = (stmt); \
158  if (UNLIKELY(!__res__.ok())) \
159  return __res__.status; \
160  var = std::move(__res__.obj); \
161  } while (false)
162 
163 } // namespace util
164 
165 #define CHECK_STATUS(stmt) \
166  LOG_IF(FATAL, UNLIKELY(::util::detail::StatusFailPrintImpl(stmt))) << \
167  "status check failed on <" #stmt ">"
168 
169 #define CHECKED_GET(stmt) ::util::detail::CheckedGet(__FILE__, __LINE__, stmt)