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