/* * Copyright (C) 2016-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ // Implementation of Library Fundamentals v3's std::expected, as described here: http://wg21.link/p0323r4 #pragma once /* expected synopsis namespace std { namespace experimental { inline namespace fundamentals_v3 { // ?.?.4, Expected for object types template class expected; // ?.?.5, Expected specialization for void template class expected; // ?.?.6, unexpect tag struct unexpect_t { unexpect_t() = default; }; inline constexpr unexpect_t unexpect{}; // ?.?.7, class bad_expected_access template class bad_expected_access; // ?.?.8, Specialization for void. template <> class bad_expected_access; // ?.?.9, Expected relational operators template constexpr bool operator==(const expected&, const expected&); template constexpr bool operator!=(const expected&, const expected&); // ?.?.10, Comparison with T template constexpr bool operator==(const expected&, const T&); template constexpr bool operator==(const T&, const expected&); template constexpr bool operator!=(const expected&, const T&); template constexpr bool operator!=(const T&, const expected&); // ?.?.10, Comparison with unexpected template constexpr bool operator==(const expected&, const unexpected&); template constexpr bool operator==(const unexpected&, const expected&); template constexpr bool operator!=(const expected&, const unexpected&); template constexpr bool operator!=(const unexpected&, const expected&); // ?.?.11, Specialized algorithms void swap(expected&, expected&) noexcept(see below); template class expected { public: typedef T value_type; typedef E error_type; typedef unexpected unexpected_type; template struct rebind { using type = expected; }; // ?.?.4.1, constructors constexpr expected(); constexpr expected(const expected&); constexpr expected(expected&&) noexcept(see below); template EXPLICIT constexpr expected(const expected&); template EXPLICIT constexpr expected(expected&&); template EXPLICIT constexpr expected(U&& v); template constexpr explicit expected(in_place_t, Args&&...); template constexpr explicit expected(in_place_t, initializer_list, Args&&...); template constexpr expected(unexpected const&); template constexpr expected(unexpected &&); template constexpr explicit expected(unexpect_t, Args&&...); template constexpr explicit expected(unexpect_t, initializer_list, Args&&...); // ?.?.4.2, destructor ~expected(); // ?.?.4.3, assignment 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&&...); // ?.?.4.4, swap void swap(expected&) noexcept(see below); // ?.?.4.5, observers constexpr const T* operator ->() const; constexpr T* operator ->(); constexpr const T& operator *() const&; constexpr T& operator *() &; constexpr const T&& operator *() const &&; constexpr T&& operator *() &&; constexpr explicit operator bool() const noexcept; constexpr bool has_value() const noexcept; constexpr const T& value() const&; constexpr T& value() &; constexpr const T&& value() const &&; constexpr T&& value() &&; constexpr const E& error() const&; constexpr E& error() &; constexpr const E&& error() const &&; constexpr E&& error() &&; template constexpr T value_or(U&&) const&; template T value_or(U&&) &&; private: bool has_val; // exposition only union { value_type val; // exposition only unexpected_type unexpect; // exposition only }; }; }}} */ #include #include #include #include #include #include #include #include namespace std { namespace experimental { inline namespace fundamentals_v3 { struct unexpected_t { unexpected_t() = default; }; #if __cplusplus < 201703L #define __EXPECTED_INLINE_VARIABLE static const #else #define __EXPECTED_INLINE_VARIABLE inline #endif __EXPECTED_INLINE_VARIABLE constexpr unexpected_t unexpect { }; template class bad_expected_access; template<> class bad_expected_access : public std::exception { public: explicit bad_expected_access() { } }; template class bad_expected_access : public bad_expected_access { public: explicit bad_expected_access(E val) : val(val) { } const char* what() const noexcept override { return std::exception::what(); } E& error() & { return val; } const E& error() const& { return val; } E&& error() && { return std::move(val); } const E&& error() const&& { return std::move(val); } private: E val; }; namespace __expected_detail { #if COMPILER_SUPPORTS(EXCEPTIONS) #define __EXPECTED_THROW(__exception) (throw __exception) #else inline NO_RETURN_DUE_TO_CRASH void __expected_terminate() { RELEASE_ASSERT_NOT_REACHED(); } #define __EXPECTED_THROW(...) __expected_detail::__expected_terminate() #endif __EXPECTED_INLINE_VARIABLE constexpr enum class value_tag_t { } value_tag { }; __EXPECTED_INLINE_VARIABLE constexpr enum class error_tag_t { } error_tag { }; template::value>* = nullptr> void destroy(T&) { } template::value && (std::is_class::value || std::is_union::value)>* = nullptr> void destroy(T& t) { t.~T(); } template union constexpr_storage { typedef T value_type; typedef E error_type; typedef unexpected unexpected_type; char dummy; value_type val; error_type err; constexpr constexpr_storage() : dummy() { } constexpr constexpr_storage(value_tag_t) : val() { } constexpr constexpr_storage(error_tag_t) : err() { } template constexpr constexpr_storage(value_tag_t, U&& v) : val(std::forward(v)) { } template constexpr constexpr_storage(error_tag_t, U&& e) : err(std::forward(e)) { } ~constexpr_storage() = default; }; template union storage { typedef T value_type; typedef E error_type; typedef unexpected unexpected_type; char dummy; value_type val; error_type err; constexpr storage() : dummy() { } constexpr storage(value_tag_t) : val() { } constexpr storage(error_tag_t) : err() { } constexpr storage(value_tag_t, const value_type& val) : val(val) { } constexpr storage(value_tag_t, value_type&& val) : val(std::forward(val)) { } constexpr storage(error_tag_t, const error_type& err) : err(err) { } constexpr storage(error_tag_t, error_type&& err) : err(std::forward(err)) { } ~storage() { } }; template union constexpr_storage { typedef void value_type; typedef E error_type; typedef unexpected unexpected_type; char dummy; error_type err; constexpr constexpr_storage() : dummy() { } constexpr constexpr_storage(value_tag_t) : dummy() { } constexpr constexpr_storage(error_tag_t) : err() { } constexpr constexpr_storage(error_tag_t, const error_type& e) : err(e) { } ~constexpr_storage() = default; }; template union storage { typedef void value_type; typedef E error_type; typedef unexpected unexpected_type; char dummy; error_type err; constexpr storage() : dummy() { } constexpr storage(value_tag_t) : dummy() { } constexpr storage(error_tag_t) : err() { } constexpr storage(error_tag_t, const error_type& err) : err(err) { } constexpr storage(error_tag_t, error_type&& err) : err(std::forward(err)) { } ~storage() { } }; template struct constexpr_base { typedef T value_type; typedef E error_type; typedef unexpected unexpected_type; constexpr_storage s; bool has; constexpr constexpr_base() : s(), has(true) { } constexpr constexpr_base(value_tag_t tag) : s(tag), has(true) { } constexpr constexpr_base(error_tag_t tag) : s(tag), has(false) { } template constexpr constexpr_base(value_tag_t tag, U&& val) : s(tag, std::forward(val)), has(true) { } template constexpr constexpr_base(error_tag_t tag, U&& err) : s(tag, std::forward(err)), has(false) { } ~constexpr_base() = default; }; template struct base { typedef T value_type; typedef E error_type; typedef unexpected unexpected_type; storage s; bool has; constexpr base() : s(), has(true) { } constexpr base(value_tag_t tag) : s(tag), has(true) { } constexpr base(error_tag_t tag) : s(tag), has(false) { } constexpr base(value_tag_t tag, const value_type& val) : s(tag, val), has(true) { } constexpr base(value_tag_t tag, value_type&& val) : s(tag, std::forward(val)), has(true) { } constexpr base(error_tag_t tag, const error_type& err) : s(tag, err), has(false) { } constexpr base(error_tag_t tag, error_type&& err) : s(tag, std::forward(err)), has(false) { } base(const base& o) : has(o.has) { if (has) ::new (std::addressof(s.val)) value_type(o.s.val); else ::new (std::addressof(s.err)) error_type(o.s.err); } base(base&& o) : has(o.has) { if (has) ::new (std::addressof(s.val)) value_type(std::move(o.s.val)); else ::new (std::addressof(s.err)) error_type(std::move(o.s.err)); } ~base() { if (has) destroy(s.val); else destroy(s.err); } }; template struct constexpr_base { typedef void value_type; typedef E error_type; typedef unexpected unexpected_type; constexpr_storage s; bool has; constexpr constexpr_base() : s(), has(true) { } constexpr constexpr_base(value_tag_t tag) : s(tag), has(true) { } constexpr constexpr_base(error_tag_t tag) : s(tag), has(false) { } constexpr constexpr_base(error_tag_t tag, const error_type& err) : s(tag, err), has(false) { } constexpr constexpr_base(error_tag_t tag, error_type&& err) : s(tag, std::forward(err)), has(false) { } ~constexpr_base() = default; }; template struct base { typedef void value_type; typedef E error_type; typedef unexpected unexpected_type; storage s; bool has; constexpr base() : s(), has(true) { } constexpr base(value_tag_t tag) : s(tag), has(true) { } constexpr base(error_tag_t tag) : s(tag), has(false) { } constexpr base(error_tag_t tag, const error_type& err) : s(tag, err), has(false) { } constexpr base(error_tag_t tag, error_type&& err) : s(tag, std::forward(err)), has(false) { } base(const base& o) : has(o.has) { if (!has) ::new (std::addressof(s.err)) error_type(o.s.err); } base(base&& o) : has(o.has) { if (!has) ::new (std::addressof(s.err)) error_type(std::move(o.s.err)); } ~base() { if (!has) destroy(s.err); } }; template using base_select = typename std::conditional< ((std::is_void::value || std::is_trivially_destructible::value) && std::is_trivially_destructible::value), constexpr_base::type, typename std::remove_const::type>, base::type, typename std::remove_const::type> >::type; } // namespace __expected_detail template class expected : private __expected_detail::base_select { WTF_MAKE_FAST_ALLOCATED; typedef __expected_detail::base_select base; public: typedef typename base::value_type value_type; typedef typename base::error_type error_type; typedef typename base::unexpected_type unexpected_type; private: typedef expected type; public: template struct rebind { using type = expected; }; constexpr expected() : base(__expected_detail::value_tag) { } expected(const expected&) = default; expected(expected&&) = default; constexpr expected(const value_type& e) : base(__expected_detail::value_tag, e) { } constexpr expected(value_type&& e) : base(__expected_detail::value_tag, std::forward(e)) { } template constexpr explicit expected(std::in_place_t, Args&&... args) : base(__expected_detail::value_tag, value_type(std::forward(args)...)) { } // template constexpr explicit expected(in_place_t, std::initializer_list, Args&&...); constexpr expected(const unexpected_type& u) : base(__expected_detail::error_tag, u.value()) { } constexpr expected(unexpected_type&& u) : base(__expected_detail::error_tag, std::forward(u).value()) { } template constexpr expected(const unexpected& u) : base(__expected_detail::error_tag, u.value()) { } template constexpr expected(unexpected&& u) : base(__expected_detail::error_tag, std::forward(u.value())) { } template constexpr explicit expected(unexpected_t, Args&&... args) : base(__expected_detail::value_tag, unexpected_type(std::forward(args)...)) { } // template constexpr explicit expected(unexpected_t, std::initializer_list, Args&&...); ~expected() = default; expected& operator=(const expected& e) { type(e).swap(*this); return *this; } expected& operator=(expected&& e) { type(std::move(e)).swap(*this); return *this; } template expected& operator=(U&& u) { type(std::forward(u)).swap(*this); return *this; } expected& operator=(const unexpected_type& u) { type(u).swap(*this); return *this; } expected& operator=(unexpected_type&& u) { type(std::move(u)).swap(*this); return *this; } // template void emplace(Args&&...); // template void emplace(std::initializer_list, Args&&...); void swap(expected& o) { using std::swap; if (base::has && o.has) swap(base::s.val, o.s.val); else if (base::has && !o.has) { error_type e(std::move(o.s.err)); __expected_detail::destroy(o.s.err); ::new (std::addressof(o.s.val)) value_type(std::move(base::s.val)); __expected_detail::destroy(base::s.val); ::new (std::addressof(base::s.err)) error_type(std::move(e)); swap(base::has, o.has); } else if (!base::has && o.has) { value_type v(std::move(o.s.val)); __expected_detail::destroy(o.s.val); ::new (std::addressof(o.s.err)) error_type(std::move(base::s.err)); __expected_detail::destroy(base::s.err); ::new (std::addressof(base::s.val)) value_type(std::move(v)); swap(base::has, o.has); } else swap(base::s.err, o.s.err); } constexpr const value_type* operator->() const { return &base::s.val; } value_type* operator->() { return &base::s.val; } constexpr const value_type& operator*() const & { return base::s.val; } value_type& operator*() & { return base::s.val; } constexpr const value_type&& operator*() const && { return std::move(base::s.val); } constexpr value_type&& operator*() && { return std::move(base::s.val); } constexpr explicit operator bool() const { return base::has; } constexpr bool has_value() const { return base::has; } constexpr const value_type& value() const & { return base::has ? base::s.val : (__EXPECTED_THROW(bad_expected_access(base::s.err)), base::s.val); } constexpr value_type& value() & { return base::has ? base::s.val : (__EXPECTED_THROW(bad_expected_access(base::s.err)), base::s.val); } constexpr const value_type&& value() const && { return std::move(base::has ? base::s.val : (__EXPECTED_THROW(bad_expected_access(base::s.err)), base::s.val)); } constexpr value_type&& value() && { return std::move(base::has ? base::s.val : (__EXPECTED_THROW(bad_expected_access(base::s.err)), base::s.val)); } constexpr const error_type& error() const & { return !base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access()), base::s.err); } error_type& error() & { return !base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access()), base::s.err); } constexpr error_type&& error() && { return std::move(!base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access()), base::s.err)); } constexpr const error_type&& error() const && { return std::move(!base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access()), base::s.err)); } template constexpr value_type value_or(U&& u) const & { return base::has ? **this : static_cast(std::forward(u)); } template value_type value_or(U&& u) && { return base::has ? std::move(**this) : static_cast(std::forward(u)); } }; template class expected : private __expected_detail::base_select { typedef __expected_detail::base_select base; public: typedef typename base::value_type value_type; typedef typename base::error_type error_type; typedef typename base::unexpected_type unexpected_type; private: typedef expected type; public: template struct rebind { using type = expected; }; constexpr expected() : base(__expected_detail::value_tag) { } expected(const expected&) = default; expected(expected&&) = default; // constexpr explicit expected(in_place_t); constexpr expected(unexpected_type const& u) : base(__expected_detail::error_tag, u.value()) { } constexpr expected(unexpected_type&& u) : base(__expected_detail::error_tag, std::forward(u).value()) { } template constexpr expected(unexpected const& u) : base(__expected_detail::error_tag, u.value()) { } ~expected() = default; expected& operator=(const expected& e) { type(e).swap(*this); return *this; } expected& operator=(expected&& e) { type(std::move(e)).swap(*this); return *this; } expected& operator=(const unexpected_type& u) { type(u).swap(*this); return *this; } // Not in the current paper. expected& operator=(unexpected_type&& u) { type(std::move(u)).swap(*this); return *this; } // Not in the current paper. // void emplace(); void swap(expected& o) { using std::swap; if (base::has && o.has) { // Do nothing. } else if (base::has && !o.has) { error_type e(std::move(o.s.err)); ::new (std::addressof(base::s.err)) error_type(e); swap(base::has, o.has); } else if (!base::has && o.has) { ::new (std::addressof(o.s.err)) error_type(std::move(base::s.err)); swap(base::has, o.has); } else swap(base::s.err, o.s.err); } constexpr explicit operator bool() const { return base::has; } constexpr bool has_value() const { return base::has; } void value() const { !base::has ? __EXPECTED_THROW(bad_expected_access()) : void(); } constexpr const E& error() const & { return !base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access()), base::s.err); } E& error() & { return !base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access()), base::s.err); } constexpr E&& error() && { return std::move(!base::has ? base::s.err : (__EXPECTED_THROW(bad_expected_access()), base::s.err)); } }; template constexpr bool operator==(const expected& x, const expected& y) { return bool(x) == bool(y) && (x ? x.value() == y.value() : x.error() == y.error()); } template constexpr bool operator!=(const expected& x, const expected& y) { return !(x == y); } template constexpr bool operator==(const expected& x, const expected& y) { return bool(x) == bool(y) && (x ? true : x.error() == y.error()); } template constexpr bool operator==(const expected& x, const T& y) { return x ? *x == y : false; } template constexpr bool operator==(const T& x, const expected& y) { return y ? x == *y : false; } template constexpr bool operator!=(const expected& x, const T& y) { return x ? *x != y : true; } template constexpr bool operator!=(const T& x, const expected& y) { return y ? x != *y : true; } template constexpr bool operator==(const expected& x, const unexpected& y) { return x ? false : x.error() == y.value(); } template constexpr bool operator==(const unexpected& x, const expected& y) { return y ? false : x.value() == y.error(); } template constexpr bool operator!=(const expected& x, const unexpected& y) { return x ? true : x.error() != y.value(); } template constexpr bool operator!=(const unexpected& x, const expected& y) { return y ? true : x.value() != y.error(); } template void swap(expected& x, expected& y) { x.swap(y); } }}} // namespace std::experimental::fundamentals_v3 __EXPECTED_INLINE_VARIABLE constexpr auto& unexpect = std::experimental::unexpect; template using Expected = std::experimental::expected;