From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#pragma once
#include <utility>
#include <exception>
#include <iosfwd>
namespace panda {
template <class E>
struct unexpected {
static_assert(!std::is_same<E, void>::value, "E must not be void");
unexpected () = delete;
constexpr explicit unexpected (const E& e) : _val(e) {}
constexpr explicit unexpected (E&& e) : _val(std::move(e)) {}
constexpr const E& value () const & { return _val; }
constexpr const E&& value () const && { return std::move(_val); }
E& value () & { return _val; }
E&& value () && { return std::move(_val); }
private:
E _val;
};
template <class E>
inline unexpected<typename std::decay<E>::type> make_unexpected (E &&e) {
return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
}
template <class E>
struct bad_expected_access : std::exception {
explicit bad_expected_access (E e) : _val(std::move(e)) {}
virtual const char* what () const noexcept override { return "Bad expected access"; }
const E& error () const & { return _val; }
const E&& error () const && { return std::move(_val); }
E& error () & { return _val; }
E&& error () && { return std::move(_val); }
private:
E _val;
};
/// A tag to tell expected to construct the unexpected value
struct unexpect_t { unexpect_t() = default; };
static constexpr unexpect_t unexpect {};
template <class T, class E>
struct expected {
using value_type = T;
using error_type = E;
using unexpected_type = unexpected<E>;
template <typename = typename std::enable_if<std::is_default_constructible<T>::value>::type>
expected () {
_has_val = true;
::new (&_val) T();
}
expected (const expected& ex) {
if (ex._has_val) construct_val(ex._val);
else construct_err(ex._err);
}
expected (expected&& ex) {
if (ex._has_val) construct_val(std::move(ex._val));
else construct_err(std::move(ex._err));
}
template <class T2, class E2>
explicit expected (const expected<T2,E2>& ex) {
if (ex._has_val) construct_val(ex._val);
else construct_err(ex._err);
}
template <class T2, class E2>
explicit expected (expected<T2,E2>&& ex) {
if (ex._has_val) construct_val(std::move(ex._val));
else construct_err(std::move(ex._err));
}
template <class T2, typename = typename std::enable_if<std::is_constructible<T, T2>::value>::type>
expected (T2&& v) {
construct_val(std::forward<T2>(v));
}
template <class E2>
expected (const unexpected<E2>& uex) {
construct_err(uex.value());
}
template <class E2>
expected (unexpected<E2>&& uex) {
construct_err(std::move(uex.value()));
}
~expected () {
if (_has_val) _val.~T();
else _err.~E();
}
expected& operator= (const expected& ex) {
if (ex._has_val) set_val(ex._val);
else set_err(ex._err);
return *this;
}
expected& operator= (expected&& ex) {
if (ex._has_val) set_val(std::move(ex._val));
else set_err(std::move(ex._err));
return *this;
}
template <class T2>
expected& operator= (T2&& v) {
set_val(std::forward<T2>(v));
return *this;
}
template <class E2>
expected& operator= (const unexpected<E2>& uex) {
set_err(uex.value());
return *this;
}
template <class E2>
expected& operator= (unexpected<E2>&& uex) {
set_err(std::move(uex.value()));
return *this;
}
constexpr bool has_value () const noexcept { return _has_val; }
constexpr explicit operator bool () const noexcept { return _has_val; }
const T& value () const & { if (!_has_val) throw bad_expected_access<E>(_err); return _val; }
T& value () & { if (!_has_val) throw bad_expected_access<E>(_err); return _val; }
const T&& value () const && { if (!_has_val) throw bad_expected_access<E>(_err); return std::move(_val); }
T&& value () && { if (!_has_val) throw bad_expected_access<E>(_err); return std::move(_val); }
template <class T2> constexpr T value_or (T2&& v) const & { return bool(*this) ? this->_val : static_cast<T>(std::forward<T2>(v)); }
template <class T2> constexpr T value_or (T2&& v) && { return bool(*this) ? std::move(this->_val) : static_cast<T>(std::forward<T2>(v)); }
const E& error () const & { return _err; }
E& error () & { return _err; }
const E&& error () const && { return std::move(_err); }
E&& error () && { return std::move(_err); }
const T* operator-> () const { return &_val; }
T* operator-> () { return &_val; }
const T& operator* () const & { return _val; }
T& operator* () & { return _val; }
const T&& operator* () const && { return std::move(_val); }
T&& operator* () && { return std::move(_val); }
template <class...Args>
void emplace (Args&&... args) {
if (_has_val) _val = T(std::forward<Args>(args)...);
else {
auto tmp = std::move(_err);
_err.~E();
try {
::new (&_val) T(std::forward<Args>(args)...);
_has_val = true;
} catch (...) {
new (&_err) E(std::move(tmp));
throw;
}
}
}
template <class F>
auto and_then (F&& f) const & -> decltype(f(std::declval<T>())) {
if (!_has_val) return unexpected_type(_err);
return f(_val);
}
template <class F>
auto and_then (F&& f) const && -> decltype(f(std::declval<T>())) {
if (!_has_val) return unexpected_type(std::move(_err));
return f(std::move(_val));
}
template <class F>
auto or_else (F&& f) const & -> decltype(f(std::declval<E>())) {
if (_has_val) return *this;
return f(_err);
}
template <class F>
auto or_else (F&& f) const && -> decltype(f(std::declval<E>())) {
if (_has_val) return std::move(*this);
return f(std::move(_err));
}
template <class F>
auto map (F&& f) const & -> expected<decltype(f(std::declval<T>())), E> {
if (!_has_val) return unexpected_type(_err);
return f(_val);
}
template <class F>
auto map (F&& f) const && -> expected<decltype(f(std::declval<T>())), E> {
if (!_has_val) return unexpected_type(std::move(_err));
return f(std::move(_val));
}
template <class F>
auto map_error (F&& f) const & -> expected<T, decltype(f(std::declval<E>()))> {
if (_has_val) return _val;
return make_unexpected(f(_err));
}
template <class F>
auto map_error (F&& f) const && -> expected<T, decltype(f(std::declval<E>()))> {
if (_has_val) return std::move(_val);
return make_unexpected(f(std::move(_err)));
}
template <class T2 = T>
void swap (expected& ex) {
if (_has_val) {
if (ex._has_val) std::swap(_val, ex._val);
else {
auto tmp = std::move(ex._err);
ex._err.~E();
ex._has_val = true;
new (&ex._val) T(std::move(_val));
_val.~T();
_has_val = false;
new (&_err) E(std::move(tmp));
}
}
else {
if (ex._has_val) ex.swap(*this);
else std::swap(_err, ex._err);
}
}
private:
template <class T2, class E2> friend struct expected;
template <class T2>
void construct_val (T2&& v) {
_has_val = true;
::new (&_val) T(std::forward<T2>(v));
}
template <class E2>
void construct_err (E2&& e) {
_has_val = false;
::new (&_err) E(std::forward<E2>(e));
}
template <class T2>
void set_val (T2&& v) {
if (_has_val) _val = std::forward<T2>(v);
else {
_err.~E();
_has_val = true;
::new (&_val) T(std::forward<T2>(v));
}
}
template <class E2>
void set_err (E2&& e) {
if (_has_val) {
_val.~T();
_has_val = false;
::new (&_err) E(std::forward<E2>(e));
}
else _err = std::forward<E2>(e);
}
union {
T _val;
E _err;
};
bool _has_val;
};
template <class E>
struct expected<void, E> {
using value_type = void;
using error_type = E;
using unexpected_type = unexpected<E>;
expected () { _has_val = true; }
expected (const expected& ex) {
if (ex._has_val) _has_val = true;
else construct_err(ex._err);
}
expected (expected&& ex) {
if (ex._has_val) _has_val = true;
else construct_err(std::move(ex._err));
}
template <class E2>
explicit expected (const expected<void,E2>& ex) {
if (ex._has_val) _has_val = true;
else construct_err(ex._err);
}
template <class E2>
explicit expected (expected<void,E2>&& ex) {
if (ex._has_val) _has_val = true;
else construct_err(std::move(ex._err));
}
template <class E2>
expected (const unexpected<E2>& uex) {
construct_err(uex.value());
}
template <class E2>
expected (unexpected<E2>&& uex) {
construct_err(std::move(uex.value()));
}
~expected () {
if (!_has_val) _err.~E();
}
expected& operator= (const expected& ex) {
if (ex._has_val) set_val();
else set_err(ex._err);
return *this;
}
expected& operator= (expected&& ex) {
if (ex._has_val) set_val();
else set_err(std::move(ex._err));
return *this;
}
template <class E2>
expected& operator= (const unexpected<E2>& uex) {
set_err(uex.value());
return *this;
}
template <class E2>
expected& operator= (unexpected<E2>&& uex) {
set_err(std::move(uex.value()));
return *this;
}
constexpr bool has_value () const noexcept { return _has_val; }
constexpr explicit operator bool () const noexcept { return _has_val; }
const E& error () const & { return _err; }
E& error () & { return _err; }
const E&& error () const && { return std::move(_err); }
E&& error () && { return std::move(_err); }
template <class F>
auto and_then (F&& f) const & -> decltype(f()) {
if (!_has_val) return unexpected_type(_err);
return f();
}
template <class F>
auto and_then (F&& f) const && -> decltype(f()) {
if (!_has_val) return unexpected_type(std::move(_err));
return f();
}
template <class F>
auto or_else (F&& f) const & -> decltype(f(std::declval<E>())) {
if (_has_val) return *this;
return f(_err);
}
template <class F>
auto or_else (F&& f) const && -> decltype(f(std::declval<E>())) {
if (_has_val) return std::move(*this);
return f(std::move(_err));
}
template <class F>
auto map (F&& f) const & -> expected<decltype(f()), E> {
if (!_has_val) return unexpected_type(_err);
return f();
}
template <class F>
auto map (F&& f) const && -> expected<decltype(f()), E> {
if (!_has_val) return unexpected_type(std::move(_err));
return f();
}
template <class F>
auto map_error (F&& f) const & -> expected<void, decltype(f(std::declval<E>()))> {
if (_has_val) return {};
return make_unexpected(f(_err));
}
template <class F>
auto map_error (F&& f) const && -> expected<void, decltype(f(std::declval<E>()))> {
if (_has_val) return {};
return make_unexpected(f(std::move(_err)));
}
template <class E2 = E>
void swap (expected& ex) {
if (_has_val) {
if (!ex._has_val) {
new (&_err) E(std::move(ex._err));
ex._err.~E();
std::swap(_has_val, ex._has_val);
}
}
else {
if (ex._has_val) ex.swap(*this);
else std::swap(_err, ex._err);
}
}
private:
template <class T2, class E2> friend struct expected;
template <class E2>
void construct_err (E2&& e) {
_has_val = false;
::new (&_err) E(std::forward<E2>(e));
}
void set_val () {
if (_has_val) return;
_err.~E();
_has_val = true;
}
template <class E2>
void set_err (E2&& e) {
if (_has_val) {
_has_val = false;
::new (&_err) E(std::forward<E2>(e));
}
else _err = std::forward<E2>(e);
}
union {
E _err;
};
bool _has_val;
};
template <class T, class E, class T2, class E2>
bool operator== (const expected<T, E>& lhs, const expected<T2, E2>& rhs) {
if (lhs.has_value() != rhs.has_value()) return false;
return lhs.has_value() ? *lhs == *rhs : lhs.error() == rhs.error();
}
template <class E, class E2>
bool operator== (const expected<void, E>& lhs, const expected<void, E2>& rhs) {
if (lhs.has_value() != rhs.has_value()) return false;
return lhs.has_value() ? true : lhs.error() == rhs.error();
}
template <class T, class E, class T2, class E2>
bool operator!= (const expected<T, E>& lhs, const expected<T2, E2>& rhs) { return !operator==(lhs, rhs); }
template <class T, class E, class T2>
typename std::enable_if<!std::is_void<T>::value, bool>::type
operator== (const expected<T, E>& x, const T2& v) { return x.has_value() ? *x == v : false; }
template <class T, class E, class T2>
bool operator== (const T2& v, const expected<T, E>& x) { return x == v; }
template <class T, class E, class T2>
bool operator!= (const expected<T, E>& x, const T2& v) { return !(x == v); }
template <class T, class E, class T2>
bool operator!= (const T2& v, const expected<T, E>& x) { return !(x == v); }
template <class T, class E>
bool operator== (const expected<T, E>& x, const unexpected<E>& e) { return x.has_value() ? false : x.error() == e.value(); }
template <class T, class E>
bool operator== (const unexpected<E>& e, const expected<T, E>& x) { return x == e; }
template <class T, class E>
bool operator!= (const expected<T, E>& x, const unexpected<E>& e) { return !(x == e); }
template <class T, class E>
bool operator!= (const unexpected<E>& e, const expected<T, E>& x) { return !(x == e); }
template <class T, class E>
void swap (expected<T,E>& lhs, expected<T,E>& rhs) { lhs.swap(rhs); }
template <class T, class E, typename = decltype(std::declval<std::ostream&>() << std::declval<T>() << std::declval<E>())>
std::ostream& operator<< (std::ostream& s, const expected<T, E>& x) {
if (x.has_value()) {
s << x.value();
} else {
s << x.error();
}
return s;
}
}