Created
October 29, 2023 14:14
-
-
Save abedra/1ca118d17bf614bea8e375aa6cf02192 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <variant> | |
#include <optional> | |
#include <functional> | |
template<class... As> struct visitor : As... { using As::operator()...; }; | |
template<class... As> visitor(As...) -> visitor<As...>; | |
template<class A> | |
struct Ok { | |
explicit constexpr Ok(A a) : value_(std::move(a)) {} | |
constexpr A&& get() { return std::move(value_); } | |
constexpr auto& value() { return value_; } | |
private: | |
A value_; | |
}; | |
template<class A> | |
struct Err { | |
explicit constexpr Err(A a) : value_(std::move(a)) {} | |
constexpr A&& get() { return std::move(value_); } | |
constexpr auto& value() { return value_; } | |
private: | |
A value_; | |
}; | |
template<class A, class B> | |
struct Result { | |
using Type = std::variant<Ok<A>, Err<B>>; | |
constexpr Result(Ok<A> value) : value(std::move(value)) {} | |
constexpr Result(Err<B> value) : value(std::move(value)) {} | |
[[nodiscard]] constexpr bool is_ok() const noexcept { return std::holds_alternative<Ok<A>>(value); } | |
[[nodiscard]] constexpr bool is_err() const noexcept { return std::holds_alternative<Err<B>>(value); } | |
[[nodiscard]] constexpr std::optional<A> ok() noexcept { | |
if (is_ok()) { | |
return std::get<Ok<A>>(value).value(); | |
} | |
return std::nullopt; | |
} | |
[[nodiscard]] constexpr std::optional<B> err() noexcept { | |
if (is_err()) { | |
return std::get<Err<B>>(value).value(); | |
} | |
return std::nullopt; | |
} | |
[[nodiscard]] constexpr A unwrap() { | |
if (is_ok()) { | |
return std::get<Ok<A>>(value).value(); | |
} | |
throw std::runtime_error("Called unwrap on an Err value"); | |
} | |
[[nodiscard]] constexpr A unwrap_or(const A &other) noexcept { | |
if (is_ok()) { | |
return std::get<Ok<A>>(value).value(); | |
} | |
return other; | |
} | |
[[nodiscard]] constexpr B unwrap_err() { | |
if (is_err()) { | |
return std::get<Err<B>>(value).value(); | |
} | |
throw std::runtime_error("Called unwrap_err on an Ok value"); | |
} | |
[[nodiscard]] constexpr A expect(const std::string &message) { | |
if (is_ok()) { | |
return std::get<Ok<A>>(value).value(); | |
} | |
throw std::runtime_error(message); | |
} | |
template<class C> | |
[[nodiscard]] constexpr C match(std::function<C(const A &a)> okFn, std::function<C(const B &b)> errFn) noexcept { | |
return std::visit(visitor{ | |
[&okFn] (Ok<A> &a) { return okFn(a.value()); }, | |
[&errFn](Err<B> &b) { return errFn(b.value()); } | |
}, value); | |
} | |
private: | |
Type value; | |
}; | |
struct Success { | |
std::string value; | |
friend std::ostream &operator<<(std::ostream &os, const Success &success) { | |
os << "value: " << success.value; | |
return os; | |
} | |
}; | |
struct Error { | |
std::string value; | |
friend std::ostream &operator<<(std::ostream &os, const Error &error) { | |
os << "value: " << error.value; | |
return os; | |
} | |
}; | |
template<class A> | |
std::ostream& operator<<(std::ostream& os, std::optional<A> const& opt) | |
{ | |
return opt ? os << opt.value() : os << "nullopt"; | |
} | |
/* | |
int main() { | |
Result<Success, Error> x = Ok(Success{"success"}); | |
Result<Success, Error> y = Err(Error{"error"}); | |
std::cout << x.ok() << std::endl; | |
std::cout << x.err() << std::endl; | |
y.template match<void>( | |
[](auto &ok){ | |
std::cout << ok << std::endl; | |
}, | |
[](auto &err){ | |
std::cout << err << std::endl; | |
} | |
); | |
std::cout << x.unwrap() << std::endl; | |
std::cout << y.unwrap_err() << std::endl; | |
std::cout << y.unwrap_or(Success{"other"}) << std::endl; | |
std::cout << y.expect("couldn't get value") << std::endl; | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment