From 013567d05066cc82a22490f63431b60c8f8297f8 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Thu, 26 Oct 2017 21:51:24 +0100 Subject: [PATCH] Compiles! --- expected.hpp | 558 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 336 insertions(+), 222 deletions(-) diff --git a/expected.hpp b/expected.hpp index 84119df..dc6c89d 100644 --- a/expected.hpp +++ b/expected.hpp @@ -15,6 +15,8 @@ #define TL_EXPECTED_HPP #include +#include +#include namespace tl { namespace detail { @@ -22,15 +24,25 @@ template using enable_if_t = typename std::enable_if::type; } + #ifndef TL_IN_PLACE_T_DEFINED + #define TL_IN_PLACE_T_DEFINED + /// \brief A tag type to tell optional to construct its value in-place + struct in_place_t { + explicit in_place_t() = default; + }; + /// \brief A tag to tell optional to construct its value in-place + static constexpr in_place_t in_place{}; + #endif + template class unexpected { public: - static_assert(!std::is_same, "E must not be void"); + static_assert(!std::is_same::value, "E must not be void"); unexpected() = delete; constexpr explicit unexpected(const E& e) : m_val(e) {} - constexpr explicit unexpected(E&&) + constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {} constexpr const E& value() const & { return m_val; } @@ -51,11 +63,34 @@ using enable_if_t = typename std::enable_if::type; operator!=(const unexpected& lhs, const unexpected& rhs) { return lhs.value() != rhs.value(); } -} + + struct unexpect_t { + unexpect_t() = default; + }; + static constexpr unexpect_t unexpect{}; namespace detail { - template ::value, bool = std::is_trivially_destrictible::value> + template ::value, bool = std::is_trivially_destructible::value> struct expected_storage_base { + 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>* = nullptr> + 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> + constexpr explicit expected_storage_base(unexpect_t, Args&&... args) : + m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args&&...>::value>* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args) : + m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { if (m_has_val) { m_val.~T(); @@ -73,6 +108,26 @@ namespace detail { template struct expected_storage_base { + 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>* = nullptr> + 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> + constexpr explicit expected_storage_base(unexpect_t, Args&&... args) : + m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args&&...>::value>* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args) : + m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; bool m_has_val; union { @@ -83,6 +138,26 @@ namespace detail { template struct expected_storage_base { + 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>* = nullptr> + 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> + constexpr explicit expected_storage_base(unexpect_t, Args&&... args) : + m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args&&...>::value>* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args) : + m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { if (!m_has_val) { m_unexpect.~unexpected(); @@ -99,6 +174,26 @@ namespace detail { template struct expected_storage_base { + 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>* = nullptr> + 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> + constexpr explicit expected_storage_base(unexpect_t, Args&&... args) : + m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args&&...>::value>* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args) : + m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { if (m_has_val) { m_val.~T(); @@ -107,24 +202,49 @@ namespace detail { bool m_has_val; union { T m_val; - unexpected m_unexpect; + unexpected m_unexpect; }; }; - template + template struct expected_storage_base { + constexpr expected_storage_base() + : m_val(), m_has_val(true) {} + + + template ::value>* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args&&... args) : + m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args&&...>::value>* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args) : + m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; bool m_has_val; struct dummy{}; union { dummy m_val; - unexpected m_unexpect; + unexpected m_unexpect; }; }; - template + template struct expected_storage_base { + constexpr expected_storage_base() + : m_val(), m_has_val(true) {} + + template ::value>* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args&&... args) : + m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args&&...>::value>* = nullptr> + constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args) : + m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { if (m_has_val) { m_val.~T(); @@ -135,7 +255,7 @@ namespace detail { struct dummy{}; union { dummy m_val; - unexpected m_unexpect; + unexpected m_unexpect; }; }; @@ -144,216 +264,9 @@ namespace detail { 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 { +class bad_expected_access : public std::exception { public: explicit bad_expected_access(E e) : m_val(std::move(e)) {} @@ -370,14 +283,214 @@ private: E m_val; }; -template <> -class bad_expected_access : public exception { +template +class expected : private detail::expected_storage_base, private detail::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"); + + T* valptr() { return std::addressof(this->m_value); } + unexpected* errptr() { return std::addressof(this->m_unexpect); } + T& val() { return this->m_value; } + unexpected& err() { return this->m_unexpect; } + const T& val() const { return this->m_value; } + const unexpected& err() const { return this->m_unexpect; } + + using storage_base = detail::expected_storage_base; + public: - explicit bad_expected_access(); + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + + constexpr expected() = default; + + template ::value>* = nullptr> + constexpr expected(in_place_t, Args&&... args) : + storage_base(in_place, std::forward(args)...){} + + template &, Args&&...>::value>* = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args&&... args) : + storage_base(in_place, il, std::forward(args)...){} + + template ::value>* = nullptr, detail::enable_if_t::value>* = nullptr> + explicit constexpr expected(unexpected const& e) + : storage_base(unexpect, e) {} + + template ::value>* = nullptr, detail::enable_if_t::value>* = nullptr> + constexpr expected(unexpected const& e) + : storage_base(unexpect, e) {} + + template ::value>* = nullptr, detail::enable_if_t::value>* = nullptr> + explicit constexpr expected(unexpected && e) noexcept(std::is_nothrow_constructible::value) + : storage_base(unexpect, std::move(e)) {} + + template ::value>* = nullptr, detail::enable_if_t::value>* = nullptr> + constexpr expected(unexpected && e) noexcept(std::is_nothrow_constructible::value) + : storage_base(unexpect, std::move(e)) {} + + template ::value>* = nullptr> + constexpr explicit expected(unexpect_t, Args&&... args) + : storage_base(unexpect, std::forward(args)...) {} + + template &, Args&&...>::value>* = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) + : storage_base(unexpect, il, std::forward(args)...) {} + + constexpr expected(const expected& rhs) { + if (rhs.has_value()) { + ::new (valptr()) T (*rhs); + } + else { + ::new (errptr()) unexpected_type (unexpected(rhs.error())); + } + } + + //TODO SFINAE + constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value) { + if (rhs.has_value()) { + ::new (valptr()) T (std::move(*rhs)); + } + else { + ::new (errptr()) unexpected_type (unexpected(std::move(rhs.error()))); + } + } + + //TODO SFINAE + template ::value || !std::is_convertible::value)>* = nullptr> + explicit constexpr expected(const expected& rhs) { + if (rhs.has_value()) { + ::new (valptr()) T (*rhs); + } + else { + ::new (errptr()) unexpected_type (unexpected(rhs.error())); + } + } + + //TODO SFINAE + template ::value || !std::is_convertible::value)>* = nullptr> + explicit constexpr expected(const expected& rhs) { + if (rhs.has_value()) { + ::new (valptr()) T (*rhs); + } + else { + ::new (errptr()) unexpected_type (unexpected(rhs.error())); + } + } + + //TODO SFINAE + template ::value || std::is_convertible::value)>* = nullptr> + constexpr expected(expected&& rhs) { + if (rhs.has_value()) { + ::new (valptr()) T (std::move(*rhs)); + } + else { + ::new (errptr()) unexpected_type (unexpected(std::move(rhs.error()))); + } + } + + //TODO SFINAE + template ::value>* = nullptr> + explicit constexpr expected(U&& v) + : expected(in_place, std::forward(v)) + {} + + //TODO SFINAE + template ::value>* = nullptr> + constexpr expected(U&& v) + : expected(in_place, std::forward(v)) + {} + +//TODO + expected& operator=(const expected&); + expected& operator=(expected&&) noexcept; + template expected& operator=(U&&); + template + expected& operator=(const unexpected&); + template + expected& operator=(unexpected&&) noexcept; + template + void emplace(Args&&...); + template + void emplace(std::initializer_list, Args&&...); + +//TODO SFINAE + void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible::value && noexcept(swap(std::declval(), std::declval())) && std::is_nothrow_move_constructible::value && noexcept(swap(std::declval(), std::declval()))) + { + if (has_value() && rhs.has_value()) { + using std::swap; + swap(val(), rhs.val()); + } + else if (!has_value() && rhs.has_value()) { + using std::swap; + swap(err(), rhs.err()); + } + else if (has_value()) { + auto temp = std::move(rhs.err()); + ::new (rhs.valptr()) T (val()); + ::new (errptr()) unexpected_type (std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + else { + auto temp = std::move(this->err()); + ::new (valptr()) T (rhs.val()); + ::new (errptr()) unexpected_type (std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + } + + constexpr const T* operator ->() const { return valptr(); } + constexpr T* operator ->() { return valptr(); } + constexpr const T& operator *() const& { return val(); } + constexpr T& operator *() & { return val(); } + constexpr const T&& operator *() const && { return std::move(val()); } + constexpr T&& operator *() && { return std::move(val()); } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr const T& value() const& { + if (!has_value()) throw bad_expected_access(err()); + return val(); + } + constexpr T& value() & { + if (!has_value()) throw bad_expected_access(err()); + return val(); + } + constexpr const T&& value() const && { + if (!has_value()) throw bad_expected_access(err()); + return std::move(val()); + } + constexpr T&& value() && { + if (!has_value()) throw bad_expected_access(err()); + return std::move(val()); + } + constexpr const E& error() const& { return err().value(); } + constexpr E& error() & { return err().value(); } + constexpr const E&& error() const && { return std::move(err().value()); } + constexpr E&& error() && { return std::move(err().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&& v) && { + 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)); + + } + + }; - template <> - class bad_expected_access; + +//TODO +template +class expected { + +}; template constexpr bool operator==(const expected& lhs, const expected& rhs) { @@ -432,5 +545,6 @@ template ::v void swap(expected& lhs, expected& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } +} #endif