Skip to content

Instantly share code, notes, and snippets.

@hatsusato
Last active August 22, 2019 09:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hatsusato/d21785bf60ab34699069f29e56021673 to your computer and use it in GitHub Desktop.
Save hatsusato/d21785bf60ab34699069f29e56021673 to your computer and use it in GitHub Desktop.
#ifndef INCLUDE_GUARD_AB644267_F64D_4246_AF44_EA40B259E804
#define INCLUDE_GUARD_AB644267_F64D_4246_AF44_EA40B259E804
#include <functional>
#include <utility>
#include <variant>
namespace hatsusato {
namespace detail {
namespace tag {
struct Ok {};
struct Err {};
} // namespace tag
namespace container {
template <typename T>
struct Val {
template <typename U>
explicit Val(U&& u) : t{std::forward<U>(u)} {}
T get() && { return std::move(t); }
T& get() & { return t; }
const T& get() const& { return t; }
private:
T t;
};
template <typename T>
struct Ref {
template <typename U>
explicit Ref(U& u) : t{u} {}
T& get() { return t; }
private:
T& t;
};
} // namespace container
namespace holder {
template <typename Tag, typename U>
class Holder : private container::Val<U> {
using Base = container::Val<U>;
public:
using Base::Base, Base::get;
U& get() & = delete;
const U& get() const& = delete;
};
template <typename Tag, typename U>
class Holder<Tag, U&> : private container::Ref<U> {
using Base = container::Ref<U>;
public:
using Base::Base, Base::get;
U& get() & = delete;
};
template <typename T>
using Ok = Holder<tag::Ok, T>;
template <typename E>
using Err = Holder<tag::Err, E>;
} // namespace holder
namespace storage {
template <typename Tag, typename U>
class Storage : private container::Val<U> {
using Base = container::Val<U>;
public:
using Base::Base, Base::get;
};
template <typename Tag, typename U>
class Storage<Tag, U&> : private std::reference_wrapper<U> {
using Base = std::reference_wrapper<U>;
public:
using Base::Base, Base::get;
};
template <typename T>
using Ok = Storage<tag::Ok, T>;
template <typename E>
using Err = Storage<tag::Err, E>;
} // namespace storage
} // namespace detail
template <typename T, typename E>
class Result {
using Ok = detail::storage::Ok<T>;
using Err = detail::storage::Err<E>;
std::variant<Ok, Err> s;
public:
template <typename U>
Result(detail::holder::Ok<U>&& u) : s{Ok{std::move(u).get()}} {}
template <typename U>
Result(detail::holder::Err<U>&& u) : s{Err{std::move(u).get()}} {}
explicit operator bool() const { return s.index() == 0; }
T operator*() && { return value(); }
T& operator*() & { return value(); }
const T& operator*() const& { return value(); }
auto operator-> () { return &operator*(); }
auto operator-> () const { return &operator*(); }
T value() && { return std::get<0>(std::move(s)).get(); }
T& value() & { return std::get<0>(s).get(); }
const T& value() const& { return std::get<0>(s).get(); }
E error() && { return std::get<1>(std::move(s)).get(); }
E& error() & { return std::get<1>(s).get(); }
const E& error() const& { return std::get<1>(s).get(); }
};
namespace detail {
template <typename>
struct ResultTrait : std::false_type {};
template <typename T, typename E>
struct ResultTrait<Result<T, E>> : std::true_type {
using Ok = holder::Ok<T>;
using Err = holder::Err<E>;
};
} // namespace detail
template <typename T>
auto ok(T&& x) {
using Trait = detail::ResultTrait<std::remove_reference_t<T>>;
if constexpr (Trait::value) {
return typename Trait::Ok{std::forward<T>(x).value()};
} else {
return detail::holder::Ok<T>{std::forward<T>(x)};
}
}
template <typename E>
auto err(E&& x) {
using Trait = detail::ResultTrait<std::remove_reference_t<E>>;
if constexpr (Trait::value) {
return typename Trait::Err{std::forward<E>(x).error()};
} else {
return detail::holder::Err<E>{std::forward<E>(x)};
}
}
} // namespace hatsusato
#endif // INCLUDE_GUARD_AB644267_F64D_4246_AF44_EA40B259E804
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment