Skip to content

Commit

Permalink
Add support for result<void, E>
Browse files Browse the repository at this point in the history
This is distinct from option<E> in that the value is clearly an error.
  • Loading branch information
jeaye committed Sep 22, 2023
1 parent 0a0b582 commit 70a3b84
Showing 1 changed file with 76 additions and 0 deletions.
76 changes: 76 additions & 0 deletions include/cpp/jank/result.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ namespace jank

T data;
};

template <bool Ok>
struct result<Ok, void>
{ };
}

inline detail::result<true, void> ok()
{ return {}; }
template <typename R, typename Decayed = std::decay_t<R>>
detail::result<true, Decayed> ok(R &&data)
{ return { std::forward<R>(data) }; }
Expand Down Expand Up @@ -135,6 +141,76 @@ namespace jank
boost::variant<R, E> data;
};

struct void_t
{ };

/* result<void, E> is a special case which doesn't store data for the success case.
* It still uses a variant, but with a special void_t type which does nothing. The normal
* "ok" functions for extracting data are gone.
*
* This is favorable over an option<E> since result<R, E> is clearly used for error handling. */
template <typename E>
struct result<void, E>
{
result(detail::result<true, void> &&) : data{ void_t{} }
{}
result(detail::result<false, E> &&e) : data{ std::move(e.data) }
{}
/* Allow implicit construction of E from its ctor args. */
template <typename T>
result(T &&t, std::enable_if_t<std::is_constructible_v<E, T>>* = nullptr)
: data{ std::forward<T>(t) }
{}
/* Allow implicit construction from results with compatible constructor args. This allows
* things like ok(none) for option<R>. */
template <typename T>
result(detail::result<false, T> const &t, std::enable_if_t<std::is_constructible_v<E, T>>* = nullptr)
: data{ t.data }
{}

bool is_ok() const
{ return data.which() == 0; }
bool is_err() const
{ return data.which() == 1; }

void assert_ok() const
{
if(is_ok())
{ return; }

auto const &err(expect_err());
std::cout << "error: expected ok result, but found: " << err << std::endl;
throw err;
}

E const& expect_err() const
{ return boost::get<E>(data); }
E* expect_err_ptr()
{ return &boost::get<E>(data); }
E const* expect_err_ptr() const
{ return &boost::get<E>(data); }
E expect_err_move()
{ return std::move(boost::get<E>(data)); }
option<E> err()
{
if(is_err())
{ return some(boost::get<E>(data)); }
return none;
}

bool operator ==(result const &rhs) const
{ return rhs.data == data; }
bool operator !=(result const &rhs) const
{ return rhs.data != data; }
bool operator ==(E const &rhs) const
{ return data.which() == 1 && rhs == boost::get<E>(data); }

boost::variant<void_t, E> data;
};

inline std::ostream& operator <<(std::ostream &os, void_t const &)
{ return os; }

template <typename R, typename E>
std::ostream& operator <<(std::ostream &os, result<R, E> const &r)
{
Expand Down

0 comments on commit 70a3b84

Please sign in to comment.