diff options
| author | 2021-11-01 10:09:37 -0400 | |
|---|---|---|
| committer | 2021-11-02 15:20:35 -0400 | |
| commit | 52e52924bb00723b6fd4536419cbbae39ee86b35 (patch) | |
| tree | 8eb8359bf82adde70386c06e536a8b3f2454fa96 /src/core/hle/result.h | |
| parent | common: Implement a subset of P0323 (std::expected) (diff) | |
| download | yuzu-52e52924bb00723b6fd4536419cbbae39ee86b35.tar.gz yuzu-52e52924bb00723b6fd4536419cbbae39ee86b35.tar.xz yuzu-52e52924bb00723b6fd4536419cbbae39ee86b35.zip | |
hle/result: Reimplement ResultVal using Common::Expected
Common::Expected effectively provides the same functions as ResultVal, so we can implement it with this.
This can be replaced with std::expected with minimal effort should it be standardized in the C++ Standard Template Library.
Diffstat (limited to 'src/core/hle/result.h')
| -rw-r--r-- | src/core/hle/result.h | 180 |
1 files changed, 63 insertions, 117 deletions
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 2c6b24848..29fa4eed2 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 | ||
| @@ -189,150 +188,97 @@ constexpr ResultCode ResultUnknown(UINT32_MAX); | |||
| 189 | template <typename T> | 188 | template <typename T> |
| 190 | class ResultVal { | 189 | class ResultVal { |
| 191 | public: | 190 | public: |
| 192 | /// Constructs an empty `ResultVal` with the given error code. The code must not be a success | 191 | constexpr ResultVal() : expected{} {} |
| 193 | /// code. | 192 | |
| 194 | ResultVal(ResultCode error_code = ResultUnknown) : result_code(error_code) { | 193 | constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {} |
| 195 | ASSERT(error_code.IsError()); | 194 | |
| 196 | } | 195 | template <typename U> |
| 196 | constexpr ResultVal(U&& val) : expected{std::forward<U>(val)} {} | ||
| 197 | 197 | ||
| 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> | 198 | template <typename... Args> |
| 203 | [[nodiscard]] static ResultVal WithCode(ResultCode success_code, Args&&... args) { | 199 | 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 | 200 | ||
| 209 | ResultVal(const ResultVal& o) noexcept : result_code(o.result_code) { | 201 | ~ResultVal() = default; |
| 210 | if (!o.empty()) { | ||
| 211 | new (&object) T(o.object); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | 202 | ||
| 215 | ResultVal(ResultVal&& o) noexcept : result_code(o.result_code) { | 203 | constexpr ResultVal(const ResultVal&) = default; |
| 216 | if (!o.empty()) { | 204 | constexpr ResultVal(ResultVal&&) = default; |
| 217 | new (&object) T(std::move(o.object)); | ||
| 218 | } | ||
| 219 | } | ||
| 220 | 205 | ||
| 221 | ~ResultVal() { | 206 | ResultVal& operator=(const ResultVal&) = default; |
| 222 | if (!empty()) { | 207 | ResultVal& operator=(ResultVal&&) = default; |
| 223 | object.~T(); | ||
| 224 | } | ||
| 225 | } | ||
| 226 | 208 | ||
| 227 | ResultVal& operator=(const ResultVal& o) noexcept { | 209 | [[nodiscard]] constexpr explicit operator bool() const noexcept { |
| 228 | if (this == &o) { | 210 | 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 | } | 211 | } |
| 246 | 212 | ||
| 247 | ResultVal& operator=(ResultVal&& o) noexcept { | 213 | [[nodiscard]] constexpr ResultCode Code() const { |
| 248 | if (this == &o) { | 214 | 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 | } | 215 | } |
| 266 | 216 | ||
| 267 | /** | 217 | [[nodiscard]] constexpr bool Succeeded() const { |
| 268 | * Replaces the current result with a new constructed result value in-place. The code must not | 218 | 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 | } | 219 | } |
| 280 | 220 | ||
| 281 | /// Returns true if the `ResultVal` contains an error code and no value. | 221 | [[nodiscard]] constexpr bool Failed() const { |
| 282 | [[nodiscard]] bool empty() const { | 222 | return !expected.has_value(); |
| 283 | return result_code.IsError(); | ||
| 284 | } | 223 | } |
| 285 | 224 | ||
| 286 | /// Returns true if the `ResultVal` contains a return value. | 225 | [[nodiscard]] constexpr T* operator->() { |
| 287 | [[nodiscard]] bool Succeeded() const { | 226 | return std::addressof(expected.value()); |
| 288 | return result_code.IsSuccess(); | ||
| 289 | } | 227 | } |
| 290 | /// Returns true if the `ResultVal` contains an error code and no value. | 228 | |
| 291 | [[nodiscard]] bool Failed() const { | 229 | [[nodiscard]] constexpr const T* operator->() const { |
| 292 | return empty(); | 230 | return std::addressof(expected.value()); |
| 293 | } | 231 | } |
| 294 | 232 | ||
| 295 | [[nodiscard]] ResultCode Code() const { | 233 | [[nodiscard]] constexpr T& operator*() & { |
| 296 | return result_code; | 234 | return *expected; |
| 297 | } | 235 | } |
| 298 | 236 | ||
| 299 | [[nodiscard]] const T& operator*() const { | 237 | [[nodiscard]] constexpr const T& operator*() const& { |
| 300 | return object; | 238 | return *expected; |
| 301 | } | 239 | } |
| 302 | [[nodiscard]] T& operator*() { | 240 | |
| 303 | return object; | 241 | [[nodiscard]] constexpr T&& operator*() && { |
| 242 | return *expected; | ||
| 304 | } | 243 | } |
| 305 | [[nodiscard]] const T* operator->() const { | 244 | |
| 306 | return &object; | 245 | [[nodiscard]] constexpr const T&& operator*() const&& { |
| 246 | return *expected; | ||
| 307 | } | 247 | } |
| 308 | [[nodiscard]] T* operator->() { | 248 | |
| 309 | return &object; | 249 | [[nodiscard]] constexpr T& Unwrap() & { |
| 250 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | ||
| 251 | return expected.value(); | ||
| 310 | } | 252 | } |
| 311 | 253 | ||
| 312 | /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing. | 254 | [[nodiscard]] constexpr const T& Unwrap() const& { |
| 313 | template <typename U> | 255 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); |
| 314 | [[nodiscard]] T ValueOr(U&& value) const { | 256 | return expected.value(); |
| 315 | return !empty() ? object : std::move(value); | ||
| 316 | } | 257 | } |
| 317 | 258 | ||
| 318 | /// Asserts that the result succeeded and returns a reference to it. | 259 | [[nodiscard]] constexpr T&& Unwrap() && { |
| 319 | [[nodiscard]] T& Unwrap() & { | ||
| 320 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | 260 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); |
| 321 | return **this; | 261 | return std::move(expected.value()); |
| 322 | } | 262 | } |
| 323 | 263 | ||
| 324 | [[nodiscard]] T&& Unwrap() && { | 264 | [[nodiscard]] constexpr const T&& Unwrap() const&& { |
| 325 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | 265 | ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); |
| 326 | return std::move(**this); | 266 | return std::move(expected.value()); |
| 267 | } | ||
| 268 | |||
| 269 | template <typename U> | ||
| 270 | [[nodiscard]] constexpr T ValueOr(U&& v) const& { | ||
| 271 | return expected.value_or(v); | ||
| 272 | } | ||
| 273 | |||
| 274 | template <typename U> | ||
| 275 | [[nodiscard]] constexpr T ValueOr(U&& v) && { | ||
| 276 | return expected.value_or(v); | ||
| 327 | } | 277 | } |
| 328 | 278 | ||
| 329 | private: | 279 | private: |
| 330 | // A union is used to allocate the storage for the value, while allowing us to construct and | 280 | // TODO: Replace this with std::expected once it is standardized in the STL. |
| 331 | // destruct it at will. | 281 | Common::Expected<T, ResultCode> expected; |
| 332 | union { | ||
| 333 | T object; | ||
| 334 | }; | ||
| 335 | ResultCode result_code; | ||
| 336 | }; | 282 | }; |
| 337 | 283 | ||
| 338 | /** | 284 | /** |
| @@ -341,16 +287,16 @@ private: | |||
| 341 | */ | 287 | */ |
| 342 | template <typename T, typename... Args> | 288 | template <typename T, typename... Args> |
| 343 | [[nodiscard]] ResultVal<T> MakeResult(Args&&... args) { | 289 | [[nodiscard]] ResultVal<T> MakeResult(Args&&... args) { |
| 344 | return ResultVal<T>::WithCode(ResultSuccess, std::forward<Args>(args)...); | 290 | return ResultVal<T>{std::forward<Args>(args)...}; |
| 345 | } | 291 | } |
| 346 | 292 | ||
| 347 | /** | 293 | /** |
| 348 | * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just | 294 | * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just |
| 349 | * copy or move constructing. | 295 | * copy or move constructing. |
| 350 | */ | 296 | */ |
| 351 | template <typename Arg> | 297 | template <typename T> |
| 352 | [[nodiscard]] ResultVal<std::remove_cvref_t<Arg>> MakeResult(Arg&& arg) { | 298 | [[nodiscard]] ResultVal<std::remove_cvref_t<T>> MakeResult(T&& val) { |
| 353 | return ResultVal<std::remove_cvref_t<Arg>>::WithCode(ResultSuccess, std::forward<Arg>(arg)); | 299 | return ResultVal<std::remove_cvref_t<T>>{std::forward<T>(val)}; |
| 354 | } | 300 | } |
| 355 | 301 | ||
| 356 | /** | 302 | /** |