blob: ce86c7086c9c1b0563293a640170d45045d11833 [file] [log] [blame]
Jason Henlineac232dd2016-10-25 20:18:56 +00001//===--- status.h - Status and Expected classes -----------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef ACXXEL_STATUS_H
11#define ACXXEL_STATUS_H
12
13#include <cassert>
14#include <string>
15
16// The clang compiler supports annotating class declarations with the
17// warn_unused_result attribute, and this has the meaning that whenever that
18// type is returned from a function, the function is marked as
19// warn_unused_result.
20//
21// The gcc compiler does not support warn_unused_result for classes, only for
22// functions, so we only use this feature with clang.
23#ifdef __clang__
24#define ACXXEL_WARN_UNUSED_RESULT_TYPE __attribute__((warn_unused_result))
25#else
26#define ACXXEL_WARN_UNUSED_RESULT_TYPE
27#endif
28
29namespace acxxel {
30
31/// Status type.
32///
33/// May represent failure with a string error message, or may indicate success.
34class ACXXEL_WARN_UNUSED_RESULT_TYPE Status {
35public:
36 /// Creates a Status representing success.
37 Status() : HasMessage(false) {}
38
39 /// Creates a Status representing failure with the given error message.
40 explicit Status(const std::string &Message)
41 : HasMessage(true), Message(Message) {}
42
43 Status(const Status &) = default;
44
45 Status &operator=(const Status &) = default;
46
47 Status(Status &&) noexcept = default;
48
49 // Cannot use default because the move assignment operator for std::string is
50 // not marked noexcept.
51 Status &operator=(Status &&That) noexcept {
52 HasMessage = That.HasMessage;
53 Message = std::move(That.Message);
54 return *this;
55 }
56
57 ~Status() = default;
58
59 /// Returns true if this Status represents failure. Otherwise, returns false.
60 bool isError() const { return HasMessage; }
61
62 /// Returns true if this Status represents success. Otherwise, returns false.
63 operator bool() const { return !HasMessage; }
64
65 /// Gets a reference to the error message for this Status.
66 ///
67 /// Should only be called if isError() returns true.
68 const std::string &getMessage() const { return Message; }
69
70private:
71 bool HasMessage;
72 std::string Message;
73};
74
75class ExpectedBase {
76protected:
77 enum class State {
78 SUCCESS,
79 FAILURE,
80 MOVED,
81 };
82};
83
84/// Either a value of type T or a Status representing failure.
85template <typename T> class Expected : public ExpectedBase {
86public:
87 /// Creates an Expected representing failure with the given Error status.
88 // Intentionally implicit.
89 Expected(Status AnError)
90 : TheState(State::FAILURE), TheError(std::move(AnError)) {
91 assert(AnError.isError() && "constructing an error Expected value from a "
92 "success status is not allowed");
93 }
94
95 /// Creates an Expected representing success with the given value.
96 // Intentionally implicit.
97 Expected(T Value) : TheState(State::SUCCESS), TheValue(std::move(Value)) {}
98
99 Expected(const Expected &That) : TheState(That.TheState) {
100 switch (TheState) {
101 case State::SUCCESS:
102 new (&TheValue) T(That.TheValue);
103 break;
104 case State::FAILURE:
105 new (&TheError) Status(That.TheError);
106 break;
107 case State::MOVED:
108 // Nothing to do in this case.
109 break;
110 }
111 }
112
113 Expected &operator=(Expected That) {
114 TheState = That.TheState;
115 switch (TheState) {
116 case State::SUCCESS:
117 new (&TheValue) T(std::move(That.TheValue));
118 break;
119 case State::FAILURE:
120 new (&TheError) Status(std::move(That.TheError));
121 break;
122 case State::MOVED:
123 // Nothing to do in this case.
124 break;
125 }
126 return *this;
127 }
128
129 Expected(Expected &&That) noexcept : TheState(That.TheState) {
130 switch (TheState) {
131 case State::SUCCESS:
132 new (&TheValue) T(std::move(That.TheValue));
133 break;
134 case State::FAILURE:
135 new (&TheError) Status(std::move(That.TheError));
136 break;
137 case State::MOVED:
138 // Nothing to do in this case.
139 break;
140 }
141 That.TheState = State::MOVED;
142 }
143
144 template <typename U>
145 Expected(const Expected<U> &That) : TheState(That.TheState) {
146 switch (TheState) {
147 case State::SUCCESS:
148 new (&TheValue) T(That.TheValue);
149 break;
150 case State::FAILURE:
151 new (&TheError) Status(That.TheError);
152 break;
153 case State::MOVED:
154 // Nothing to do in this case.
155 break;
156 }
157 }
158
159 template <typename U> Expected(Expected<U> &&That) : TheState(That.TheState) {
160 switch (TheState) {
161 case State::SUCCESS:
162 new (&TheValue) T(std::move(That.TheValue));
163 break;
164 case State::FAILURE:
165 new (&TheError) Status(std::move(That.TheError));
166 break;
167 case State::MOVED:
168 // Nothing to do in this case.
169 break;
170 }
171 }
172
173 ~Expected() {
174 switch (TheState) {
175 case State::SUCCESS:
176 TheValue.~T();
177 break;
178 case State::FAILURE:
179 TheError.~Status();
180 break;
181 case State::MOVED:
182 // Nothing to do for this case.
183 break;
184 }
185 }
186
187 /// Returns true if this instance represents failure.
188 bool isError() const { return TheState != State::SUCCESS; }
189
190 /// Gets a reference to the Status object.
191 ///
192 /// Should only be called if isError() returns true.
193 const Status &getError() const {
194 assert(isError());
195 return TheError;
196 }
197
198 /// Gets a const reference to the value object.
199 ///
200 /// Should only be called if isError() returns false.
201 const T &getValue() const {
202 assert(!isError());
203 return TheValue;
204 }
205
206 /// Gets a reference to the value object.
207 ///
208 /// Should only be called if isError() returns false.
209 T &getValue() {
210 assert(!isError());
211 return TheValue;
212 }
213
214 /// Takes the value from this object by moving it to the return value.
215 ///
216 /// Should only be called if isError() returns false.
217 T takeValue() {
218 assert(!isError());
219 TheState = State::MOVED;
220 return std::move(TheValue);
221 }
222
223private:
224 template <typename U> friend class Expected;
225
226 State TheState;
227
228 union {
229 T TheValue;
230 Status TheError;
231 };
232};
233
234} // namespace acxxel
235
236#endif // ACXXEL_STATUS_H