diff --git a/expected.hpp b/expected.hpp new file mode 100644 index 0000000..72b3895 --- /dev/null +++ b/expected.hpp @@ -0,0 +1,431 @@ +/// +// 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