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