Skip to content

Commit

Permalink
✨ Attach error with bad_result_access
Browse files Browse the repository at this point in the history
The exception thrown during access to a `result` object previously
simply contained a generic error message, but did not contain the
error code. This doesn't lend much context to the actual error, which
complicates identification by the user.

The `std::expected` proposal for which this project was once based on
throws an exception containing the `E` type, but derives from an
exception containing the `void` type. This specialization of the
exception is precisely what was being avoided by not doing this in the
first place as part of this implementation.

However, since designsh ave changed, it has become more apparent that it
would be useful to attach the exact error type into the exception for
the few cases that this will be used.
  • Loading branch information
bitwizeshift committed Dec 10, 2020
1 parent 3f5c849 commit 5e7e83c
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 38 deletions.
123 changes: 109 additions & 14 deletions include/result.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ inline namespace bitwizeshift {
template <typename, typename>
class result;

template <typename>
class bad_result_access;

//===========================================================================
Expand Down Expand Up @@ -315,28 +316,52 @@ inline namespace bitwizeshift {
#if !defined(RESULT_DISABLE_EXCEPTIONS)

//===========================================================================
// class : bad_result_access
// class : bad_result_access<E>
//===========================================================================

/////////////////////////////////////////////////////////////////////////////
/// \brief An exception thrown when result::value is accessed without
/// a contained value
/////////////////////////////////////////////////////////////////////////////
template <typename E>
class bad_result_access : public std::logic_error
{
//-------------------------------------------------------------------------
// Constructor / Assignment
//-------------------------------------------------------------------------
public:

bad_result_access();
/// \brief Constructs this exception using the underlying error type for
/// the error type
///
/// \param error the underlying error
template <typename E2,
typename = typename std::enable_if<std::is_constructible<E,E2>::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;

//-------------------------------------------------------------------------

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
Expand Down Expand Up @@ -1370,8 +1395,9 @@ inline namespace bitwizeshift {
template <typename T, typename E>
constexpr auto extract_error(const result<T,E>& exp) noexcept -> const E&;

template <typename E>
[[noreturn]]
auto throw_bad_result_access() -> void;
auto throw_bad_result_access(E&& error) -> void;

} // namespace detail

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -2815,11 +2844,50 @@ namespace std {
// Constructors
//-----------------------------------------------------------------------------

template <typename E>
template <typename E2, typename>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::bad_result_access<E>::bad_result_access(E2&& error)
: logic_error{"error attempting to access value from result containing error"},
m_error(detail::forward<E2>(error))
{

}

//-----------------------------------------------------------------------------
// Observers
//-----------------------------------------------------------------------------

template <typename E>
inline RESULT_INLINE_VISIBILITY
auto RESULT_NS_IMPL::bad_result_access<E>::error()
& noexcept -> E&
{
return m_error;
}

template <typename E>
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<E>::error()
&& noexcept -> E&&
{
return static_cast<E&&>(m_error);
}

template <typename E>
inline RESULT_INLINE_VISIBILITY
auto RESULT_NS_IMPL::bad_result_access<E>::error()
const & noexcept -> const E&
{
return m_error;
}

template <typename E>
inline RESULT_INLINE_VISIBILITY
auto RESULT_NS_IMPL::bad_result_access<E>::error()
const && noexcept -> const E&&
{
return static_cast<const E&&>(m_error);
}

#endif
Expand Down Expand Up @@ -3531,14 +3599,23 @@ auto RESULT_NS_IMPL::detail::extract_error(const result<T,E>& exp) noexcept -> c
return result_error_extractor::get(exp);
}

template <typename E>
[[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<E>::type
>::type
>;

throw exception_type{
detail::forward<E>(error)
};
#endif
}

Expand Down Expand Up @@ -3868,7 +3945,8 @@ inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR
auto RESULT_NS_IMPL::result<T,E>::value()
& -> typename std::add_lvalue_reference<T>::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
);
}
Expand All @@ -3880,7 +3958,8 @@ auto RESULT_NS_IMPL::result<T,E>::value()
{
using reference = typename std::add_rvalue_reference<T>::type;

return (has_value() || (detail::throw_bad_result_access(), true),
return (has_value() ||
(detail::throw_bad_result_access(static_cast<E&&>(m_storage.storage.m_error)), true),
static_cast<reference>(m_storage.storage.m_value)
);
}
Expand All @@ -3890,7 +3969,8 @@ inline RESULT_INLINE_VISIBILITY constexpr
auto RESULT_NS_IMPL::result<T,E>::value()
const & -> typename std::add_lvalue_reference<typename std::add_const<T>::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
);
}
Expand All @@ -3902,7 +3982,8 @@ auto RESULT_NS_IMPL::result<T,E>::value()
{
using reference = typename std::add_rvalue_reference<typename std::add_const<T>::type>::type;

return (has_value() || (detail::throw_bad_result_access(), true),
return (has_value() ||
(detail::throw_bad_result_access(static_cast<const E&&>(m_storage.storage.m_error)), true),
(static_cast<reference>(m_storage.storage.m_value))
);
}
Expand Down Expand Up @@ -4364,9 +4445,23 @@ auto RESULT_NS_IMPL::result<void, E>::has_error()
template <typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR
auto RESULT_NS_IMPL::result<void, E>::value()
const -> void
const & -> void
{
static_cast<void>(has_value() || (detail::throw_bad_result_access(), true));
static_cast<void>(
has_value() ||
(detail::throw_bad_result_access(m_storage.storage.m_error), true)
);
}

template <typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR
auto RESULT_NS_IMPL::result<void, E>::value()
&& -> void
{
static_cast<void>(
has_value() ||
(detail::throw_bad_result_access(static_cast<E&&>(m_storage.storage.m_error)), true)
);
}

template <typename E>
Expand Down
Loading

0 comments on commit 5e7e83c

Please sign in to comment.