Skip to content

Instantly share code, notes, and snippets.

@abedra
Created October 29, 2023 14:14
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 abedra/1ca118d17bf614bea8e375aa6cf02192 to your computer and use it in GitHub Desktop.
Save abedra/1ca118d17bf614bea8e375aa6cf02192 to your computer and use it in GitHub Desktop.
#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