Skip to content

Latest commit

 

History

History
190 lines (150 loc) · 4.83 KB

192.md

File metadata and controls

190 lines (150 loc) · 4.83 KB
Info

Example

constexpr auto error(auto has_error) -> std::experimental::expected<bool, int> {
  if (has_error) {
    return std::experimental::unexpected{42};
  } else {
    return true;
  }
}

int main() {
  assert(error(false));
  assert(error(false).value());

  assert(not error(true).has_value());
  assert(42 == error(true).error());
}

https://godbolt.org/z/x1TKGv

Puzzle

  • Can you implement a simplified version of expected proposal for error handling?
/*TODO - expected/unexpected*/

template<auto Error, class TValue, class TError>
constexpr auto handle_error(TValue value, TError error) -> expected<TValue, TError> {
  if (Error) {
    return unexpected{error};
  } else {
    return value;
  }
}

int main() {
  using namespace boost::ut;
  using namespace std::string_literals;

  "handle error"_test = [] {
    constexpr auto error = true;
    constexpr auto no_error = false;

    should("be convertible to bool") = [] {
      struct {} _;
      expect(handle_error<no_error>(_, _) and
         not handle_error<   error>(_, _));
    };

    should("contain a value") = [] {
      const auto handle_error = ::handle_error<no_error>(42, std::runtime_error{"error"s});
      expect(handle_error and
             handle_error.has_value() and
             handle_error.value() == 42_i
      );
    };

    should("contain an error") = [] {
      const auto handle_error = ::handle_error<error>(42, std::runtime_error{"error"s});
      expect(not handle_error and
             not handle_error.has_value() and
             "error"s == handle_error.error().what()
      );
    };
  };
}

https://godbolt.org/z/rf4sP5

Solutions

struct error_tag {};

template <class TValue, class TError>
struct expected {
    constexpr expected(TValue val) : value_{std::in_place_index<0>, val} {}
    constexpr expected(TError err, error_tag) : value_{std::in_place_index<1>, err} {}

    constexpr explicit operator bool() const { return has_value(); }
    constexpr auto has_value() const { return value_.index() == 0; }
    constexpr auto value() const { return std::get<0>(value_); }
    constexpr auto error() const { return std::get<1>(value_); }

private:
    std::variant<TValue, TError> value_;
};

template <class TError>
struct unexpected {
    constexpr unexpected(TError error) : error_(error) {}

    template <class TValue>
    constexpr operator expected<TValue, TError>() { return expected<TValue, TError>{error_, error_tag{}}; }

private:
    TError error_;
};

https://godbolt.org/z/E6M5nr

template<class TError>
struct unexpected {
    unexpected(TError error) :error(error) {}
    TError error;
};

template<class TValue, class TError>
struct expected {
    expected(TValue value) : value_(value), has_value_(true)  {}
    expected(unexpected<TError> un) : has_value_(false), error_(un.error) {}
    operator bool() const { return has_value_;}
    TValue value() const { return value_; }
    bool has_value() const { return has_value_; }
    TError error() const { return error_.value(); }
private:
    TValue value_;
    bool has_value_;
    std::optional<TError> error_;
};

https://godbolt.org/z/4zbzTh

template <typename TValue, typename TError>
struct expected {
  constexpr explicit(false) expected(const TValue& value)
      : val_or_err{std::in_place_index<VAL_INDEX>, value} {}
  constexpr explicit(true) expected(const TError& error, [[maybe_unused]] const auto is_value)
      : val_or_err{std::in_place_index<ERR_INDEX>, error} {}

  constexpr operator bool() const { return has_value(); }
  constexpr auto has_value() const { return val_or_err.index() == VAL_INDEX; }
  constexpr auto value() const { return std::get<VAL_INDEX>(val_or_err); }
  constexpr auto error() const { return std::get<ERR_INDEX>(val_or_err); }

  static const auto VAL_INDEX = 0;
  static const auto ERR_INDEX = 1;
  std::variant<TValue, TError> val_or_err;
};

template <typename TError>
struct unexpected {
  constexpr explicit(true) unexpected(const TError& e) : error{e} {}

  template <typename TValue>
  constexpr operator expected<TValue, TError>() const {
    return expected<TValue, TError>{error, false};
  }

  TError error;
};

https://godbolt.org/z/q69dK5

template<typename E>
struct unexpected
{
    unexpected(E e) : error(std::move(e)) { }
    E error;
};

template<typename T, typename E>
struct expected : std::optional<T>
{
    expected(T t) : std::optional<T>(std::move(t)) { }
    expected(unexpected<E> u) : err(std::move(u.error)) {}
    E error() const { return err.value(); }
    std::optional<E> err;
};

https://godbolt.org/z/EKxYhb