diff --git a/include/result.hpp b/include/result.hpp index d46b20b..f9bfa51 100644 --- a/include/result.hpp +++ b/include/result.hpp @@ -279,6 +279,7 @@ inline namespace bitwizeshift { template class result; + template class bad_result_access; //=========================================================================== @@ -315,13 +316,14 @@ inline namespace bitwizeshift { #if !defined(RESULT_DISABLE_EXCEPTIONS) //=========================================================================== - // class : bad_result_access + // class : bad_result_access //=========================================================================== ///////////////////////////////////////////////////////////////////////////// /// \brief An exception thrown when result::value is accessed without /// a contained value ///////////////////////////////////////////////////////////////////////////// + template class bad_result_access : public std::logic_error { //------------------------------------------------------------------------- @@ -329,7 +331,13 @@ inline namespace bitwizeshift { //------------------------------------------------------------------------- public: - bad_result_access(); + /// \brief Constructs this exception using the underlying error type for + /// the error type + /// + /// \param error the underlying error + template ::value>::type> + explicit bad_result_access(E2&& error); bad_result_access(const bad_result_access& other) = default; bad_result_access(bad_result_access&& other) = default; @@ -337,6 +345,23 @@ inline namespace bitwizeshift { auto operator=(const bad_result_access& other) -> bad_result_access& = default; auto operator=(bad_result_access&& other) -> bad_result_access& = default; + + /// \{ + /// \brief Gets the underlying error + /// + /// \return the error + auto error() & noexcept -> E&; + auto error() && noexcept -> E&&; + auto error() const & noexcept -> const E&; + auto error() const && noexcept -> const E&&; + /// \} + + //------------------------------------------------------------------------- + // Private Members + //------------------------------------------------------------------------- + private: + + E m_error; }; #endif @@ -1370,8 +1395,9 @@ inline namespace bitwizeshift { template constexpr auto extract_error(const result& exp) noexcept -> const E&; + template [[noreturn]] - auto throw_bad_result_access() -> void; + auto throw_bad_result_access(E&& error) -> void; } // namespace detail @@ -2452,10 +2478,13 @@ inline namespace bitwizeshift { //------------------------------------------------------------------------- + /// \{ /// \brief Throws an exception if contains an error /// /// \throws bad_result_access if `*this` contains an error. - RESULT_CPP14_CONSTEXPR auto value() const -> void; + RESULT_CPP14_CONSTEXPR auto value() && -> void; + RESULT_CPP14_CONSTEXPR auto value() const & -> void; + /// \} /// \{ /// \brief Returns the contained error, if one exists, or a @@ -2815,11 +2844,50 @@ namespace std { // Constructors //----------------------------------------------------------------------------- +template +template +inline RESULT_INLINE_VISIBILITY +RESULT_NS_IMPL::bad_result_access::bad_result_access(E2&& error) + : logic_error{"error attempting to access value from result containing error"}, + m_error(detail::forward(error)) +{ + +} + +//----------------------------------------------------------------------------- +// Observers +//----------------------------------------------------------------------------- + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::bad_result_access::error() + & noexcept -> E& +{ + return m_error; +} + +template inline RESULT_INLINE_VISIBILITY -RESULT_NS_IMPL::bad_result_access::bad_result_access() - : logic_error{"bad_result_access"} +auto RESULT_NS_IMPL::bad_result_access::error() + && noexcept -> E&& { + return static_cast(m_error); +} +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::bad_result_access::error() + const & noexcept -> const E& +{ + return m_error; +} + +template +inline RESULT_INLINE_VISIBILITY +auto RESULT_NS_IMPL::bad_result_access::error() + const && noexcept -> const E&& +{ + return static_cast(m_error); } #endif @@ -3531,14 +3599,23 @@ auto RESULT_NS_IMPL::detail::extract_error(const result& exp) noexcept -> c return result_error_extractor::get(exp); } +template [[noreturn]] inline RESULT_INLINE_VISIBILITY -auto RESULT_NS_IMPL::detail::throw_bad_result_access() -> void +auto RESULT_NS_IMPL::detail::throw_bad_result_access(E&& error) -> void { #if defined(RESULT_DISABLE_EXCEPTIONS) std::abort(); #else - throw bad_result_access{}; + using exception_type = bad_result_access< + typename std::remove_const< + typename std::remove_reference::type + >::type + >; + + throw exception_type{ + detail::forward(error) + }; #endif } @@ -3868,7 +3945,8 @@ inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::value() & -> typename std::add_lvalue_reference::type { - return (has_value() || (detail::throw_bad_result_access(), false), + return (has_value() || + (detail::throw_bad_result_access(m_storage.storage.m_error), false), m_storage.storage.m_value ); } @@ -3880,7 +3958,8 @@ auto RESULT_NS_IMPL::result::value() { using reference = typename std::add_rvalue_reference::type; - return (has_value() || (detail::throw_bad_result_access(), true), + return (has_value() || + (detail::throw_bad_result_access(static_cast(m_storage.storage.m_error)), true), static_cast(m_storage.storage.m_value) ); } @@ -3890,7 +3969,8 @@ inline RESULT_INLINE_VISIBILITY constexpr auto RESULT_NS_IMPL::result::value() const & -> typename std::add_lvalue_reference::type>::type { - return (has_value() || (detail::throw_bad_result_access(), true), + return (has_value() || + (detail::throw_bad_result_access(m_storage.storage.m_error), true), m_storage.storage.m_value ); } @@ -3902,7 +3982,8 @@ auto RESULT_NS_IMPL::result::value() { using reference = typename std::add_rvalue_reference::type>::type; - return (has_value() || (detail::throw_bad_result_access(), true), + return (has_value() || + (detail::throw_bad_result_access(static_cast(m_storage.storage.m_error)), true), (static_cast(m_storage.storage.m_value)) ); } @@ -4364,9 +4445,23 @@ auto RESULT_NS_IMPL::result::has_error() template inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto RESULT_NS_IMPL::result::value() - const -> void + const & -> void { - static_cast(has_value() || (detail::throw_bad_result_access(), true)); + static_cast( + has_value() || + (detail::throw_bad_result_access(m_storage.storage.m_error), true) + ); +} + +template +inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR +auto RESULT_NS_IMPL::result::value() + && -> void +{ + static_cast( + has_value() || + (detail::throw_bad_result_access(static_cast(m_storage.storage.m_error)), true) + ); } template diff --git a/test/src/result.test.cpp b/test/src/result.test.cpp index c8497d9..90bd076 100644 --- a/test/src/result.test.cpp +++ b/test/src/result.test.cpp @@ -196,7 +196,7 @@ TEST_CASE("result::result()", "[ctor]") { auto sut = sut_type{}; - REQUIRE(sut.value() == value_type()); + REQUIRE(*sut == value_type()); } SECTION("Invokes T's default-construction") { // TODO: Use a static mock @@ -233,7 +233,7 @@ TEST_CASE("result::result(const result&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -273,7 +273,7 @@ TEST_CASE("result::result(const result&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -313,7 +313,7 @@ TEST_CASE("result::result(const result&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -353,7 +353,7 @@ TEST_CASE("result::result(const result&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -420,7 +420,7 @@ TEST_CASE("result::result(result&&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("New result contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -460,7 +460,7 @@ TEST_CASE("result::result(result&&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("New result contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -500,7 +500,7 @@ TEST_CASE("result::result(result&&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("New result contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -540,7 +540,7 @@ TEST_CASE("result::result(result&&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("New result contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -611,7 +611,7 @@ TEST_CASE("result::result(const result&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -687,7 +687,7 @@ TEST_CASE("result::result(const result&) (explicit)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -728,7 +728,7 @@ TEST_CASE("result::result(const result&) (explicit)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -768,7 +768,7 @@ TEST_CASE("result::result(const result&) (explicit)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -844,7 +844,7 @@ TEST_CASE("result::result(result&&)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -920,7 +920,7 @@ TEST_CASE("result::result(result&&) (explicit)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -961,7 +961,7 @@ TEST_CASE("result::result(result&&) (explicit)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -1004,7 +1004,7 @@ TEST_CASE("result::result(result&&) (explicit)", "[ctor]") { REQUIRE(sut.has_value()); } SECTION("Copy contains a value equal to the source") { - REQUIRE(sut.value() == value); + REQUIRE(*sut == value); } } @@ -2250,7 +2250,7 @@ TEST_CASE("result::value() &", "[observers]") { fail(42) }; - REQUIRE_THROWS_AS(sut.value(), bad_result_access); + REQUIRE_THROWS_AS(sut.value(), bad_result_access); } } } @@ -2271,7 +2271,7 @@ TEST_CASE("result::value() const &", "[observers]") { fail(42) }; - REQUIRE_THROWS_AS(sut.value(), bad_result_access); + REQUIRE_THROWS_AS(sut.value(), bad_result_access); } } } @@ -2288,11 +2288,11 @@ TEST_CASE("result::value() &&", "[observers]") { } SECTION("result contains an error") { SECTION("throws bad_result_access") { - auto sut = result{ - fail(42) + auto sut = result>{ + fail("hello world") }; - REQUIRE_THROWS_AS(std::move(sut).value(), bad_result_access); + REQUIRE_THROWS_AS(std::move(sut).value(), bad_result_access>); } } } @@ -2313,7 +2313,7 @@ TEST_CASE("result::value() const &&", "[observers]") { fail(42) }; - REQUIRE_THROWS_AS(std::move(sut).value(), bad_result_access); + REQUIRE_THROWS_AS(std::move(sut).value(), bad_result_access); } } } @@ -4027,7 +4027,7 @@ TEST_CASE("result::value()", "[observers]") { SECTION("Throws bad_result_access") { auto sut = result{fail(42)}; - REQUIRE_THROWS_AS(sut.value(), bad_result_access); + REQUIRE_THROWS_AS(sut.value(), bad_result_access); } } }