From 537aa6bc3668cc688604415de113541dec7a3de3 Mon Sep 17 00:00:00 2001 From: cryptocode Date: Mon, 2 Jul 2018 14:25:19 +0200 Subject: [PATCH] std::error_code and std::expected implementation --- CMakeLists.txt | 3 + rai/lib/errors.cpp | 53 + rai/lib/errors.hpp | 96 ++ rai/lib/expected.hpp | 2604 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2756 insertions(+) create mode 100644 rai/lib/errors.cpp create mode 100644 rai/lib/errors.hpp create mode 100644 rai/lib/expected.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8755f84a..65882020 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,6 +290,9 @@ add_library (secure SET (RAI_LIB_SOURCES ${PLATFORM_LIB_SOURCE} + rai/lib/errors.hpp + rai/lib/errors.cpp + rai/lib/expected.hpp rai/lib/blocks.cpp rai/lib/blocks.hpp rai/lib/interface.cpp diff --git a/rai/lib/errors.cpp b/rai/lib/errors.cpp new file mode 100644 index 00000000..0a14f040 --- /dev/null +++ b/rai/lib/errors.cpp @@ -0,0 +1,53 @@ +#include "rai/lib/errors.hpp" + +std::string nano::error_common_messages::message (int ev) const +{ + switch (static_cast (ev)) + { + case nano::error_common::generic: + return "Unknown error"; + case nano::error_common::account_exists: + return "Account already exists"; + case nano::error_common::account_not_found: + return "Account not found"; + case nano::error_common::bad_account_number: + return "Bad account number"; + case nano::error_common::bad_public_key: + return "Bad public key"; + case nano::error_common::bad_seed: + return "Bad seed"; + case nano::error_common::bad_wallet_number: + return "Bad wallet number"; + case nano::error_common::bad_work_format: + return "Bad work"; + case nano::error_common::invalid_work: + return "Invalid work"; + case nano::error_common::invalid_index: + return "Invalid index"; + case nano::error_common::numeric_conversion: + return "Numeric conversion error"; + case nano::error_common::wallet_locked: + return "Wallet is locked"; + case nano::error_common::wallet_not_found: + return "Wallet not found"; + } + + return "Invalid error code"; +} + +std::string nano::error_blocks_messages::message (int ev) const +{ + switch (static_cast (ev)) + { + case nano::error_blocks::generic: + return "Unknown error"; + case nano::error_blocks::bad_hash_number: + return "Bad hash number"; + case nano::error_blocks::invalid_block_hash: + return "Invalid block hash"; + case nano::error_blocks::not_found: + return "Block not found"; + } + + return "Invalid error code"; +} diff --git a/rai/lib/errors.hpp b/rai/lib/errors.hpp new file mode 100644 index 00000000..1315786c --- /dev/null +++ b/rai/lib/errors.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include + +using tl::expected; +using tl::make_unexpected; + +namespace nano +{ +/** Returns the error code if non-zero, otherwise the value */ +template +auto either (T && value, std::error_code ec) -> expected, std::error_code> +{ + if (ec) + { + return make_unexpected (ec); + } + else + { + return std::move (value); + } +} + +/** Common error codes */ +enum class error_common +{ + generic = 1, + account_not_found, + account_exists, + bad_account_number, + bad_public_key, + bad_seed, + bad_wallet_number, + bad_work_format, + invalid_work, + invalid_index, + numeric_conversion, + wallet_locked, + wallet_not_found +}; + +/** Block related errors */ +enum class error_blocks +{ + generic = 1, + bad_hash_number, + invalid_block_hash, + not_found +}; +} + +// Convenience macro to implement the standard boilerplate for using std::error_code with enums +// Use this at the end of any header defining one or more error code enums. +#define REGISTER_ERROR_CODES(namespace_name, enum_type) \ + namespace namespace_name \ + { \ + static_assert (static_cast (enum_type::generic) > 0, "The first error enum must be generic = 1"); \ + class enum_type##_messages : public std::error_category \ + { \ + public: \ + const char * name () const noexcept override \ + { \ + return #enum_type; \ + } \ + \ + std::string message (int ev) const override; \ + }; \ + \ + inline const std::error_category & enum_type##_category () \ + { \ + static enum_type##_messages instance; \ + return instance; \ + } \ + \ + inline std::error_code make_error_code (::namespace_name::enum_type err) \ + { \ + return { static_cast (err), enum_type##_category () }; \ + } \ + \ + inline auto unexpected_error (::namespace_name::enum_type err) -> decltype (make_unexpected (make_error_code (err))) \ + { \ + return make_unexpected (make_error_code (err)); \ + } \ + } \ + namespace std \ + { \ + template <> \ + struct is_error_code_enum<::namespace_name::enum_type> : std::true_type \ + { \ + }; \ + } + +REGISTER_ERROR_CODES (nano, error_common); +REGISTER_ERROR_CODES (nano, error_blocks); diff --git a/rai/lib/expected.hpp b/rai/lib/expected.hpp new file mode 100644 index 00000000..ee60ca06 --- /dev/null +++ b/rai/lib/expected.hpp @@ -0,0 +1,2604 @@ +/// +// 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 +// . +/// +// clang-format off +#ifndef TL_EXPECTED_HPP +#define TL_EXPECTED_HPP + +#define TL_EXPECTED_VERSION_MAJOR 0 +#define TL_EXPECTED_VERSION_MINOR 2 + +#include +#include +#include +#include + +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define TL_EXPECTED_EXCEPTIONS_ENABLED +#endif + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +/// \exclude +#define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__)) +/// \exclude +#define TL_EXPECTED_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && !defined(__clang__)) +/// \exclude +#define TL_EXPECTED_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && !defined(__clang__)) +/// \exclude +#define TL_EXPECTED_GCC55 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions +/// \exclude +#define TL_EXPECTED_NO_CONSTRR + +// GCC < 5 doesn't support some standard C++11 type traits +/// \exclude +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor +/// \exclude +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// This one will be different for GCC 5.7 if it's ever supported +/// \exclude +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +/// \exclude +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +/// \exclude +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +/// \exclude +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#endif + +#if __cplusplus > 201103L +/// \exclude +#define TL_EXPECTED_CXX14 +#endif + +#ifdef TL_EXPECTED_GCC49 +#define TL_EXPECTED_GCC49_CONSTEXPR +#else +#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#endif + +#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || defined(TL_EXPECTED_GCC49)) +/// \exclude +#define TL_EXPECTED_11_CONSTEXPR +#else +/// \exclude +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif + +namespace tl +{ +template +class expected; + +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +/// \brief Used to represent an expected with no data +class monostate +{ +}; + +/// \brief A tag type to tell expected to construct its value in-place +struct in_place_t +{ + explicit in_place_t () = default; +}; +/// \brief A tag to tell expected to construct its value in-place +static constexpr in_place_t in_place{}; +#endif + +/// Used as a wrapper to store the unexpected value +template +class unexpected +{ +public: + 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 && e) : + m_val (std::move (e)) + { + } + + /// \returns the contained value + /// \group unexpected_value + constexpr const E & value () const & + { + return m_val; + } + /// \group unexpected_value + TL_EXPECTED_11_CONSTEXPR E & value () & + { + return m_val; + } + /// \group unexpected_value + TL_EXPECTED_11_CONSTEXPR E && value () && + { + return std::move (m_val); + } + /// \exclude + constexpr const E && value () const && + { + return std::move (m_val); + } + +private: + E m_val; +}; + +/// \brief Compares two unexpected objects +/// \details Simply compares lhs.value() to rhs.value() +/// \group unexpected_relop +template +constexpr bool operator== (const unexpected & lhs, const unexpected & rhs) +{ + return lhs.value () == rhs.value (); +} +/// \group unexpected_relop +template +constexpr bool operator!= (const unexpected & lhs, const unexpected & rhs) +{ + return lhs.value () != rhs.value (); +} +/// \group unexpected_relop +template +constexpr bool operator< (const unexpected & lhs, const unexpected & rhs) +{ + return lhs.value () < rhs.value (); +} +/// \group unexpected_relop +template +constexpr bool operator<= (const unexpected & lhs, const unexpected & rhs) +{ + return lhs.value () <= rhs.value (); +} +/// \group unexpected_relop +template +constexpr bool operator> (const unexpected & lhs, const unexpected & rhs) +{ + return lhs.value () > rhs.value (); +} +/// \group unexpected_relop +template +constexpr bool operator>= (const unexpected & lhs, const unexpected & rhs) +{ + return lhs.value () >= rhs.value (); +} + +/// Create an `unexpected` from `e`, deducing the return type +/// +/// *Example:* +/// auto e1 = tl::make_unexpected(42); +/// unexpected e2 (42); //same semantics +template +unexpected::type> make_unexpected (E && e) +{ + return unexpected::type> (std::forward (e)); +} + +/// \brief A tag type to tell expected to construct the unexpected value +struct unexpect_t +{ + unexpect_t () = default; +}; +/// \brief A tag to tell expected to construct the unexpected value +static constexpr unexpect_t unexpect{}; + +/// \exclude +namespace detail +{ + template + [[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception (E && e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward (e); +#else +#ifdef _MSC_VER + __assume (0); +#else + __builtin_unreachable (); +#endif +#endif + } + +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX + // C++14-style aliases for brevity + template + using remove_const_t = typename std::remove_const::type; + template + using remove_reference_t = typename std::remove_reference::type; + template + using decay_t = typename std::decay::type; + template + using enable_if_t = typename std::enable_if::type; + template + using conditional_t = typename std::conditional::type; + + // std::conjunction from C++17 + template + struct conjunction : std::true_type + { + }; + template + struct conjunction : B + { + }; + template + struct conjunction + : std::conditional, B>::type + { + }; + + // std::invoke from C++17 + // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround + template >{}>, + int = 0> + constexpr auto invoke (Fn && f, Args &&... args) noexcept ( + noexcept (std::mem_fn (f) (std::forward (args)...))) + -> decltype (std::mem_fn (f) (std::forward (args)...)) + { + return std::mem_fn (f) (std::forward (args)...); + } + + template >{}>> + constexpr auto invoke (Fn && f, Args &&... args) noexcept ( + noexcept (std::forward (f) (std::forward (args)...))) + -> decltype (std::forward (f) (std::forward (args)...)) + { + return std::forward (f) (std::forward (args)...); + } + + // std::invoke_result from C++17 + template + struct invoke_result_impl; + + template + struct invoke_result_impl< + F, decltype (detail::invoke (std::declval (), std::declval ()...), void()), + Us...> + { + using type = decltype (detail::invoke (std::declval (), std::declval ()...)); + }; + + template + using invoke_result = invoke_result_impl; + + template + using invoke_result_t = typename invoke_result::type; +#endif + + // Trait for checking if a type is a tl::expected + template + struct is_expected_impl : std::false_type + { + }; + template + struct is_expected_impl> : std::true_type + { + }; + template + using is_expected = is_expected_impl>; + + template + using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && !std::is_same, in_place_t>::value && !std::is_same, detail::decay_t>::value && !std::is_same, detail::decay_t>::value>; + + template + using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && std::is_constructible::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value>; + + template + using is_void_or = conditional_t::value, std::true_type, U>; + + template + using is_copy_constructible_or_void = is_void_or>; + + template + using is_move_constructible_or_void = is_void_or>; + +} // namespace detail + +/// \exclude +namespace detail +{ + struct no_init_t + { + }; + static constexpr no_init_t no_init{}; + + // Implements the storage of the values, and ensures that the destructor is + // trivial if it can be. + // + // This specialization is for where neither `T` or `E` is trivially + // destructible, so the destructors must be called on destruction of the + // `expected` + template ::value, + bool = std::is_trivially_destructible::value> + struct expected_storage_base + { + constexpr expected_storage_base () : + m_val (T{}), m_has_val (true) + { + } + constexpr expected_storage_base (no_init_t) : + m_no_init (), m_has_val (false) + { + } + + 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 (); + } + else + { + m_unexpect.~unexpected (); + } + } + union + { + char m_no_init; + T m_val; + unexpected m_unexpect; + }; + bool m_has_val; + }; + + // This specialization is for when both `T` and `E` are trivially-destructible, + // so the destructor of the `expected` can be trivial. + template + struct expected_storage_base + { + constexpr expected_storage_base () : + m_val (T{}), m_has_val (true) + { + } + constexpr expected_storage_base (no_init_t) : + m_no_init (), m_has_val (false) + { + } + + 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; + union + { + char m_no_init; + T m_val; + unexpected m_unexpect; + }; + bool m_has_val; + }; + + // T is trivial, E is not. + template + struct expected_storage_base + { + constexpr expected_storage_base () : + m_val (T{}), m_has_val (true) + { + } + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base (no_init_t) : + m_no_init (), m_has_val (false) + { + } + + 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 (); + } + } + + union + { + char m_no_init; + T m_val; + unexpected m_unexpect; + }; + bool m_has_val; + }; + + // E is trivial, T is not. + template + struct expected_storage_base + { + constexpr expected_storage_base () : + m_val (T{}), m_has_val (true) + { + } + constexpr expected_storage_base (no_init_t) : + m_no_init (), m_has_val (false) + { + } + + 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 (); + } + } + union + { + char m_no_init; + T m_val; + unexpected m_unexpect; + }; + bool m_has_val; + }; + + // `T` is `void`, `E` is trivially-destructible + template + struct expected_storage_base + { + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base () : + m_has_val (true) + { + } + constexpr expected_storage_base (no_init_t) : + m_val (), m_has_val (false) + { + } + + constexpr expected_storage_base (in_place_t) : + 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; + struct dummy + { + }; + union + { + dummy m_val; + unexpected m_unexpect; + }; + bool m_has_val; + }; + + // `T` is `void`, `E` is not trivially-destructible + template + struct expected_storage_base + { + constexpr expected_storage_base () : + m_has_val (true) + { + } + constexpr expected_storage_base (no_init_t) : + m_has_val (false) + { + } + + constexpr expected_storage_base (in_place_t) : + 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 (); + } + } + + struct dummy + { + }; + union + { + char m_no_init; + dummy m_val; + unexpected m_unexpect; + }; + bool m_has_val; + }; + + // This base class provides some handy member functions which can be used in + // further derived classes + template + struct expected_operations_base : expected_storage_base + { + using expected_storage_base::expected_storage_base; + + template + void construct (Args &&... args) noexcept + { + new (std::addressof (this->m_val)) T (std::forward (args)...); + this->m_has_val = true; + } + + template + void construct_with (Rhs && rhs) noexcept + { + new (std::addressof (this->m_val)) T (std::forward (rhs).get ()); + this->m_has_val = true; + } + + template + void construct_error (Args &&... args) noexcept + { + new (std::addressof (this->m_unexpect)) + unexpected (std::forward (args)...); + this->m_has_val = false; + } + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template ::value> * = nullptr> + void assign (const expected_operations_base & rhs) noexcept + { + if (!this->m_has_val && rhs.m_has_val) + { + geterr ().~unexpected (); + construct (rhs.get ()); + } + else + { + assign_common (rhs); + } + } + + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template ::value && std::is_nothrow_move_constructible::value> * = nullptr> + void assign (const expected_operations_base & rhs) noexcept + { + if (!this->m_has_val && rhs.m_has_val) + { + T tmp = rhs.get (); + geterr ().~unexpected (); + construct (std::move (tmp)); + } + else + { + assign_common (rhs); + } + } + + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template ::value && !std::is_nothrow_move_constructible::value> * = nullptr> + void assign (const expected_operations_base & rhs) + { + if (!this->m_has_val && rhs.m_has_val) + { + auto tmp = std::move (geterr ()); + geterr ().~unexpected (); + + try + { + construct (rhs.get ()); + } + catch (...) + { + geterr () = std::move (tmp); + throw; + } + } + else + { + assign_common (rhs); + } + } + + // These overloads do the same as above, but for rvalues + template ::value> * = nullptr> + void assign (expected_operations_base && rhs) noexcept + { + if (!this->m_has_val && rhs.m_has_val) + { + geterr ().~unexpected (); + construct (std::move (rhs).get ()); + } + else + { + assign_common (std::move (rhs)); + } + } + + template ::value> * = nullptr> + void assign (expected_operations_base && rhs) + { + if (!this->m_has_val && rhs.m_has_val) + { + auto tmp = std::move (geterr ()); + geterr ().~unexpected (); + try + { + construct (std::move (rhs).get ()); + } + catch (...) + { + geterr () = std::move (tmp); + throw; + } + } + else + { + assign_common (std::move (rhs)); + } + } + +#else + + // If exceptions are disabled then we can just copy-construct + void assign (const expected_operations_base & rhs) noexcept + { + if (!this->m_has_val && rhs.m_has_val) + { + geterr ().~unexpected (); + construct (rhs.get ()); + } + else + { + assign_common (rhs); + } + } + + void assign (expected_operations_base && rhs) noexcept + { + if (!this->m_has_val && rhs.m_has_val) + { + geterr ().~unexpected (); + construct (std::move (rhs).get ()); + } + else + { + assign_common (rhs); + } + } + +#endif + + // The common part of move/copy assigning + template + void assign_common (Rhs && rhs) + { + if (this->m_has_val) + { + if (rhs.m_has_val) + { + get () = std::forward (rhs).get (); + } + else + { + get ().~T (); + construct_error (std::forward (rhs).geterr ()); + } + } + else + { + if (!rhs.m_has_val) + { + geterr () = std::forward (rhs).geterr (); + } + } + } + + bool has_value () const + { + return this->m_has_val; + } + + TL_EXPECTED_11_CONSTEXPR T & get () & + { + return this->m_val; + } + constexpr const T & get () const & + { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR T && get () && + { + return std::move (this->m_val); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const T && get () const && + { + return std::move (this->m_val); + } +#endif + + TL_EXPECTED_11_CONSTEXPR unexpected & geterr () & + { + return this->m_unexpect; + } + constexpr const unexpected & geterr () const & + { + return this->m_unexpect; + } + TL_EXPECTED_11_CONSTEXPR unexpected && geterr () && + { + return std::move (this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected && geterr () const && + { + return std::move (this->m_unexpect); + } +#endif + }; + + // This base class provides some handy member functions which can be used in + // further derived classes + template + struct expected_operations_base : expected_storage_base + { + using expected_storage_base::expected_storage_base; + + template + void construct () noexcept + { + this->m_has_val = true; + } + + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template + void construct_with (Rhs &&) noexcept + { + this->m_has_val = true; + } + + template + void construct_error (Args &&... args) noexcept + { + new (std::addressof (this->m_unexpect)) + unexpected (std::forward (args)...); + this->m_has_val = false; + } + + template + void assign (Rhs && rhs) noexcept + { + if (!this->m_has_val) + { + if (rhs.m_has_val) + { + geterr ().~unexpected (); + construct (); + } + else + { + geterr () = std::forward (rhs).geterr (); + } + } + else + { + if (!rhs.m_has_val) + { + construct_error (std::forward (rhs).geterr ()); + } + } + } + + bool has_value () const + { + return this->m_has_val; + } + + TL_EXPECTED_11_CONSTEXPR unexpected & geterr () & + { + return this->m_unexpect; + } + constexpr const unexpected & geterr () const & + { + return this->m_unexpect; + } + TL_EXPECTED_11_CONSTEXPR unexpected && geterr () && + { + std::move (this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected && geterr () const && + { + return std::move (this->m_unexpect); + } +#endif + }; + + // This class manages conditionally having a trivial copy constructor + // This specialization is for when T and E are trivially copy constructible + template :: + value && TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE (E)::value> + struct expected_copy_base : expected_operations_base + { + using expected_operations_base::expected_operations_base; + }; + + // This specialization is for when T or E are not trivially copy constructible + template + struct expected_copy_base : expected_operations_base + { + using expected_operations_base::expected_operations_base; + + expected_copy_base () = default; + expected_copy_base (const expected_copy_base & rhs) : + expected_operations_base (no_init) + { + if (rhs.has_value ()) + { + this->construct_with (rhs); + } + else + { + this->construct_error (rhs.geterr ()); + } + } + + expected_copy_base (expected_copy_base && rhs) = default; + expected_copy_base & operator= (const expected_copy_base & rhs) = default; + expected_copy_base & operator= (expected_copy_base && rhs) = default; + }; + + // This class manages conditionally having a trivial move constructor + // Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it + // doesn't implement an analogue to std::is_trivially_move_constructible. We + // have to make do with a non-trivial move constructor even if T is trivially + // move constructible +#ifndef TL_EXPECTED_GCC49 + template >::value && std::is_trivially_move_constructible::value> + struct expected_move_base : expected_copy_base + { + using expected_copy_base::expected_copy_base; + }; +#else + template + struct expected_move_base; +#endif + template + struct expected_move_base : expected_copy_base + { + using expected_copy_base::expected_copy_base; + + expected_move_base () = default; + expected_move_base (const expected_move_base & rhs) = default; + + expected_move_base (expected_move_base && rhs) noexcept ( + std::is_nothrow_move_constructible::value) : + expected_copy_base (no_init) + { + if (rhs.has_value ()) + { + this->construct_with (std::move (rhs)); + } + else + { + this->construct_error (std::move (rhs.geterr ())); + } + } + expected_move_base & operator= (const expected_move_base & rhs) = default; + expected_move_base & operator= (expected_move_base && rhs) = default; + }; + + // This class manages conditionally having a trivial copy assignment operator + template >::value && TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE (E)::value && TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE (E)::value && TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE (E)::value> + struct expected_copy_assign_base : expected_move_base + { + using expected_move_base::expected_move_base; + }; + + template + struct expected_copy_assign_base : expected_move_base + { + using expected_move_base::expected_move_base; + + expected_copy_assign_base () = default; + expected_copy_assign_base (const expected_copy_assign_base & rhs) = default; + + expected_copy_assign_base (expected_copy_assign_base && rhs) = default; + expected_copy_assign_base & operator= (const expected_copy_assign_base & rhs) + { + this->assign (rhs); + return *this; + } + expected_copy_assign_base & + operator= (expected_copy_assign_base && rhs) + = default; + }; + + // This class manages conditionally having a trivial move assignment operator + // Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it + // doesn't implement an analogue to std::is_trivially_move_assignable. We have + // to make do with a non-trivial move assignment operator even if T is trivially + // move assignable +#ifndef TL_EXPECTED_GCC49 + template , std::is_trivially_move_constructible, std::is_trivially_move_assignable>>:: + value && std::is_trivially_destructible::value && std::is_trivially_move_constructible::value && std::is_trivially_move_assignable::value> + struct expected_move_assign_base : expected_copy_assign_base + { + using expected_copy_assign_base::expected_copy_assign_base; + }; +#else + template + struct expected_move_assign_base; +#endif + + template + struct expected_move_assign_base + : expected_copy_assign_base + { + using expected_copy_assign_base::expected_copy_assign_base; + + expected_move_assign_base () = default; + expected_move_assign_base (const expected_move_assign_base & rhs) = default; + + expected_move_assign_base (expected_move_assign_base && rhs) = default; + + expected_move_assign_base & + operator= (const expected_move_assign_base & rhs) + = default; + + expected_move_assign_base & + operator= (expected_move_assign_base && rhs) noexcept ( + std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value) + { + this->assign (std::move (rhs)); + return *this; + } + }; + + // expected_delete_ctor_base will conditionally delete copy and move + // constructors depending on whether T is copy/move constructible + template ::value && std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && std::is_move_constructible::value)> + struct expected_delete_ctor_base + { + expected_delete_ctor_base () = default; + expected_delete_ctor_base (const expected_delete_ctor_base &) = default; + expected_delete_ctor_base (expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator= (const expected_delete_ctor_base &) + = default; + expected_delete_ctor_base & + operator= (expected_delete_ctor_base &&) noexcept + = default; + }; + + template + struct expected_delete_ctor_base + { + expected_delete_ctor_base () = default; + expected_delete_ctor_base (const expected_delete_ctor_base &) = default; + expected_delete_ctor_base (expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator= (const expected_delete_ctor_base &) + = default; + expected_delete_ctor_base & + operator= (expected_delete_ctor_base &&) noexcept + = default; + }; + + template + struct expected_delete_ctor_base + { + expected_delete_ctor_base () = default; + expected_delete_ctor_base (const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base (expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator= (const expected_delete_ctor_base &) + = default; + expected_delete_ctor_base & + operator= (expected_delete_ctor_base &&) noexcept + = default; + }; + + template + struct expected_delete_ctor_base + { + expected_delete_ctor_base () = default; + expected_delete_ctor_base (const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base (expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator= (const expected_delete_ctor_base &) + = default; + expected_delete_ctor_base & + operator= (expected_delete_ctor_base &&) noexcept + = default; + }; + + // expected_delete_assign_base will conditionally delete copy and move + // constructors depending on whether T and E are copy/move constructible + + // assignable + template ::value && std::is_copy_constructible::value && std::is_copy_assignable::value && std::is_copy_assignable::value), + bool EnableMove = (std::is_move_constructible::value && std::is_move_constructible::value && std::is_move_assignable::value && std::is_move_assignable::value)> + struct expected_delete_assign_base + { + expected_delete_assign_base () = default; + expected_delete_assign_base (const expected_delete_assign_base &) = default; + expected_delete_assign_base (expected_delete_assign_base &&) noexcept = default; + expected_delete_assign_base & + operator= (const expected_delete_assign_base &) + = default; + expected_delete_assign_base & + operator= (expected_delete_assign_base &&) noexcept + = default; + }; + + template + struct expected_delete_assign_base + { + expected_delete_assign_base () = default; + expected_delete_assign_base (const expected_delete_assign_base &) = default; + expected_delete_assign_base (expected_delete_assign_base &&) noexcept = default; + expected_delete_assign_base & + operator= (const expected_delete_assign_base &) + = default; + expected_delete_assign_base & + operator= (expected_delete_assign_base &&) noexcept + = delete; + }; + + template + struct expected_delete_assign_base + { + expected_delete_assign_base () = default; + expected_delete_assign_base (const expected_delete_assign_base &) = default; + expected_delete_assign_base (expected_delete_assign_base &&) noexcept = default; + expected_delete_assign_base & + operator= (const expected_delete_assign_base &) + = delete; + expected_delete_assign_base & + operator= (expected_delete_assign_base &&) noexcept + = default; + }; + + template + struct expected_delete_assign_base + { + expected_delete_assign_base () = default; + expected_delete_assign_base (const expected_delete_assign_base &) = default; + expected_delete_assign_base (expected_delete_assign_base &&) noexcept = default; + expected_delete_assign_base & + operator= (const expected_delete_assign_base &) + = delete; + expected_delete_assign_base & + operator= (expected_delete_assign_base &&) noexcept + = delete; + }; + + // This is needed to be able to construct the expected_default_ctor_base which + // follows, while still conditionally deleting the default constructor. + struct default_constructor_tag + { + explicit constexpr default_constructor_tag () = default; + }; + + // expected_default_ctor_base will ensure that expected has a deleted default + // consturctor if T is not default constructible. + // This specialization is for when T is default constructible + template ::value || std::is_void::value> + struct expected_default_ctor_base + { + constexpr expected_default_ctor_base () noexcept = default; + constexpr expected_default_ctor_base ( + expected_default_ctor_base const &) noexcept + = default; + constexpr expected_default_ctor_base (expected_default_ctor_base &&) noexcept = default; + expected_default_ctor_base & + operator= (expected_default_ctor_base const &) noexcept + = default; + expected_default_ctor_base & + operator= (expected_default_ctor_base &&) noexcept + = default; + + constexpr explicit expected_default_ctor_base (default_constructor_tag) + { + } + }; + + // This specialization is for when T is not default constructible + template + struct expected_default_ctor_base + { + constexpr expected_default_ctor_base () noexcept = delete; + constexpr expected_default_ctor_base ( + expected_default_ctor_base const &) noexcept + = default; + constexpr expected_default_ctor_base (expected_default_ctor_base &&) noexcept = default; + expected_default_ctor_base & + operator= (expected_default_ctor_base const &) noexcept + = default; + expected_default_ctor_base & + operator= (expected_default_ctor_base &&) noexcept + = default; + + constexpr explicit expected_default_ctor_base (default_constructor_tag) + { + } + }; +} // namespace detail + +template +class bad_expected_access : public std::exception +{ +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; +}; + +/// An `expected` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. +template +class expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_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"); + + T * valptr () + { + return std::addressof (this->m_val); + } + unexpected * errptr () + { + return std::addressof (this->m_unexpect); + } + + template ::value> * = nullptr> + U & val () + { + return this->m_val; + } + unexpected & err () + { + return this->m_unexpect; + } + + template ::value> * = nullptr> + const U & val () const + { + return this->m_val; + } + const unexpected & err () const + { + return this->m_unexpect; + } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + /// \group and_then + /// Carries out some operation which returns an expected on the stored object + /// if there is one. \requires `std::invoke(std::forward(f), value())` + /// returns an `expected` for some `U`. \returns Let `U` be the result + /// of `std::invoke(std::forward(f), value())`. Returns an + /// `expected`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template + TL_EXPECTED_11_CONSTEXPR auto and_then (F && f) & + { + return and_then_impl (*this, std::forward (f)); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template + TL_EXPECTED_11_CONSTEXPR auto and_then (F && f) && + { + return and_then_impl (std::move (*this), std::forward (f)); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template + constexpr auto and_then (F && f) const & + { + return and_then_impl (*this, std::forward (f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template + constexpr auto and_then (F && f) const && + { + return and_then_impl (std::move (*this), std::forward (f)); + } +#endif + +#else + /// \group and_then + /// Carries out some operation which returns an expected on the stored object + /// if there is one. \requires `std::invoke(std::forward(f), value())` + /// returns an `expected` for some `U`. \returns Let `U` be the result + /// of `std::invoke(std::forward(f), value())`. Returns an + /// `expected`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template + TL_EXPECTED_11_CONSTEXPR auto + and_then (F && f) & -> decltype (and_then_impl (*this, std::forward (f))) + { + return and_then_impl (*this, std::forward (f)); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template + TL_EXPECTED_11_CONSTEXPR auto and_then (F && f) && -> decltype ( + and_then_impl (std::move (*this), std::forward (f))) + { + return and_then_impl (std::move (*this), std::forward (f)); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template + constexpr auto and_then (F && f) const & -> decltype ( + and_then_impl (*this, std::forward (f))) + { + return and_then_impl (*this, std::forward (f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template + constexpr auto and_then (F && f) const && -> decltype ( + and_then_impl (std::move (*this), std::forward (f))) + { + return and_then_impl (std::move (*this), std::forward (f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. If `U` is `void`, returns an `expected, otherwise + // returns an `expected`. If `*this` is unexpected, the + /// result is `*this`, otherwise an `expected` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template constexpr auto map(F &&f) &; + template + TL_EXPECTED_11_CONSTEXPR auto map (F && f) & + { + return expected_map_impl (*this, std::forward (f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) &&; + template + TL_EXPECTED_11_CONSTEXPR auto map (F && f) && + { + return expected_map_impl (std::move (*this), std::forward (f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const &; + template + constexpr auto map (F && f) const & + { + return expected_map_impl (*this, std::forward (f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const &&; + template + constexpr auto map (F && f) const && + { + return expected_map_impl (std::move (*this), std::forward (f)); + } +#else + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. If `U` is `void`, returns an `expected, otherwise + // returns an `expected`. If `*this` is unexpected, the + /// result is `*this`, otherwise an `expected` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template constexpr auto map(F &&f) &; + template + TL_EXPECTED_11_CONSTEXPR decltype ( + expected_map_impl (std::declval (), std::declval ())) + map (F && f) & + { + return expected_map_impl (*this, std::forward (f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) &&; + template + TL_EXPECTED_11_CONSTEXPR decltype ( + expected_map_impl (std::declval (), std::declval ())) + map (F && f) && + { + return expected_map_impl (std::move (*this), std::forward (f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const &; + template + constexpr decltype (expected_map_impl (std::declval (), + std::declval ())) + map (F && f) const & + { + return expected_map_impl (*this, std::forward (f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + /// \group map + /// \synopsis template constexpr auto map(F &&f) const &&; + template + constexpr decltype (expected_map_impl (std::declval (), + std::declval ())) + map (F && f) const && + { + return expected_map_impl (std::move (*this), std::forward (f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + /// \brief Carries out some operation on the stored unexpected object if there + /// is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. If `U` is `void`, returns an `expected`, otherwise + /// returns an `expected`. If `*this` has an expected + /// value, the result is `*this`, otherwise an `expected` is constructed + /// from `make_unexpected(std::invoke(std::forward(f), value()))` and is + /// returned. + /// + /// \group map_error + /// \synopsis template constexpr auto map_error(F &&f) &; + template + TL_EXPECTED_11_CONSTEXPR auto map_error (F && f) & + { + return map_error_impl (*this, std::forward (f)); + } + + /// \group map_error + /// \synopsis template constexpr auto map_error(F &&f) &&; + template + TL_EXPECTED_11_CONSTEXPR auto map_error (F && f) && + { + return map_error_impl (std::move (*this), std::forward (f)); + } + + /// \group map_error + /// \synopsis template constexpr auto map_error(F &&f) const &; + template + constexpr auto map_error (F && f) const & + { + return map_error_impl (*this, std::forward (f)); + } + + /// \group map_error + /// \synopsis template constexpr auto map_error(F &&f) const &&; + template + constexpr auto map_error (F && f) const && + { + return map_error_impl (std::move (*this), std::forward (f)); + } +#else + /// \brief Carries out some operation on the stored unexpected object if there + /// is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns an `expected`. If `*this` has an expected + /// value, the result is `*this`, otherwise an `expected` is constructed + /// from `make_unexpected(std::invoke(std::forward(f), value()))` and is + /// returned. + /// + /// \group map_error + /// \synopsis template constexpr auto map_error(F &&f) &; + template + TL_EXPECTED_11_CONSTEXPR decltype (map_error_impl (std::declval (), + std::declval ())) + map_error (F && f) & + { + return map_error_impl (*this, std::forward (f)); + } + + /// \group map_error + /// \synopsis template constexpr auto map_error(F &&f) &&; + template + TL_EXPECTED_11_CONSTEXPR decltype (map_error_impl (std::declval (), + std::declval ())) + map_error (F && f) && + { + return map_error_impl (std::move (*this), std::forward (f)); + } + + /// \group map_error + /// \synopsis template constexpr auto map_error(F &&f) const &; + template + constexpr decltype (map_error_impl (std::declval (), + std::declval ())) + map_error (F && f) const & + { + return map_error_impl (*this, std::forward (f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + /// \group map_error + /// \synopsis template constexpr auto map_error(F &&f) const &&; + template + constexpr decltype (map_error_impl (std::declval (), + std::declval ())) + map_error (F && f) const && + { + return map_error_impl (std::move (*this), std::forward (f)); + } +#endif +#endif + + /// \brief Calls `f` if the expectd is in the unexpected state + /// \requires `F` is invokable with `E`, and `std::invoke_result_t` + /// must be void or convertible to `expcted`. + /// \effects If `*this` has a value, returns `*this`. + /// Otherwise, if `f` returns `void`, calls `std::forward(f)(E)` and returns + /// `std::nullopt`. Otherwise, returns `std::forward(f)(E)`. + /// + /// \group or_else + template + expected TL_EXPECTED_11_CONSTEXPR or_else (F && f) & + { + return or_else_impl (*this, std::forward (f)); + } + + template + expected TL_EXPECTED_11_CONSTEXPR or_else (F && f) && + { + return or_else_impl (std::move (*this), std::forward (f)); + } + + template + expected constexpr or_else (F && f) const & + { + return or_else_impl (*this, std::forward (f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + expected constexpr or_else (F && f) const && + { + return or_else_impl (std::move (*this), std::forward (f)); + } +#endif + constexpr expected () = default; + constexpr expected (const expected & rhs) = default; + constexpr expected (expected && rhs) = default; + expected & operator= (const expected & rhs) = default; + expected & operator= (expected && rhs) = default; + + template ::value> * = nullptr> + constexpr expected (in_place_t, Args &&... args) : + impl_base (in_place, std::forward (args)...), + ctor_base (detail::default_constructor_tag{}) + { + } + + template &, Args &&...>::value> * = nullptr> + constexpr expected (in_place_t, std::initializer_list il, Args &&... args) : + impl_base (in_place, il, std::forward (args)...), + ctor_base (detail::default_constructor_tag{}) + { + } + + /// \group unexpected_ctor + /// \synopsis EXPLICIT constexpr expected(const unexpected &e); + template ::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected (const unexpected & e) : + impl_base (unexpect, e.value ()), + ctor_base (detail::default_constructor_tag{}) + { + } + + /// \exclude + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected (unexpected const & e) : + impl_base (unexpect, e.value ()), + ctor_base (detail::default_constructor_tag{}) + { + } + + /// \group unexpected_ctor + /// \synopsis EXPLICIT constexpr expected(unexpected &&e); + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected (unexpected && e) noexcept ( + std::is_nothrow_constructible::value) : + impl_base (unexpect, std::move (e.value ())), + ctor_base (detail::default_constructor_tag{}) + { + } + + /// \exclude + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected (unexpected && e) noexcept ( + std::is_nothrow_constructible::value) : + impl_base (unexpect, std::move (e.value ())), + ctor_base (detail::default_constructor_tag{}) + { + } + + template ::value> * = nullptr> + constexpr explicit expected (unexpect_t, Args &&... args) : + impl_base (unexpect, std::forward (args)...), + ctor_base (detail::default_constructor_tag{}) + { + } + + /// \exclude + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected (unexpect_t, std::initializer_list il, + Args &&... args) : + impl_base (unexpect, il, std::forward (args)...), + ctor_base (detail::default_constructor_tag{}) + { + } + + template ::value && std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected (const expected & rhs) : + ctor_base (detail::default_constructor_tag{}) + { + if (rhs.has_value ()) + { + ::new (valptr ()) T (*rhs); + this->m_has_val = true; + } + else + { + ::new (errptr ()) unexpected_type (unexpected (rhs.error ())); + this->m_has_val = false; + } + } + + /// \exclude + template ::value && std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected (const expected & rhs) : + ctor_base (detail::default_constructor_tag{}) + { + if (rhs.has_value ()) + { + ::new (valptr ()) T (*rhs); + this->m_has_val = true; + } + else + { + ::new (errptr ()) unexpected_type (unexpected (rhs.error ())); + this->m_has_val = false; + } + } + + template < + class U, class G, + detail::enable_if_t::value && std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected (expected && rhs) : + ctor_base (detail::default_constructor_tag{}) + { + if (rhs.has_value ()) + { + ::new (valptr ()) T (std::move (*rhs)); + this->m_has_val = true; + } + else + { + ::new (errptr ()) unexpected_type (unexpected (std::move (rhs.error ()))); + this->m_has_val = false; + } + } + + /// \exclude + template < + class U, class G, + detail::enable_if_t<(std::is_convertible::value && std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected (expected && rhs) : + ctor_base (detail::default_constructor_tag{}) + { + if (rhs.has_value ()) + { + ::new (valptr ()) T (std::move (*rhs)); + this->m_has_val = true; + } + else + { + ::new (errptr ()) unexpected_type (unexpected (std::move (rhs.error ()))); + this->m_has_val = false; + } + } + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected (U && v) : + expected (in_place, std::forward (v)) + { + } + + /// \exclude + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected (U && v) : + expected (in_place, std::forward (v)) + { + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && !detail::conjunction, std::is_same>>::value && std::is_constructible::value && std::is_assignable::value && std::is_nothrow_move_constructible::value)> * = nullptr> + expected & operator= (U && v) + { + if (has_value ()) + { + val () = std::forward (v); + } + else + { + err ().~unexpected (); + ::new (valptr ()) T (std::forward (v)); + this->m_has_val = true; + } + + return *this; + } + + /// \exclude + template < + class U = T, class G = T, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && !detail::conjunction, std::is_same>>::value && std::is_constructible::value && std::is_assignable::value && std::is_nothrow_move_constructible::value)> * = nullptr> + expected & operator= (U && v) + { + if (has_value ()) + { + val () = std::forward (v); + } + else + { + auto tmp = std::move (err ()); + err ().~unexpected (); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try + { + ::new (valptr ()) T (std::move (v)); + this->m_has_val = true; + } + catch (...) + { + err () = std::move (tmp); + throw; + } +#else + ::new (valptr ()) T (std::move (v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && std::is_assignable::value> * = nullptr> + expected & operator= (const unexpected & rhs) + { + if (!has_value ()) + { + err () = rhs; + } + else + { + val ().~T (); + ::new (errptr ()) unexpected (rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && std::is_move_assignable::value> * = nullptr> + expected & operator= (unexpected && rhs) noexcept + { + if (!has_value ()) + { + err () = std::move (rhs); + } + else + { + val ().~T (); + ::new (errptr ()) unexpected (std::move (rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace (Args &&... args) + { + if (has_value ()) + { + val () = T (std::forward (args)...); + } + else + { + err ().~unexpected (); + ::new (valptr ()) T (std::forward (args)...); + this->m_has_val = true; + } + } + + /// \exclude + template ::value> * = nullptr> + void emplace (Args &&... args) + { + if (has_value ()) + { + val () = T (std::forward (args)...); + } + else + { + auto tmp = std::move (err ()); + err ().~unexpected (); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try + { + ::new (valptr ()) T (std::forward (args)...); + this->m_has_val = true; + } + catch (...) + { + err () = std::move (tmp); + throw; + } +#else + ::new (valptr ()) T (std::forward (args)...); + this->m_has_val = true; +#endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace (std::initializer_list il, Args &&... args) + { + if (has_value ()) + { + T t (il, std::forward (args)...); + val () = std::move (t); + } + else + { + err ().~unexpected (); + ::new (valptr ()) T (il, std::forward (args)...); + this->m_has_val = true; + } + } + + /// \exclude + template &, Args &&...>::value> * = nullptr> + void emplace (std::initializer_list il, Args &&... args) + { + if (has_value ()) + { + T t (il, std::forward (args)...); + val () = std::move (t); + } + else + { + auto tmp = std::move (err ()); + err ().~unexpected (); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try + { + ::new (valptr ()) T (il, std::forward (args)...); + this->m_has_val = true; + } + catch (...) + { + err () = std::move (tmp); + throw; + } +#else + ::new (valptr ()) T (il, std::forward (args)...); + this->m_has_val = true; +#endif + } + } + + // 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); + } + } + + /// \returns a pointer to the stored value + /// \requires a value is stored + /// \group pointer + constexpr const T * operator-> () const + { + return valptr (); + } + /// \group pointer + TL_EXPECTED_11_CONSTEXPR T * operator-> () + { + return valptr (); + } + + /// \returns the stored value + /// \requires a value is stored + /// \group deref + template ::value> * = nullptr> + constexpr const U & operator* () const & + { + return val (); + } + /// \group deref + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U & operator* () & + { + return val (); + } + /// \group deref + template ::value> * = nullptr> + constexpr const U && operator* () const && + { + return std::move (val ()); + } + /// \group deref + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U && operator* () && + { + return std::move (val ()); + } + + /// \returns whether or not the optional has a value + /// \group has_value + constexpr bool has_value () const noexcept + { + return this->m_has_val; + } + /// \group has_value + constexpr explicit operator bool () const noexcept + { + return this->m_has_val; + } + + /// \returns the contained value if there is one, otherwise throws + /// [bad_expected_access] + /// + /// \group value + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U & value () const & + { + if (!has_value ()) + detail::throw_exception (bad_expected_access (err ().value ())); + return val (); + } + /// \group value + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U & value () & + { + if (!has_value ()) + detail::throw_exception (bad_expected_access (err ().value ())); + return val (); + } + /// \group value + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U && value () const && + { + if (!has_value ()) + detail::throw_exception (bad_expected_access (err ().value ())); + return std::move (val ()); + } + /// \group value + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U && value () && + { + if (!has_value ()) + detail::throw_exception (bad_expected_access (err ().value ())); + return std::move (val ()); + } + + /// \returns the unexpected value + /// \requires there is an unexpected value + /// \group error + constexpr const E & error () const & + { + return err ().value (); + } + /// \group error + TL_EXPECTED_11_CONSTEXPR E & error () & + { + return err ().value (); + } + /// \group error + constexpr const E && error () const && + { + return std::move (err ().value ()); + } + /// \group error + TL_EXPECTED_11_CONSTEXPR E && error () && + { + return std::move (err ().value ()); + } + + /// \returns the stored value if there is one, otherwise returns `u` + /// \group value_or + 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)); + } + /// \group value_or + template + TL_EXPECTED_11_CONSTEXPR 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)); + } +}; + +/// \exclude +namespace detail +{ + template + using exp_t = typename detail::decay_t::value_type; + template + using err_t = typename detail::decay_t::error_type; + template + using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 + template (), + *std::declval ())), + detail::enable_if_t>::value> * = nullptr> + constexpr auto and_then_impl (Exp && exp, F && f) + { + static_assert (detail::is_expected::value, "F must return an expected"); + + return exp.has_value () + ? detail::invoke (std::forward (f), *std::forward (exp)) + : Ret (unexpect, exp.error ()); + } + + template (), + *std::declval ())), + detail::enable_if_t>::value> * = nullptr> + constexpr auto and_then_impl (Exp && exp, F && f) + { + static_assert (detail::is_expected::value, "F must return an expected"); + + return exp.has_value () ? detail::invoke (std::forward (f)) + : Ret (unexpect, exp.error ()); + } +#else + template + struct TC; + template (), + *std::declval ())), + detail::enable_if_t>::value> * = nullptr> + auto and_then_impl (Exp && exp, F && f) -> Ret + { + static_assert (detail::is_expected::value, "F must return an expected"); + + return exp.has_value () + ? detail::invoke (std::forward (f), *std::forward (exp)) + : Ret (unexpect, exp.error ()); + } + + template (), + *std::declval ())), + detail::enable_if_t>::value> * = nullptr> + constexpr auto and_then_impl (Exp && exp, F && f) -> Ret + { + static_assert (detail::is_expected::value, "F must return an expected"); + + return exp.has_value () ? detail::invoke (std::forward (f)) + : Ret (unexpect, exp.error ()); + } +#endif + +#ifdef TL_EXPECTED_CXX14 + template (), + *std::declval ())), + detail::enable_if_t::value> * = nullptr> + constexpr auto expected_map_impl (Exp && exp, F && f) + { + using result = ret_t>; + return exp.has_value () ? result (detail::invoke (std::forward (f), + *std::forward (exp))) + : result (unexpect, std::forward (exp).error ()); + } + + template (), + *std::declval ())), + detail::enable_if_t::value> * = nullptr> + auto expected_map_impl (Exp && exp, F && f) + { + using result = expected>; + if (exp.has_value ()) + { + detail::invoke (std::forward (f), *std::forward (exp)); + return result (); + } + + return result (unexpect, std::forward (exp).error ()); + } +#else + template (), + *std::declval ())), + detail::enable_if_t::value> * = nullptr> + + constexpr auto expected_map_impl (Exp && exp, F && f) + -> ret_t> + { + using result = ret_t>; + + return exp.has_value () ? result (detail::invoke (std::forward (f), + *std::forward (exp))) + : result (unexpect, std::forward (exp).error ()); + } + + template (), + *std::declval ())), + detail::enable_if_t::value> * = nullptr> + + auto expected_map_impl (Exp && exp, F && f) -> expected> + { + if (exp.has_value ()) + { + detail::invoke (std::forward (f), *std::forward (exp)); + return {}; + } + + return unexpected> (std::forward (exp).error ()); + } +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template (), + std::declval ().error ())), + detail::enable_if_t::value> * = nullptr> + constexpr auto map_error_impl (Exp && exp, F && f) + { + using result = expected, detail::decay_t>; + return exp.has_value () + ? result (*std::forward (exp)) + : result (unexpect, detail::invoke (std::forward (f), std::forward (exp).error ())); + } + template (), + std::declval ().error ())), + detail::enable_if_t::value> * = nullptr> + auto map_error_impl (Exp && exp, F && f) + { + using result = expected, monostate>; + if (exp.has_value ()) + { + return result (*std::forward (exp)); + } + + detail::invoke (std::forward (f), std::forward (exp).error ()); + return result (unexpect, monostate{}); + } +#else + template (), + std::declval ().error ())), + detail::enable_if_t::value> * = nullptr> + constexpr auto map_error_impl (Exp && exp, F && f) + -> expected, detail::decay_t> + { + using result = ret_t>; + + return exp.has_value () + ? result (*std::forward (exp)) + : result (unexpect, detail::invoke (std::forward (f), std::forward (exp).error ())); + } + + template (), + std::declval ().error ())), + detail::enable_if_t::value> * = nullptr> + auto map_error_impl (Exp && exp, F && f) -> expected, monostate> + { + using result = expected, monostate>; + if (exp.has_value ()) + { + return result (*std::forward (exp)); + } + + detail::invoke (std::forward (f), std::forward (exp).error ()); + return result (unexpect, monostate{}); + } +#endif + +#ifdef TL_EXPECTED_CXX14 + template (), + std::declval ().error ())), + detail::enable_if_t::value> * = nullptr> + constexpr auto or_else_impl (Exp && exp, F && f) + { + static_assert (detail::is_expected::value, "F must return an expected"); + return exp.has_value () + ? std::forward (exp) + : detail::invoke (std::forward (f), std::forward (exp).error ()); + } + + template (), + std::declval ().error ())), + detail::enable_if_t::value> * = nullptr> + detail::decay_t or_else_impl (Exp && exp, F && f) + { + return exp.has_value () + ? std::forward (exp) + : (detail::invoke (std::forward (f), std::forward (exp).error ()), + std::forward (exp)); + } +#else + template (), + std::declval ().error ())), + detail::enable_if_t::value> * = nullptr> + auto or_else_impl (Exp && exp, F && f) -> Ret + { + static_assert (detail::is_expected::value, "F must return an expected"); + return exp.has_value () + ? std::forward (exp) + : detail::invoke (std::forward (f), std::forward (exp).error ()); + } + + template (), + std::declval ().error ())), + detail::enable_if_t::value> * = nullptr> + detail::decay_t or_else_impl (Exp && exp, F && f) + { + return exp.has_value () + ? std::forward (exp) + : (detail::invoke (std::forward (f), std::forward (exp).error ()), + std::forward (exp)); + } +#endif +} // namespace detail + +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 U & v) +{ + return x.has_value () ? *x == v : false; +} +template +constexpr bool operator== (const U & v, const expected & x) +{ + return x.has_value () ? *x == v : false; +} +template +constexpr bool operator!= (const expected & x, const U & v) +{ + return x.has_value () ? *x != v : true; +} +template +constexpr bool operator!= (const U & 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 () ? false : x.error () == e.value (); +} +template +constexpr bool operator== (const unexpected & e, const expected & x) +{ + return x.has_value () ? false : x.error () == e.value (); +} +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 (); +} + +// 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); +} +} // namespace tl + +#define TL_OPTIONAL_EXPECTED_MUTEX +#endif +// clang-format on