From 52e52924bb00723b6fd4536419cbbae39ee86b35 Mon Sep 17 00:00:00 2001 From: Morph Date: Mon, 1 Nov 2021 10:09:37 -0400 Subject: 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. --- src/core/hle/result.h | 180 ++++++++++++++++++-------------------------------- 1 file changed, 63 insertions(+), 117 deletions(-) (limited to 'src/core/hle/result.h') 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 @@ #pragma once -#include -#include #include "common/assert.h" #include "common/bit_field.h" #include "common/common_types.h" +#include "common/expected.h" // All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes @@ -189,150 +188,97 @@ constexpr ResultCode ResultUnknown(UINT32_MAX); template class ResultVal { public: - /// Constructs an empty `ResultVal` with the given error code. The code must not be a success - /// code. - ResultVal(ResultCode error_code = ResultUnknown) : result_code(error_code) { - ASSERT(error_code.IsError()); - } + constexpr ResultVal() : expected{} {} + + constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {} + + template + constexpr ResultVal(U&& val) : expected{std::forward(val)} {} - /** - * Similar to the non-member function `MakeResult`, with the exception that you can manually - * specify the success code. `success_code` must not be an error code. - */ template - [[nodiscard]] static ResultVal WithCode(ResultCode success_code, Args&&... args) { - ResultVal result; - result.emplace(success_code, std::forward(args)...); - return result; - } + constexpr ResultVal(Args&&... args) : expected{std::in_place, std::forward(args)...} {} - ResultVal(const ResultVal& o) noexcept : result_code(o.result_code) { - if (!o.empty()) { - new (&object) T(o.object); - } - } + ~ResultVal() = default; - ResultVal(ResultVal&& o) noexcept : result_code(o.result_code) { - if (!o.empty()) { - new (&object) T(std::move(o.object)); - } - } + constexpr ResultVal(const ResultVal&) = default; + constexpr ResultVal(ResultVal&&) = default; - ~ResultVal() { - if (!empty()) { - object.~T(); - } - } + ResultVal& operator=(const ResultVal&) = default; + ResultVal& operator=(ResultVal&&) = default; - ResultVal& operator=(const ResultVal& o) noexcept { - if (this == &o) { - return *this; - } - if (!empty()) { - if (!o.empty()) { - object = o.object; - } else { - object.~T(); - } - } else { - if (!o.empty()) { - new (&object) T(o.object); - } - } - result_code = o.result_code; - - return *this; + [[nodiscard]] constexpr explicit operator bool() const noexcept { + return expected.has_value(); } - ResultVal& operator=(ResultVal&& o) noexcept { - if (this == &o) { - return *this; - } - if (!empty()) { - if (!o.empty()) { - object = std::move(o.object); - } else { - object.~T(); - } - } else { - if (!o.empty()) { - new (&object) T(std::move(o.object)); - } - } - result_code = o.result_code; - - return *this; + [[nodiscard]] constexpr ResultCode Code() const { + return expected.has_value() ? ResultSuccess : expected.error(); } - /** - * Replaces the current result with a new constructed result value in-place. The code must not - * be an error code. - */ - template - void emplace(ResultCode success_code, Args&&... args) { - ASSERT(success_code.IsSuccess()); - if (!empty()) { - object.~T(); - } - new (&object) T(std::forward(args)...); - result_code = success_code; + [[nodiscard]] constexpr bool Succeeded() const { + return expected.has_value(); } - /// Returns true if the `ResultVal` contains an error code and no value. - [[nodiscard]] bool empty() const { - return result_code.IsError(); + [[nodiscard]] constexpr bool Failed() const { + return !expected.has_value(); } - /// Returns true if the `ResultVal` contains a return value. - [[nodiscard]] bool Succeeded() const { - return result_code.IsSuccess(); + [[nodiscard]] constexpr T* operator->() { + return std::addressof(expected.value()); } - /// Returns true if the `ResultVal` contains an error code and no value. - [[nodiscard]] bool Failed() const { - return empty(); + + [[nodiscard]] constexpr const T* operator->() const { + return std::addressof(expected.value()); } - [[nodiscard]] ResultCode Code() const { - return result_code; + [[nodiscard]] constexpr T& operator*() & { + return *expected; } - [[nodiscard]] const T& operator*() const { - return object; + [[nodiscard]] constexpr const T& operator*() const& { + return *expected; } - [[nodiscard]] T& operator*() { - return object; + + [[nodiscard]] constexpr T&& operator*() && { + return *expected; } - [[nodiscard]] const T* operator->() const { - return &object; + + [[nodiscard]] constexpr const T&& operator*() const&& { + return *expected; } - [[nodiscard]] T* operator->() { - return &object; + + [[nodiscard]] constexpr T& Unwrap() & { + ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); + return expected.value(); } - /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing. - template - [[nodiscard]] T ValueOr(U&& value) const { - return !empty() ? object : std::move(value); + [[nodiscard]] constexpr const T& Unwrap() const& { + ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); + return expected.value(); } - /// Asserts that the result succeeded and returns a reference to it. - [[nodiscard]] T& Unwrap() & { + [[nodiscard]] constexpr T&& Unwrap() && { ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); - return **this; + return std::move(expected.value()); } - [[nodiscard]] T&& Unwrap() && { + [[nodiscard]] constexpr const T&& Unwrap() const&& { ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); - return std::move(**this); + return std::move(expected.value()); + } + + template + [[nodiscard]] constexpr T ValueOr(U&& v) const& { + return expected.value_or(v); + } + + template + [[nodiscard]] constexpr T ValueOr(U&& v) && { + return expected.value_or(v); } private: - // A union is used to allocate the storage for the value, while allowing us to construct and - // destruct it at will. - union { - T object; - }; - ResultCode result_code; + // TODO: Replace this with std::expected once it is standardized in the STL. + Common::Expected expected; }; /** @@ -341,16 +287,16 @@ private: */ template [[nodiscard]] ResultVal MakeResult(Args&&... args) { - return ResultVal::WithCode(ResultSuccess, std::forward(args)...); + return ResultVal{std::forward(args)...}; } /** * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just * copy or move constructing. */ -template -[[nodiscard]] ResultVal> MakeResult(Arg&& arg) { - return ResultVal>::WithCode(ResultSuccess, std::forward(arg)); +template +[[nodiscard]] ResultVal> MakeResult(T&& val) { + return ResultVal>{std::forward(val)}; } /** -- cgit v1.2.3 From 98b351758c9912735ad0cedd0ab68c39185e6040 Mon Sep 17 00:00:00 2001 From: Morph Date: Mon, 1 Nov 2021 10:38:11 -0400 Subject: hle/result: Amend ResultVal documentation This amends the documentation slightly to reflect the updated interface. --- src/core/hle/result.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'src/core/hle/result.h') diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 29fa4eed2..a1917d32b 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -154,34 +154,32 @@ constexpr ResultCode ResultSuccess(0); constexpr ResultCode ResultUnknown(UINT32_MAX); /** - * This is an optional value type. It holds a `ResultCode` and, if that code is a success code, - * also holds a result of type `T`. If the code is an error code then trying to access the inner - * value fails, thus ensuring that the ResultCode of functions is always checked properly before - * their return value is used. It is similar in concept to the `std::optional` type - * (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in - * C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html). + * This is an optional value type. It holds a `ResultCode` and, if that code is ResultSuccess, it + * also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying + * to access the inner value with operator* is undefined behavior and will assert with Unwrap(). + * Users of this class must be cognizant to check the status of the ResultVal with operator bool(), + * Code(), Succeeded() or Failed() prior to accessing the inner value. * * An example of how it could be used: * \code * ResultVal Frobnicate(float strength) { * if (strength < 0.f || strength > 1.0f) { * // Can't frobnicate too weakly or too strongly - * return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common, - * ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + * return ResultCode{ErrorModule::Common, 1}; * } else { * // Frobnicated! Give caller a cookie - * return MakeResult(42); + * return MakeResult(42); * } * } * \endcode * * \code - * ResultVal frob_result = Frobnicate(0.75f); + * auto frob_result = Frobnicate(0.75f); * if (frob_result) { * // Frobbed ok * printf("My cookie is %d\n", *frob_result); * } else { - * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex); + * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.Code().raw); * } * \endcode */ @@ -283,7 +281,7 @@ private: /** * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct - * `T` with and creates a success `ResultVal` contained the constructed value. + * `T` with and creates a `ResultVal` that contains the constructed value. */ template [[nodiscard]] ResultVal MakeResult(Args&&... args) { -- cgit v1.2.3 From cb09ea0f0174162a85f47fdb8446b397c3c57e20 Mon Sep 17 00:00:00 2001 From: Morph Date: Tue, 2 Nov 2021 17:23:19 -0400 Subject: general: Remove MakeResult helpers This is made obsolete by the presence of implicit constructors. --- src/core/hle/result.h | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'src/core/hle/result.h') diff --git a/src/core/hle/result.h b/src/core/hle/result.h index a1917d32b..3807b9aa8 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -168,7 +168,7 @@ constexpr ResultCode ResultUnknown(UINT32_MAX); * return ResultCode{ErrorModule::Common, 1}; * } else { * // Frobnicated! Give caller a cookie - * return MakeResult(42); + * return 42; * } * } * \endcode @@ -279,24 +279,6 @@ private: Common::Expected expected; }; -/** - * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct - * `T` with and creates a `ResultVal` that contains the constructed value. - */ -template -[[nodiscard]] ResultVal MakeResult(Args&&... args) { - return ResultVal{std::forward(args)...}; -} - -/** - * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just - * copy or move constructing. - */ -template -[[nodiscard]] ResultVal> MakeResult(T&& val) { - return ResultVal>{std::forward(val)}; -} - /** * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps * the contained value and assigns it to `target`, which can be either an l-value expression or a -- cgit v1.2.3