diff options
| author | 2021-11-02 18:41:57 -0400 | |
|---|---|---|
| committer | 2021-11-02 18:41:57 -0400 | |
| commit | 3d1f2bb3aa2e5bced4ab2342b5e591224f09f0ba (patch) | |
| tree | 5cbd9a62e1daf1c1de97444858064fe8210df077 /src/core/hle/result.h | |
| parent | Merge pull request #7227 from vonchenplus/fix_memory_leak_v2 (diff) | |
| parent | general: Remove MakeResult helpers (diff) | |
| download | yuzu-3d1f2bb3aa2e5bced4ab2342b5e591224f09f0ba.tar.gz yuzu-3d1f2bb3aa2e5bced4ab2342b5e591224f09f0ba.tar.xz yuzu-3d1f2bb3aa2e5bced4ab2342b5e591224f09f0ba.zip | |
Merge pull request #7268 from Morph1984/expected-resultval
common, result: Implement a subset of std::expected and use it in ResultVal
Diffstat (limited to 'src/core/hle/result.h')
| -rw-r--r-- | src/core/hle/result.h | 210 |
1 files changed, 68 insertions, 142 deletions
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 2c6b24848..3807b9aa8 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -4,11 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <new> | ||
| 8 | #include <utility> | ||
| 9 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 10 | #include "common/bit_field.h" | 8 | #include "common/bit_field.h" |
| 11 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/expected.h" | ||
| 12 | 11 | ||
| 13 | // All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes | 12 | // All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes |
| 14 | 13 | ||
| @@ -155,203 +154,130 @@ constexpr ResultCode ResultSuccess(0); | |||
| 155 | constexpr ResultCode ResultUnknown(UINT32_MAX); | 154 | constexpr ResultCode ResultUnknown(UINT32_MAX); |
| 156 | 155 | ||
| 157 | /** | 156 | /** |
| 158 | * This is an optional value type. It holds a `ResultCode` and, if that code is a success code, | 157 | * This is an optional value type. It holds a `ResultCode` and, if that code is ResultSuccess, it |
| 159 | * also holds a result of type `T`. If the code is an error code then trying to access the inner | 158 | * also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying |
| 160 | * value fails, thus ensuring that the ResultCode of functions is always checked properly before | 159 | * to access the inner value with operator* is undefined behavior and will assert with Unwrap(). |
| 161 | * their return value is used. It is similar in concept to the `std::optional` type | 160 | * Users of this class must be cognizant to check the status of the ResultVal with operator bool(), |
| 162 | * (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in | 161 | * Code(), Succeeded() or Failed() prior to accessing the inner value. |
| 163 | * C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html). | ||
| 164 | * | 162 | * |
| 165 | * An example of how it could be used: | 163 | * An example of how it could be used: |
| 166 | * \code | 164 | * \code |
| 167 | * ResultVal<int> Frobnicate(float strength) { | 165 | * ResultVal<int> Frobnicate(float strength) { |
| 168 | * if (strength < 0.f || strength > 1.0f) { | 166 | * if (strength < 0.f || strength > 1.0f) { |
| 169 | * // Can't frobnicate too weakly or too strongly | 167 | * // Can't frobnicate too weakly or too strongly |
| 170 | * return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common, | 168 | * return ResultCode{ErrorModule::Common, 1}; |
| 171 | * ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 172 | * } else { | 169 | * } else { |
| 173 | * // Frobnicated! Give caller a cookie | 170 | * // Frobnicated! Give caller a cookie |
| 174 | * return MakeResult<int>(42); | 171 | * return 42; |
| 175 | * } | 172 | * } |
| 176 | * } | 173 | * } |
| 177 | * \endcode | 174 | * \endcode |
| 178 | * | 175 | * |
| 179 | * \code | 176 | * \code |
| 180 | * ResultVal<int> frob_result = Frobnicate(0.75f); | 177 | * auto frob_result = Frobnicate(0.75f); |
| 181 | * if (frob_result) { | 178 | * if (frob_result) { |
| 182 | * // Frobbed ok | 179 | * // Frobbed ok |
| 183 | * printf("My cookie is %d\n", *frob_result); | 180 | * printf("My cookie is %d\n", *frob_result); |
| 184 | * } else { | 181 | * } else { |
| 185 | * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex); | 182 | * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.Code().raw); |
| 186 | * } | 183 | * } |
| 187 | * \endcode | 184 | * \endcode |
| 188 | */ | 185 | */ |
| 189 | template <typename T> | 186 | template <typename T> |
| 190 | class ResultVal { | 187 | class ResultVal { |
| 191 | public: | 188 | public: |
| 192 | /// Constructs an empty `ResultVal` with the given error code. The code must not be a success | 189 | constexpr ResultVal() : expected{} {} |
| 193 | /// code. | 190 | |
| 194 | ResultVal(ResultCode error_code = ResultUnknown) : result_code(error_code) { | 191 | constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {} |
| 195 | ASSERT(error_code.IsError()); | 192 | |
| 196 | } | 193 | template <typename U> |
| 194 | constexpr ResultVal(U&& val) : expected{std::forward<U>(val)} {} | ||
| 197 | 195 | ||
| 198 | /** | ||
| 199 | * Similar to the non-member function `MakeResult`, with the exception that you can manually | ||
| 200 | * specify the success code. `success_code` must not be an error code. | ||
| 201 | */ | ||
| 202 | template <typename... Args> | 196 | template <typename... Args> |
| 203 | [[nodiscard]] static ResultVal WithCode(ResultCode success_code, Args&&... args) { | 197 | constexpr ResultVal(Args&&... args) : expected{std::in_place, std::forward<Args>(args)...} {} |
| 204 | ResultVal<T> result; | ||
| 205 | result.emplace(success_code, std::forward<Args>(args)...); | ||
| 206 | return result; | ||
| 207 | } | ||
| 208 | 198 | ||
| 209 | ResultVal(const ResultVal& o) noexcept : result_code(o.result_code) { | 199 | ~ResultVal() = default; |
| 210 | if (!o.empty()) { | ||
| 211 | new (&object) T(o.object); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | 200 | ||
| 215 | ResultVal(ResultVal&& o) noexcept : result_code(o.result_code) { | 201 | constexpr ResultVal(const ResultVal&) = default; |
| 216 | if (!o.empty()) { | 202 | constexpr ResultVal(ResultVal&&) = default; |
| 217 | new (&object) T(std::move(o.object)); | ||
| 218 | } | ||
| 219 | } | ||
| 220 | 203 | ||
| 221 | ~ResultVal() { | 204 | ResultVal& operator=(const ResultVal&) = default; |
| 222 | if (!empty()) { | 205 | ResultVal& operator=(ResultVal&&) = default; |
| 223 | object.~T(); | ||
| 224 | } | ||
| 225 | } | ||
| 226 | 206 | ||
| 227 | ResultVal& operator=(const ResultVal& o) noexcept { | 207 | [[nodiscard]] constexpr explicit operator bool() const noexcept { |
| 228 | if (this == &o) { | 208 | return expected.has_value(); |
| 229 | return *this; | ||
| 230 | } | ||
| 231 | if (!empty()) { | ||
| 232 | if (!o.empty()) { | ||
| 233 | object = o.object; | ||
| 234 | } else { | ||
| 235 | object.~T(); | ||
| 236 | } | ||
| 237 | } else { | ||
| 238 | if (!o.empty()) { | ||
| 239 | new (&object) T(o.object); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | result_code = o.result_code; | ||
| 243 | |||
| 244 | return *this; | ||
| 245 | } | 209 | } |
| 246 | 210 | ||
| 247 | ResultVal& operator=(ResultVal&& o) noexcept { | 211 | [[nodiscard]] constexpr ResultCode Code() const { |
| 248 | if (this == &o) { | 212 | return expected.has_value() ? ResultSuccess : expected.error(); |
| 249 | return *this; | ||
| 250 | } | ||
| 251 | if (!empty()) { | ||
| 252 | if (!o.empty()) { | ||
| 253 | object = std::move(o.object); | ||
| 254 | } else { | ||
| 255 | object.~T(); | ||
| 256 | } | ||
| 257 | } else { | ||
| 258 | if (!o.empty()) { | ||
| 259 | new (&object) T(std::move(o.object)); | ||
| 260 | } | ||
| 261 | } | ||
| 262 | result_code = o.result_code; | ||
| 263 | |||
| 264 | return *this; | ||
| 265 | } | 213 | } |
| 266 | 214 | ||
| 267 | /** | 215 | [[nodiscard]] constexpr bool Succeeded() const { |
| 268 | * Replaces the current result with a new constructed result value in-place. The code must not | 216 | return expected.has_value(); |
| 269 | * be an error code. | ||
| 270 | */ | ||
| 271 | template <typename... Args> | ||
| 272 | void emplace(ResultCode success_code, Args&&... args) { | ||
| 273 | ASSERT(success_code.IsSuccess()); | ||
| 274 | if (!empty()) { | ||
| 275 | object.~T(); | ||
| 276 | } | ||
| 277 | new (&object) T(std::forward<Args>(args)...); | ||
| 278 | result_code = success_code; | ||
| 279 | } | 217 | } |
| 280 | 218 | ||
| 281 | /// Returns true if the `ResultVal` contains an error code and no value. | 219 | [[nodiscard]] constexpr bool Failed() const { |
| 282 | [[nodiscard]] bool empty() const { | 220 | return !expected.has_value(); |
| 283 | return result_code.IsError(); | ||
| 284 | } | 221 | } |
| 285 | 222 | ||
| 286 | /// Returns true if the `ResultVal` contains a return value. | 223 | [[nodiscard]] constexpr T* operator->() { |
| 287 | [[nodiscard]] bool Succeeded() const { | 224 | return std::addressof(expected.value()); |
| 288 | return result_code.IsSuccess(); | ||
| 289 | } | 225 | } |
| 290 | /// Returns true if the `ResultVal` contains an error code and no value. | 226 | |
| 291 | [[nodiscard]] bool Failed() const { | 227 | [[nodiscard]] constexpr const T* operator->() const { |
| 292 | return empty(); | 228 | return std::addressof(expected.value()); |
| 293 | } | 229 | } |
| 294 | 230 | ||
| 295 | [[nodiscard]] ResultCode Code() const { | 231 | [[nodiscard]] constexpr T& operator*() & { |
| 296 | return result_code; | 232 | return *expected; |
| 297 | } | 233 | } |
| 298 | 234 | ||
| 299 | [[nodiscard]] const T& operator*() const { | 235 | [[nodiscard]] constexpr const T& operator*() const& { |
| 300 | return object; | 236 | return *expected; |
| 301 | } | 237 | } |
| 302 | [[nodiscard]] T& operator*() { | 238 | |
| 303 | return object; | 239 | [[nodiscard]] constexpr T&& operator*() && { |
| 240 | return *expected; | ||
| 304 | } | 241 | } |
| 305 | [[nodiscard]] const T* operator->() const { | 242 | |
| 306 | return &object; | 243 | [[nodiscard]] constexpr const T&& operator*() const&& { |
| 244 | return *expected; | ||
| 307 | } | 245 | } |
| 308 | [[nodiscard]] T* operator->() { | 246 | |
| 309 | return &object; | 247 | [[nodiscard]] constexpr T& Unwrap() & { |
| 248 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | ||
| 249 | return expected.value(); | ||
| 310 | } | 250 | } |
| 311 | 251 | ||
| 312 | /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing. | 252 | [[nodiscard]] constexpr const T& Unwrap() const& { |
| 313 | template <typename U> | 253 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); |
| 314 | [[nodiscard]] T ValueOr(U&& value) const { | 254 | return expected.value(); |
| 315 | return !empty() ? object : std::move(value); | ||
| 316 | } | 255 | } |
| 317 | 256 | ||
| 318 | /// Asserts that the result succeeded and returns a reference to it. | 257 | [[nodiscard]] constexpr T&& Unwrap() && { |
| 319 | [[nodiscard]] T& Unwrap() & { | ||
| 320 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | 258 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); |
| 321 | return **this; | 259 | return std::move(expected.value()); |
| 322 | } | 260 | } |
| 323 | 261 | ||
| 324 | [[nodiscard]] T&& Unwrap() && { | 262 | [[nodiscard]] constexpr const T&& Unwrap() const&& { |
| 325 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | 263 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); |
| 326 | return std::move(**this); | 264 | return std::move(expected.value()); |
| 327 | } | 265 | } |
| 328 | 266 | ||
| 329 | private: | 267 | template <typename U> |
| 330 | // A union is used to allocate the storage for the value, while allowing us to construct and | 268 | [[nodiscard]] constexpr T ValueOr(U&& v) const& { |
| 331 | // destruct it at will. | 269 | return expected.value_or(v); |
| 332 | union { | 270 | } |
| 333 | T object; | ||
| 334 | }; | ||
| 335 | ResultCode result_code; | ||
| 336 | }; | ||
| 337 | 271 | ||
| 338 | /** | 272 | template <typename U> |
| 339 | * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct | 273 | [[nodiscard]] constexpr T ValueOr(U&& v) && { |
| 340 | * `T` with and creates a success `ResultVal` contained the constructed value. | 274 | return expected.value_or(v); |
| 341 | */ | 275 | } |
| 342 | template <typename T, typename... Args> | ||
| 343 | [[nodiscard]] ResultVal<T> MakeResult(Args&&... args) { | ||
| 344 | return ResultVal<T>::WithCode(ResultSuccess, std::forward<Args>(args)...); | ||
| 345 | } | ||
| 346 | 276 | ||
| 347 | /** | 277 | private: |
| 348 | * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just | 278 | // TODO: Replace this with std::expected once it is standardized in the STL. |
| 349 | * copy or move constructing. | 279 | Common::Expected<T, ResultCode> expected; |
| 350 | */ | 280 | }; |
| 351 | template <typename Arg> | ||
| 352 | [[nodiscard]] ResultVal<std::remove_cvref_t<Arg>> MakeResult(Arg&& arg) { | ||
| 353 | return ResultVal<std::remove_cvref_t<Arg>>::WithCode(ResultSuccess, std::forward<Arg>(arg)); | ||
| 354 | } | ||
| 355 | 281 | ||
| 356 | /** | 282 | /** |
| 357 | * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps | 283 | * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps |