/// // expected - An implementation of std::expected with extensions // Written in 2017 by Simon Brand (@TartanLlama) // // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to the // public domain worldwide. This software is distributed without any warranty. // // You should have received a copy of the CC0 Public Domain Dedication // along with this software. If not, see // . /// #ifndef TL_EXPECTED_HPP #define TL_EXPECTED_HPP #include namespace tl { template class unexpected { public: static_assert(!std::is_same, "E must not be void"); unexpected() = delete; constexpr explicit unexpected(const E& e) : m_val(e) {} constexpr explicit unexpected(E&&) : m_val(std::move(e)) {} constexpr const E& value() const & { return m_val; } constexpr E& value() & { return m_val; } constexpr E&& value() && { return std::move(m_val); } constexpr E const&& value() const&& { return std::move(m_val); } private: E m_val; // exposition only }; template constexpr bool operator==(const unexpected& lhs, const unexpected& rhs) { return lhs.value() == rhs.value(); } template constexpr bool operator!=(const unexpected& lhs, const unexpected& rhs) { return lhs.value() != rhs.value(); } } namespace detail { template ::value, bool = std::is_trivially_destrictible::value> struct expected_storage_base { ~expected_storage_base() { if (m_has_val) { m_val.~T(); } else { m_unexpect.~unexpected(); } } bool m_has_val; union { T m_val; unexpected m_unexpect; }; }; template struct expected_storage_base { ~expected_storage_base() = default; bool m_has_val; union { T m_val; unexpected m_unexpect; }; }; template struct expected_storage_base { ~expected_storage_base() { if (!m_has_val) { m_unexpect.~unexpected(); } } bool m_has_val; union { T m_val; unexpected m_unexpect; }; }; template struct expected_storage_base { ~expected_storage_base() { if (m_has_val) { m_val.~T(); } } bool m_has_val; union { T m_val; unexpected m_unexpect; }; }; template struct expected_storage_base { ~expected_storage_base() = default; bool m_has_val; struct dummy{}; union { dummy m_val; unexpected m_unexpect; }; }; template struct expected_storage_base { ~expected_storage_base() { if (m_has_val) { m_val.~T(); } } bool m_has_val; struct dummy{}; union { dummy m_val; unexpected m_unexpect; }; }; //TODO, conditionally delete things template class expected_ctor_base{}; } template class expected : private expected_storage_base, private expected_ctor_base { static_assert(!std::is_reference::value, "T must not be a reference"); static_assert(!std::is_same>::value, "T must not be in_place_t"); static_assert(!std::is_same>::value, "T must not be unexpect_t"); static_assert(!std::is_same>::value, "T must not be unexpected"); static_assert(!std::is_reference::value, "E must not be a reference"); static_assert(!std::is_same::value, "T must not be void"); public: typedef T value_type; typedef E error_type; typedef unexpected unexpected_type; template struct rebind { using type = expected; }; // X.Z.4.1, constructors constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} template ::value>* = nullptr> constexpr expected_storage_base(in_place_t, Args&&... args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args&&...>::value> constexpr expected_storage_base(in_place_t, std::initializer_list il, Args&&... args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value>* = nullptr, detail::enable_if_t::value>* = nullptr> explicit constexpr expected(unexpected const& e) : m_unexpect(std::move(e)), m_has_value(false) {} template ::value>* = nullptr, detail::enable_if_t::value>* = nullptr> constexpr expected(unexpected const& e) : m_unexpect(std::move(e)), m_has_value(false) {} template ::value>* = nullptr, detail::enable_if_t::value>* = nullptr> explicit constexpr expected(unexpected && e) noexcept(std::is_nothrow_constructible::value) : m_unexpect(std::move(e)), m_has_value(false) {} template ::value>* = nullptr, detail::enable_if_t::value>* = nullptr> constexpr expected(unexpected && e) noexcept(std::is_nothrow_constructible::value) : m_unexpect(std::move(e)), m_has_value(false) {} template ::value>* = nullptr> constexpr explicit expected(unexpect_t, Args&&... args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template &, Args&&...>::value>* = nullptr> constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} constexpr expected(const expected& rhs) { if (rhs.has_value()) { new (std::addressof(m_value)) (*rhs); } else { new (std::addressof(m_unexpect)) (unexpected(rhs.error())); } } //TODO SFINAE constexpr expected(expected&&) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) { if (rhs.has_value()) { new (std::addressof(m_value)) (std::move(*rhs)); } else { new (std::addressof(m_unexpect)) (unexpected(std::move(rhs.error()))); } } //TODO SFINAE template || !std::is_convertible::value>* = nullptr> explicit constexpr expected(const expected& rhs) { if (rhs.has_value()) { new (std::addressof(m_value)) (*rhs); } else { new (std::addressof(m_unexpect)) (unexpected(rhs.error())); } } //TODO SFINAE template || !std::is_convertible::value>* = nullptr> explicit constexpr expected(const expected& rhs) { if (rhs.has_value()) { new (std::addressof(m_value)) (*rhs); } else { new (std::addressof(m_unexpect)) (unexpected(rhs.error())); } } //TODO SFINAE template || std::is_convertible::value>* = nullptr> constexpr expected(expected&&) { if (rhs.has_value()) { new (std::addressof(m_value)) (std::move(*rhs)); } else { new (std::addressof(m_unexpect)) (unexpected(std::move(rhs.error()))); } } //TODO SFINAE template ::value>* = nullptr> explicit constexpr expected(U&& v) : expected_storage_base(in_place, std::forward(v)) {} //TODO SFINAE template ::value>* = nullptr> constexpr expected(U&& v) : expected_storage_base(in_place, std::forward(v)) {} //TODO expected& operator=(const expected&); expected& operator=(expected&&) noexcept(see below); template expected& operator=(U&&); template expected& operator=(const unexpected&); template expected& operator=(unexpected&&) noexcept(see below); template void emplace(Args&&...); template void emplace(initializer_list, Args&&...); //TODO SFINAE void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible::value && noexcept(swap(declval(), declval())) && std::is_nothrow_move_constructible::value && noexcept(swap(declval(), declval()))) { if (has_value() && rhs.has_value()) { using std::swap; swap(m_val, rhs.m_val); } else if (!has_value() && rhs.has_value()) { using std::swap; swap(m_unexpect, rhs.m_unexpect); } else if (has_value()) { auto temp = std::move(rhs.m_unexpect); new (std::addressof(rhs.m_val)) (m_val); new (std::addressof(m_unexpect)) (temp); std::swap(m_has_value, rhs.m_has_value); } else { auto temp = std::move(m_unexpect); new (std::addressof(m_val)) (rhs.m_val); new (std::addressof(rhs.m_unexpect)) (temp); std::swap(m_has_value, rhs.m_has_value); } } constexpr const T* operator ->() const { return std::addressof(m_val); } constexpr T* operator ->() { return std::addressof(m_val); } constexpr const T& operator *() const& { return m_val; } constexpr T& operator *() & { return m_val; } constexpr const T&& operator *() const && { return std::move(m_val); } constexpr T&& operator *() && { return std::move(m_val); } constexpr explicit operator bool() const noexcept { return m_has_val; } constexpr bool has_value() const noexcept { return m_has_val; } constexpr const T& value() const& { if (!m_has_value) throw bad_expected_access(); return m_value; } constexpr T& value() & { if (!m_has_value) throw bad_expected_access(); return m_value; } constexpr const T&& value() const && { if (!m_has_value) throw bad_expected_access(); return std::move(m_value); } constexpr T&& value() && { if (!m_has_value) throw bad_expected_access(); return std::move(m_value); } constexpr const E& error() const& { return m_unexpect.value(); } constexpr E& error() & { return m_unexpect.value(); } constexpr const E&& error() const && { return std:move(m_unexpect.value()); } constexpr E&& error() && { return std:move(m_unexpect.value()); } template constexpr T value_or(U&& v) const& { static_assert(std::is_copy_constructible::value && std::is_convertible::value, "T must be copy-constructible and convertible to from U&&"); return bool(*this) ? **this : static_cast(std::forward(v)); } template T value_or(U&&) && { static_assert(std::is_move_constructible::value && std::is_convertible::value, "T must be move-constructible and convertible to from U&&"); return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); } }; //TODO template class expected { }; struct unexpect_t { unexpect_t() = default; }; inline constexpr unexpect_t unexpect{}; template class bad_expected_access : public bad_expected_access { public: explicit bad_expected_access(E e) : m_val(std::move(e)) {} virtual const char* what() const noexcept override { return "Bad expected access"; } const E& error() const& { return m_val; } E& error() & { return m_val; } const E&& error() const && { return std::move(m_val); } E&& error() && { return std::move(m_val); } private: E m_val; }; template <> class bad_expected_access : public exception { public: explicit bad_expected_access(); }; template <> class bad_expected_access; template constexpr bool operator==(const expected& lhs, const expected& rhs) { return (lhs.has_value() != rhs.has_value()) ? false : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); } template constexpr bool operator!=(const expected& lhs, const expected& rhs) { return (lhs.has_value() != rhs.has_value()) ? true : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); } template constexpr bool operator==(const expected& x, const T& v) { return x.has_value() ? *x == v : false; } template constexpr bool operator==(const T& v, const expected& x) { return x.has_value() ? *x == v : false; } template constexpr bool operator!=(const expected& x, const T& v) { return x.has_value() ? *x != v : true; } template constexpr bool operator!=(const T& v, const expected& x) { return x.has_value() ? *x != v : true; } template constexpr bool operator==(const expected& x, const unexpected& e) { return x.has_value() ? true : x.error() == e.value(); } template constexpr bool operator==(const unexpected& e, const expected& x) { return x.has_value() ? true : x.error() == e.value(); } template constexpr bool operator!=(const expected& x, const unexpected& e) { return x.has_value() ? false : x.error() != e.value(); } template constexpr bool operator!=(const unexpected& e, const expected& x) { return x.has_value() ? false : x.error() != e.value(); } //TODO is_swappable template ::value && std::is_move_constructible::value>* = nullptr> void swap(expected& lhs, expected& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } #endif